Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
731 views

Step by Step Functional Verification With Systemverilog and Ovm PDF

Uploaded by

Monideepika S
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
731 views

Step by Step Functional Verification With Systemverilog and Ovm PDF

Uploaded by

Monideepika S
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 520

Step-by-Step

Functional Verification
with SystemVeriiog
and OVM
Step-by-Step
Functional Verification
with SystemVerilog
and OVM

by

Sasan Iman
SiMantis Inc.
Santa Clara, CA
Spring 2008
Sa san Iman
SiMantis, Inc.
900 Lafayette St. Suite 707
Santa Clara, CA 95050
iman@simantls.com

Step-by-Step Functional Verification with SystemVeriiog and DVM

ISBN-10: 0-9816562-1-8
ISBN-13: 978-0-9816562-1-2

Printed on acid-free paper.

© 2008 Hansen Brown Publishing Company


All rights reserved. This work may not be translated or copied in whole or in part without the written permis-
sion of the publisher (Hansen Brown Publishing Company, info@hansenbrown.com), except for brief
excerpts in connection with reviews or scholarly analysis. Use In connection with any form of information
storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now
known or hereafter developed is forbidden.
The use in this publication of trade names, trademarks, service marks and similar terms, even if they are not
identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to pro-
prietary rights.

Printed in the United States of America

987654321

Hansen Brown Publishing Company


San Francisco, CA
info@hansenbrown.com
Foreword

By now, the metaphor of "the perfect storm" is in danger of becoming a cliche to describe the
forces causing rapid evolution in some aspect of the electronics industry. Nevertheless, the
term is entirely applicable to the current evolution-arguably even a revolution-in func-
tional verification for chip designs. Three converging forces are at work today: complexity,
language, and methodology.
The challenges posed in the verification oftoday's large, complex chips is well known.
Far too many chips do not ship on first silicon due to functional bugs that should have been
caught before tapeout. Hand-written simulation tests are being almost entirely replaced by
constrained-random verification environments using functional coverage metrics to deter-
mine when to tape out. Specification of assertions, constraints, and coverage points has
become an essential part of the development process.
The SystemVerilog language has been a major driver in the adoption of these advanced
verification techniques. SystemVerilog provides constructs for assertions, constraints, and
coverage along with powerful object-oriented capabilities that foster reusable testbenches
and verification components. The broad vendor support and wide industry adoption of Sys-
tem Veri log have directly led to mainstream use of constrained-random, coverage-driven ver-
ification environments.
However, a language alone cannot guarantee successful verification. SystemVerilog is a
huge language with many ways to accomplish similar tasks, and it doesn't directly address
such essential areas as verification planning, common testbench building blocks, and com-
munication between verification components. Such topics require a comprehensive verifica-
tion methodology to tie together the advanced techniques and the features of the language in
a systematic approach.
Fortunately, the Open Verification Methodology (OVM) recently arrived to address this
critical need. Developed by Cadence Design Systems and Mentor Graphics, the OVM is
completely open (freely downloadable from ovmworld.org) and guaranteed to run on the
simulation products from both companies. The OVM leverages many years of verification
experience from many of the world's experts. It was greeted with enormous enthusiasm by
the industry and is used today on countless chip projects.
vi

Thus, the timing of this book could not be better. It provides thorough coverage of all
three forces at work. The complexity challenge is addressed by timely advice on verification
planning and coherent descriptions of advanced verification techniques. Many aspects of the
SystemVerilog language, including its assertion and testbench constructs, are covered in
detail. Finally, this book embraces the OVM as the guide for verification success, providing
a real-world example deploying this methodology.
Functional verification has never been easy, but it has become an overwhelming prob-
lem for many chip development teams. This book should be a great comfort for both design
and verification engineers. Perhaps, like The Hitchhiker s Guide to the Galaxy, it should
have "DON'T PANIC!" on its cover. So grab a beverage of your choice and curl up in a com-
fortable chair to learn how to get started on your toughest verification problems.

Michael McNamara
Past Chairman of the Verilog Standards Committee.
Former VP of Engineering of Chronologic Simulation (creator of VCS).
Currently Vice President and General Manager, Cadence Design Systems.
Spring 2008
Table ofContents

Foreword ..................................... " .............. v


Table of Contents .............................................. vii
Preface ..................................................... xvii
Online Resources ............................................. xix
Book Structure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi
- ----_._-_._--_._ __._-----_ ...._ . _ . _ - - - - - - - - - -
..

Part 1: Verification Methodologies, Planning, and Architecture. . . .. 1


Chapter 1: Verification Tools and Methodologies ...................... 3
1.1 Setting the Stage ............................................... 3
1.1.1 Design Flow: From Intent to Product .......................... 4
1.1.1.1 Transaction Level Modeling and Design Abstraction Levels ... 5
1.1.1.2 Design Implementation Flow ........................... 6
1.1.2 Functional Verification: Gaining Closure ........................ 7
1.1.2.1 Black-Box, White-Box, and Gray-Box Verification ........... 8
1.1.3 The Verification Challenge ................................. 10
1.2 Verification Metrics ............................................. 12
1.2.1 Granularity ............................................. 12
1.2.2 Manual Effort ........................................... 13
1.2.3 Effectiveness ........................................... 14
1.2.4 Completeness ........................................... 14
1.2.5 Verification Environment Reusability ......................... 14
1.2.6 Simulation Result Reusability ............................... 15
1.3 Interaction between Design and Verification ......................... 15
1.4 Verification Technologies ........................................ 16
1.4.1 Formal Verification ....................................... 17
1.4.1.1 Equivalence Checking ............................... 17
1.4.1.2 Formal Property (Assertion) Checking ................... 18
1.4.2 Simulation-Based Verification ............................... 19
1.4.3 Acceleration and Emulation ................................ 20
1.5 Verification Methodologies ....................................... 21
1.5.1 Assertion-Based Verification ................................ 21
1.5.1.1 Assertion Evaluation and Tool Flow ..................... 22
viii

1.5.1.2 White-Box vs. Black-Box Assertions .................... 24


1.5.2 Coverage-Driven Verification ............................... 24
1.5.2.1 Transaction-Driven Verification ........................ 25
1.5.2.2 Constrained Random Generation ...................... 26
1.5.2.3 Automatic Result Checking ........................... 28
1.5.2.4 Coverage Collection ................................ 28
1.5.2.5 Directed Tests ..................................... 29
1.5.2.6 Coverage-Driven Verification Project Life Cycle ........... 29
1.5.3 Metric-Driven Verification .................................. 30
Chapter 2: Verification Planning .................................. 31
2.1 The Modern Verification Plan ..................................... 31
2.1.1 Verification Planning Challenges ............................ 32
2.1.2 Verification Planning Goals ................................. 34
2.1.3 Verification Plan Contents .................................. 34
2.1.4 Verification Project Cycle: Gaining Closure .................... 35
2.2 Building the Verification Plan ..................................... 35
2.2.1 Identify Actors ........................................... 36
2.2.2 Preparing to Start the Brainstorming Session ................... 37
2.2.3 Brainstorm about the Content ............................... 38
2.2.4 Verification Plan Structure .................................. 38
2.2.4.1 Verification Plan Outline ............................. 38
2.2.4.2 Verification Views ................................... 39
2.2.4.3 Verification Scenario Details ......................... .40
2.2.4.4 Verification Build and Run Infrastructure ................. 40
2.2.5 Capturing Features ....................................... 41
2.2.6 Capturing Attributes ...................................... 41
2.3 From Plan to Environment ....................................... 42
2.3.1 Identifying Required Stimulus ............................... 42
2.3.2 Identifying Required Checkers and Collectors .................. 43
2.4 Measuring Progress ............................................ 43
2.5 Reacting to Results ............................................ 43
Chapter 3: Verification Environment Architecture .................... .45
3.1 Abstract View of a Verification Environment ......................... 45
3.2 Interface Verification Components ................................. 47
3.2.1 Logical View ............................................ 48
3.2.2 Driver ................................................. 49
3.2.2.1 Feature Set ....................................... 50
3.2.2.2 User Interface ..................................... 51
3.2.3 Sequencer ............................................. 52
3.2.4 Agent and Bus Monitors ................................... 53
3.3 Software Verification Components ................................. 54
3.4 Module/System Verification Components ........................... 55
3.4.1 Sequencer ............................................. 57
3.4.1.1 Initialization ....................................... 57
3.4.1.2 Configuration ...................................... 58
3.4.1.3 Scenario Generation and Driving ...................... 58
3.4.2 VE Monitor ............................................. 58
3.4.3 DUV Monitor and Assertion Checker ......................... 59
3.4.4 Scoreboarding .......................................... 60
3.4.5 Coverage Collector ....................................... 61
3.5 Verification Component Reuse ................................... 61
ix
-_._--_._--_.__ .__ . ---::-----:-
Part 2: All about SystemVerilog ............................ 63
Chapter 4: SystemVeriiog as a Programming Language ................65
4.1 The Generalized Model of a Digital System .......................... 65
4.2 Structure of a SystemVerilog Program .............................. 67
4.2.1 Hierarchies of Different Types .............................. 69
4.3 Data Types and Objects in SystemVeriiog ........................... 69
4.3.1 Data Lifetime ............................................ 70
4.3.2 Update Mechanisms ...................................... 71
4.3.3 Data Types ............................................. 71
4.3.3.1 Built-In Data Types ................................. 72
4.3.3.2 Enumerated Data Types ............................. 74
4.3.3.3 Arrays ........................................... 75
4.3.3.3.1 Static Arrays ................................ 76
4.3.3.3.2 Dynamic Arrays ............................. 77
4.3.3.3.3 Associative Arrays ........................... 78
4.3.3.3.4 Queues .................................... 80
4.3.3.4 Composite Data Types .............................. 81
4.3.3.5 User Defined Data Types ............................. 82
4.3.4 Operators .............................................. 83
4.4 Procedural Statements and Blocks ................................ 84
4.4.1 Assignment Statements ................................... 84
4.4.2 Subroutine Calls ......................................... 85
4.4.2.1 Functions ......................................... 85
4.4.2.2 Tasks ............................................ 87
4.4.3 Selection Statements ..................................... 88
4.4.3.1 If-Else Statements .................................. 88
4.4.3.2 Case Statements ................................... 89
4.4.3.3 Random Case Statements ............................ 90
4.4.4 Loop and Jump Statements ................................ 90
4.4.5 Event Control Statements .................................. 91
4.5 Module Hierarchy .............................................. 91
4.5.1 Modules and Module Ports ................................. 93
4.5.1.1 Port Connection Syntax .............................. 94
4.5.1.2 Port Connection Rules ............................... 94
4.5.2 Interface Blocks ......................................... 95
4.5.3 Parameters ............................................. 99
4.5.4 Hierarchical Names ..................................... 100
4.6 Processes and Execution Threads ............................... 100
4.6.1 Static Processes ........................................ 101
4.6.2 Dynamic Processes ..................................... 101
4.7 Object-Oriented Programming and Classes ........................ 101
4.7.1 Static Properties and Methods ............................. 104
4.7.2 Class Hierarchy, Inheritance, and Abstract Classes ............. 105
4.7.2.1 Parent Class Scope: Super .......................... 106
4.7.2.2 Abstract Classes .................................. 106
4.7.2.3 Virtual Methods ................................... 107
4.7.2.4 Out of Block Declarations ........................... 108
4.7.3 Parameterized Classes ................................... 108
4.7.4 Polymorphism: Uniform Access to Diverse Objects ............. 109
4.7.5 Data Hiding ............................................ 110
x

Chapter 5: SystemVeriiog as a Verification Language ................. 113


5.1 Scheduling Semantics ......................................... 114
5.1.1 Active Region .......................................... 115
5.1.2 I nactive Region ......................................... 115
5.1.3 NBA Region ........................................... 116
5.1.4 Observed Region ....................................... 116
5.1.5 Reactive Region ........................................ 116
5.1.6 Postponed Region ...................................... 117
5.2 Clocking Blocks .............................................. 117
5.2.1 Input and Output Skews .................................. 120
5.2.2 Default Clocking ........................................ 121
5.3 Program Block ............................................... 121
5.4 Inter-Process Communication and Synchronization .................. 125
5.4.1 Events ................................................ 125
5.4.2 Semaphores ........................................... 127
5.4.2.1 Semaphore for Mutual Exclusion ...................... 127
5.4.2.2 Thread Rendezvous ............................... 128
5.4.3 Mailboxes ............................................. 130
5.5 Constrained Random Generation ................................ 131
5.6 Property Specification and Evaluation ............................. 131
5.7 .Coverage Collection .......................................... 131

Part 3: Open Verification Methodology ..................... 133


Chapter 6: OVM Infrastructure ................................... 135
6.1 Verification Class Library Goals .................................. 136
6.2 OVM Class Library Features .................................... 137
6.3 Object and Component Factories ................................ 140
6.3.1 Type and Instance Overrides .............................. 141
6.3.2 OVM Object Factory ..................................... 142
6.3.3 OVM Component Factory ................................. 145
6.4 Field Automation ............................................. 147
6.5 Core Utilities ................................................ 149
6.5.1 Copy ................................................. 150
6.5.2 Compare .............................................. 151
6.5.3 Print ................................................. 153
6.5.4 Packing and Unpacking .................................. 155
6.5.4.1 Packing and Unpacking of Automated Fields ............ 159
6.5.4.2 Packing and Unpacking Metadata ..................... 159
Chapter 7: OVM Component Hierarchy ............................ 161
7.1 Abstract View of the Component Hierarchy ......................... 161
7.2 Hierarchy and Configuration Constructs ........................... 164
7.3 Verification Environment Component Types ........................ 169
7.3.1 Verification Environment Components ....................... 169
7.3.2 OVM Models of Verification Environment Components .......... 172
7.4 Simulation Phase Control ...................................... 174
7.4.1 Starting the Simulation Phases ............................. 175
7.4.2 Stopping the Simulation Run Phase ......................... 176
Chapter 8: OVM Transaction Sequences .......................... 181
8.1 Verification Scenarios and Sequences ............................ 182
8.2 Sequencers ....... . ....................................... 184
xi

8.2.1 Sequence Items ........................................ 185


8.2.2 Sequencer Declaration ................................... 186
8.3 Sequences .................................................. 187
8.4 Hierarchical Sequences ........................................ 189
8.5 Sequence Library ............................................. 192
8.5.1 Predefined and User Defined Sequences .................... 192
8.6 Executing Sequences and Sequence Items ........................ 193
8.6.1 Executing Root Sequences and Subsequences using Methods ... 193
8.6.2 Executing Subsequences and Sequence Items using Macros ..... 195
8.7 Sequence Item Interfaces ...................................... 198
8.8 Sequencer Arbitration Mechanism ................................ 199
8.8.1 Sequence Interaction .................................... 200
8.8.2 Sequence Selection ..................................... 200
8.9 Virtual Sequencers ............................................ 202
8.10 Sequence Interfaces ......................................... 203
Chapter 9: OVM Transaction Interfaces ........................... 207
9.1 Transaction Connector Objects .................................. 209
9.2 Binding Connector Objects ..................................... 211
9.3 Unidirectional Interfaces ....................................... 213
9.4 Bidirectional Interfaces ........................................ 219
9.5 Analysis Interface ............................................ 224
9.6 Multiple Connector Objects in One Component ...................... 228
9.7 Transaction Channels ......................................... 229
9.7.1 TLM FIFO Channel ...................................... 231
9.7.2 Analysis FIFO Channel ................................... 232
9.7.3 RequesUResponse Channel ............................... 233

----------
Part 4: Randomization Engine and Data Modeling ............ 235
Chapter 10: Constrained Random Generation ...................... 237
10.1 Random Generators and Constraint Solvers ....................... 237
10.1.1 Constrained Randomization and Variable Ordering Effects ...... 238
10.1.2 Random Generation Engine .............................. 241
10.2 Randomization in SystemVerilog ................................ 243
10.2.1 Random Variables ..................................... 244
10.2.2 Random Dynamic Arrays ................................ 246
10.2.3 Constraint Blocks ...................................... 247
10.3 Constraint-Specific Operators .................................. 249
10.3.1 Set Membership Constraints ............................. 249
10.3.2 Distribution Constraints .................................. 250
10.3.3 Implication Constraints .................................. 250
10.3.4 If-Else Constraints ..................................... 251
10.3.5 Iterative Constraints .................................... 252
10.3.6 Global Constraints ..................................... 253
10.3.7 Variable Ordering Constraints ............................. 254
10.3.8 Function-Call Constraints ................................ 255
10.4 Constraint Guards ........................................... 255
10.5 ContrOlling Constrained Randomization .......................... 257
10.5.1 Controlling Constraints .................................. 257
10.5.2 Disabling Random Variables .............................. 258
10.5.3 Randomization Flow Methods ............................ 259
xii

10.6 Random Stability ............................................ 261


10.7 System Functions ........................................... 264
10.7.1 $urandom ............................................ 264
10.7.2 $urandom_rangeO ..................................... 264
10.7.3 srandomO . '" ..................... , .... '" ........... 264
Chapter 11: Data Modeling .....................................267
11.1 Data Models in SystemVerilog .................................. 268
11.2 Data Model Fields and Constraints .............................. 269
11.2.1 Data Model Fields ...................................... 269
11.2.2 Data Model Constraints .................................. 271
11.3 Hierarchical Models .......................................... 273
11.4 Data Model Subtypes ... " .......... '" .. '" .............. '" .274
11.5 Data Model Views ........................................... 277
11.5.1 Method View .......................................... 278
11.5.2 Constraint View ........................................ 278
11.5.2.1 Abstract Ranges ................................. 278
11.5.2.2 Coordinated Ranges .............................. 279
11.5.2.3 Default Ranges .................................. 279
11.6 Transactions: Command and Status ............................. 280

Part 5: Environment Implementation and Scenario Generation . .. 281


Chapter 12: Module-Based VE Implementation ...................... 283
12.1 Module-Based Implementation Overview ......................... 284
12.2 XBar Design Specification ..................................... 286
12.3 XBar VE Architecture ......................................... 288
12.4 DUV Wrapper ............................................... 289
12.5 Library Package ........................... , ....... , ....... , . 290
12.6 XBar Data Packet ....................................... , ... 292
12.7 Transaction Intertaces ........................... , ............ 292
12.8 Event Intertaces ............................. , ........ , ...... 294
12.9 Building the Hierarchy ........................................ 295
12.10 Monitor ................................................... 298
12.11 Driver ....................................... , ............ 300
12.12 Sequencer .... " ....... " ........................ " ... " .. 301
12.13 Scoreboarding ............................................. 304
12.14 Test Component ............................................ 306
12.15 Modifying Sequencer Default Behavior .......................... 307
Chapter 13: Class-Based VE Implementation ....................... 309
13.1 Class-Based Implementation Overview ........................... 309
13.2 XBar Data Packet ........................................... 311
13.3 Physical Interfaces ........................................... 312
13.4 Transaction Ports and Interfaces ........ " ........... " ......... 313
13.5 Event-Based Synchronization .................................. 314
13.6 Building the Hierarchy ........................................ 315
13.6.1 XBar Receive Agent .................................... 316
13.6.1.1 Receive Monitor .................................. 316
13.6.1.2 Receive Sequencer ............................... 318
13.6.1.3 XBar Driver ..................................... 320
13.6.1.4 XBar AgentTop Level Component ................... 321
13.6.2 XBar Transmit Agent ................................... 322
xiii

13.6.3 XBar Interface VC ...................................... 322


13.6.4 XBar Verification Environment ............................ 324
13.6.5 XBar Tests ........................................... 325
13.6.6 XBar Testbench ....................................... 327
13.7 Monitor Run Phase ........................................... 328
13.8 Driver Run Phase ........................................... 329
13.9 Sequencer ................................................. 330
13.10 Scoreboarding ............................................. 331
13.10.1 Implementation with User Defined Analysis Imp Types ........ 331
13.10.2 Implementation with Listener Subcomponents ............... 332
Chapter 14: Verification Scenario Generation ....................... 337
14.1 XBar Communication Protocol .................................. 337
14.2 XBar Transfer and Frame Models ............................... 339
14.3 XBar Sequence Generation Architecture .......................... 340
14.4 Flat Sequences ............................................. 343
14.4.1 Sequencer Default Behavior .............................. 345
14.4.2 Adding Sequences to the Sequence Library ................. 346
14.4.3 Modifying the Default Behavior ............................ 347
14.5 Hierarchical Sequences ....................................... 348
14.6 Reactive Sequences ......................................... 350
14.7 Virtual Sequences ........................................... 352
14.7.1 Virtual Sequencer ...................................... 354
14.7.2 Sequence Interface Connectivity .......................... 355
14.7.3 Downstream Sequences ................................. 356
14.7.4 Virtual Sequence Library ................................ 356
14.8 Grabber Sequences .......................................... 358
14.9 Layered Sequences .......................................... 361
14.10 Conditional Sequences ......................... : ............ 364
14.11 Sequence Synchronization with End of Run ...................... 367

Part 6: Assertion-Based Verification ....................... 369


Chapter 15: Property Specification and Evaluation Engine ............. 371
15.1 Property SpeCification Hierarchy ................................ 372
15.2 Boolean Expressions ......................................... 374
15.2.1 Operand Types ........................................ 374
15.2.2 Operators ........................ " .................. 375
15.2.3 Sampling Events ....................................... 375
15.2.4 Operand Value Sampling: Sampled Value vs. Current Value ..... 375
15.2.5 Sampled Value Functions ................................ 376
15.3 Sequences ................................................. 377
15.3.1 Sequence Declarations and Formal Arguments ............... 379
15.3.2 Sequence Evaluation Model .............................. 381
15.3.2.1 Evaluation Sub-Threads and Multiple Matches .......... 382
15.3.3 Sequence Operators .................................... 383
15.3.3.1 Sequence Delay Repeat Operators ................... 383
15.3.3.2 Sequence Repeat Operators ........................ 384
15.3.3.3 Sequence AND Operator. . . . . . . .. . ................ 386
15.3.3.4 Sequence OR Operator ............................ 387
15.3.3.5 Sequence INTERSECT Operator .................... 387
15.3.3.6 Sequence THROUGHOUT Operator ................. 388
xvi
Preface

Functional verification has been a major focus of product development for more than a
decade now. This period has witnessed the introduction of new tools, methodologies, lan-
guages, planning approaches, and management philosophies, all sharply focused on address-
ing this very visible, and increasingly difficult, aspect of product development. Significant
progress has been made during this period, culminating, in recent years, in the emergence
and maturity of best-in-class tools and practices. These maturing technologies not only allow
the functional verification challenge to be addressed today, but also provide a foundation on
which much-needed future innovations will be based. This means that having a deep under-
standing of, and hands-on skills in applying, these maturing technologies is mandatory for all
engineers and technologists whose task is to address the current and future functional verifi-
cation challenges.
A hallmark of maturing technologies is the emergence of multi-vendor supported and
standardized verification languages and libraries. The SystemVerilog hardware design and
verification language (IEEE standard 1800), and the SystemVerilog-based Open Verification
Methodology (OVM) provide a powerful solution for addressing the functional verification
challenge. SystemVerilog is an extension of Verilog-2005 (IEEE Standard 1364-2005), and
enhances features of Verilog by introducing new data types, constrained randomization,
object-oriented programming, assertion constructs, and coverage constructs. OVM, in tum,
provides the methodology and the class library that enable the implementation of a verifica-
tion environment according to best-in-class verification practices.
This book is intended for a wide range of readers. It can be used to learn functional ver-
ification methodology, the SystemVerilog language, and the OVM class library and its meth-
odology. This book can also be used as a step-by-step guide for implementing a verification
environment. In addition, the source code for the full implementation of the XBar verifica-
tion environment can be used as a template for starting a new project. As such, this book can
be used by engineers starting to learn the SystemVerilog language concepts and syntax, as
well as advanced readers looking to achieve better verification quality in their next verifica-
tion project. This book can also be used as a reference for the SystemVerilog language and
the OVM class library. All examples provided in this book are fully compliant with System-
Verilog IEEE 1800 standard and should compile and run on any IEEE 1800-compliant simu-
lator.
xviii

Acknowledgements
The creation of this book would not have been possible without the generous support of
many individuals. I am especially grateful to David Tokic and Luis Morales for helping tum
this book from a nascent idea into a viable target, to Susan Peterson for getting this project
off the ground and for her infectious positive energy, and to Tom Anderson for his continued
technical and logistical guidance and support throughout the life of this effort. Special thanks
also go to Sarah Cooper Lundell and Adam Sherer for valuable planning and technical dis-
cussions, and to Ben Kauffman, the technical editor.
The examples included in this book were developed and verified using the Incisive
Functional Verification Platform® developed by Cadence Design Systems, and obtained
through Cadence's Verification Alliance program. I would like to thank Cadence Design
Systems and the Verification Alliance program for their generous support of this effort.
The technical content of this book has benefited greatly from feedback by great engi-
neers and technologists. Special thanks go to David Pena and Zeev Kirshenbaum for
itt-depth discussions on many parts of this book. In addition, technical feedback and discus-
sions by individuals from a diverse set of companies have contributed significantly to
improving the technical content of this book. I am especially grateful to these individuals
whose nan:les and affiliations are listed below.
Sasan Iman
Santa Clara. CA
Spring 2008

Adiel Khan Cadence Design Systems


Abhijit Sinha SiMantis Inc.
AnupSharma Azul Systems
Chuck Chiang Cisco Systems
DavePena Cadence Design Systems
Firooz Massoudi Maxim Integrated Products
Gabi Leshem Cadence Design Systems
Gerard Ventura Cadence Design Systems
James Baldwin Qualcomm
Jayavel Sabapathy SiMantis Inc.
Jenny Zhang Cadence Design Systems
Phu Huynh Cadence Design Systems
Poornachandra Rao Analog Devices
Richard Miller Cisco Systems
SyedMahmud Maxim Integrated Products
Tim Pylant Cadence Design Systems
Umer Yousafzai Cadence Design Systems
Vincent Huang AMD
Virendra Jaiswal SiMantis Inc.
Warren Stapleton Montalvo Systems
Yahya Khateeb PLX Technology
Zeev Kirshenbaum Cadence Design Systems
Online Resources

System Verilog

To learn more about the System Veri log language, and to keep up with the latest updates to
the SystemVeriiog LRM, visit:
http://www.SystemVerilog.org/
http://www.EDA-stds.org/sv/

OVM
This book is based on OVM release 1.0.1. To download the latest version of the OVM class
library, to participate in OVM user forums, to learn about the latest related news and semi-
nars, and to contribute to the OVM community, visit:
http://www.OVMWorld.orgt

Book Examples and Updates


To download the full implementation of the XBar design and its verification environment,
examples used in this book, and for any updates and/or errata released for this book, visit:
http://www.SiMantis.com/

Feedback
We welcome your feedback on this book. Please email your feedback to:
fvsvovm@simantis.com
xx
Book Structure

This book provides a complete guide and reference for functional verification methodology,
learning the System Verilog language, and for building a verification environment using Sys-
temVerilog and the OVM class library. Given the range of material covered in this book, it is
expected that the focus of anyone reader may be on one specific topic, or that anyone reader
may prefer to bypass familiar content. To better support the range of readers who can benefit
from this book, its content is grouped into parts. These parts are ordered so that the prerequi-
site knowledge required for any topic is covered in the parts appearing before that topic. This
means that this book can be studied in a linear fashion, but the clear breakdown of topics into
these parts facilitates selective focus on anyone topic. This book consists of the following
parts:
• Part 1: Verification Methodologies, Planning, and Architecture
This part focuses on the functional verification problem, its relation to the product
development flow, challenges that it raises, available tools, and metrics that are
used for evaluating the effectiveness of a functional verification approach (chapter
I). This part also provides a detailed description of verification planning for a cov-
erage-driven verification flow (chapter 2). The architectural view of a verification
environment is also described in this part of the book (chapter 3).
The discussion in this part of the book is implementation independent, and pro-
vides the background that is necessary before a verification solution can be imple-
mented.
• Part 2: All about SystemVerilog
This part provides a detailed introduction to the SystemVerilog language by
describing its programming (chapter 4) and verification related features (chapter
5). This part can be used to learn the syntax and semantics of the SystemVerilog
language, and to also learn its veri fication related features. In addition to chapter 4,
randomization features are described in chapter 10, assertion features are described
in chapter 15, and coverage features are described in chapter 17. The content in
this part is organized through tables and examples so it can also be used as a desk
reference for the SystemVerilog language.
xxii

• Part 3: Open Verification Methodology


This part provides an in-depth description of OVM features, and gives examples of
using these features to implement the different pieces of a verification environ-
ment Chapter 6 describes the infrastructure and the core utilities of the OVM class
library, Chapter 7 describes OVM features for building a verification environment
hierarchy and support for modeling simulation phases, Chapter 8 describes trans-
action sequence (i.e., scenario) generation architecture of the OVM, and the utili-
ties available for implementing this architecture. Chapter 9 describes transaction
and channel interfaces, and their implementation using the predefined classes of
the OVM class library.
The discussions in this part describe the architectural view of verification environ-
ment elements based on OVM constructs, and illustrate the implementation of
these elements through small and self-contained examples. The use of OVM con-
structs described in this part of the book for implementing a full-scale verification
environment is described in part 5 of this book.
• Part 4: Randomization and Data Modeling
This part provides an in-depth look at the randomization feature of SystemVeri log
by describing how the randomization engine operates, constructs provided by Sys-
temVerilog, and issues that must be considered when using randomization (chapter
10). ThIs part also describes techniques and the relevant issues for building a data
model using the SystemVerilog language constructs (chapter 11). A data model
implemented using the guidelines described in this chapter is used to represent a
transaction that is exchanged between verification environment components,
• Part 5: Verification Environment Implementation and Scenario Generation
This part describes the implementation of a verification environment using a mod-
ule-based (chapter 12) and a class-based (chapter 13) approach. It also describes
the implementation of transaction sequences for modeling verification scenarios
(chapter 14). The description in this part is based on the XBar design, a cross-bar
switch (section 12.2). The discussions on class-based implementation of the verifi-
cation environment for the Xbar design (chapter 13) and the generation of its trans-
action sequences (chapter 14) provide a complete example of a verification
environment implementation using the OVM class library and its recommended
guidelines.
• Part 6: Assertion-Based Verification
This part provides an in-depth description of the sequence and property specifica-
tion constructs of SystemVerilog (chapter 15), and describes the recommended
methodology for assertion-based verification (chapter 16). A good understanding
of the material in this part is needed for having the ability to write concise and
complex properties, and to make effective use of assertions in reaching verification
closure.
• Part 7: Coverage Modeling and Measurement
This part provides an in-depth description ofthe coverage constructs of the Sys-
tem Verilog language (chapter 17). The definition of a coverage plan, implementa-
tion of a coverage plan using the constructs provided by SystemVerilog, and the
flow for carrying out a coverage-driven verification flow is described (chapter 18).
1

PART 1
Verification Methodologies,
Planning, and Architecture
2
CHAPTER 1 Verification Tools and
Methodologies

The introduction of SystemVerilog as a new hardware design and verification language is


motivated by the need for a powerful tool that can facilitate the implementation of the latest
verification methodologies. As such, having intimate knowledge of the challenge posed by
functional verification, the latest verification methodologies, and the way in which design
and verification flow fit together is a first step in the successful use of SystemVerilog in
completing a verification project.
This chapter brings functional verification into focus by describing its context, the chal-
lenges it raises, and the approaches that best address these challenges. Section 1.1 sets the
stage by describing the background against which a verification project is executed, the
meaning offunctional verification within this context, and the challenges faced in successful
execution of a verification project. The execution of a verification project cannot be
improved without tangible metrics for evaluating its quality. Section 1.2 introduces metrics
that collectively define the execution quality of a verification project. Section 1.3 summa-
rizes the interaction between design and verification, and in doing so, highlights the natural
order of verification project execution imposed by design activities. Section 1.4 describes the
different technologies that are available for verification and discusses where in the verifica-
tion flow they best fit. Finally, section 1.5 describes best-in-class verification methodologies
that leverage the available technologies to address verification challenges while adhering to
restrictions inherent in its order of execution.

1.1 Setting the Stage


- - - - - - = - - - - - - = - - - - - - - - - - - --------------------

A functional verification project plays against the backdrop of a product development flow.
The following subsections describe the product design flow, the meaning of functional verifi-
cation and challenges that must be met to successfully and efficiently execute a verification
project.
4 Verification Tools and Methodologies

1.1.1 Design Flow: From Intent to Product


Good knowledge of the design process is needed for effective management of verification
project complexity. This knowledge is also necessary for understanding the effect of local
verification decisions on the overall progress and effectiveness of verification. This section
provides an overview of the design flow.

O Design Intent
(Descriplive Text)
Product cannot
be implemented

-----------~
/O---]-F-Un-c-tio-n-a-ls-p-eC-ifi-lc-at-io-n'
Product and System
Engineers

~~----------------~ Specification cannot


be implemented.
(Descriptive Tex! Transactio
Level Models (TLM)) -------...

~D~'" ",'"M.
0_-- DeSign Implementation
(HDl: Verilog, VHDl)
~ Implementation targets
cannot be met.

Synthesis &
Back-end Tools

Figure 1.1 Product Development Flow

Product development flows are as varied as the products they produce. Most flows,
however, can be described in terms of abstract phases corresponding to product idea devel-
opment and design and implementation stages. Figure l.1 shows an overview of one such
design flow, from intent to final product. A product is usually scoped by a marketing team as
an opportunity to satisfy a demand in the marketplace. The initial description of a product
must be described with careful consideration of the overall abilities and limitations of the
underlying technologies. This consideration is essential for delivering the product with good
confidence, on target, and with the desired functionality. The initial product intent is turned
into a functional description through discussions between the marketing team and product
and system engineers. These discussions are geared towards solidifying the general features
of the product (e.g., user level features available to consumer, the number and types of
required interfaces) and identifying the architecture that is best suited for delivering the
desired functionality. An important part of this architectural exploration stage is to perform
analysis sufficient for confirming that the required features are practical and can be sup-
ported by the suggested architecture (e.g., that the proposed bandwidth of the internal bus of
a multi-interface device can support the expected traffic between all interfaces).
Transaction le\'el models (TLtvI) are used at the architectural level of abstraction to
model the blocks identified in the early stages of architectural exploration and analysis. In
Setting the Stage 5

general, tr~nsaction level model~ allow designers to specify bl.ock behaviors at a high level
of abstractIOn where the focus IS on. the system .level behavIor of blocks and interaction
between blocks, and not on low-levellmplementahon and cycle accurate behaviors.
Once the architectural model of a design is finalized, design engineers take this fu _
tion.al specification a~d crea~e an impl~men~ati~n in a target lang~age (e.g., Verilo g, Syst:-
Venlog, VHDL). ThIs functIOnal specIficatIOn IS then translated mto a final implementati.Q
through a series of steps in which appropriate tools and techniques are used t(} create t~
design implementation. Once the design implementation is produced, the final prOduct is cre-
ated by using the synthesis and back-end tools to build the chips and boards that will com-
prise the final product.
Transaction level models play an important role in the design and verification process in
that they provide an early model of the design that can be used for architectural evaluation
and whose verification must be considered in the verification flow. An overview oftransac_
tion level models and the design flow is described in the following subsections.

1.1.1.1 Transaction Level Modeling and Design Abstraction Levels


Different levels of abstraction are used in taking a design through stages of implementation.
These abstraction levels are:
• Transaction level models (TLM)
• Register transfer level models (RTL)
• Netlist of gates
Unlike a register transfer level description (which requires that a design be described in
terms of registers, combinational blocks, and their interconnections), and netlist of gates
(which requires that a design be described in terms of gates in a target library and their inter-
connections), transaction level models are not an abstraction level in the strict sense of the
term. This is because transaction level modeling allows models to contain a range of detail
all the way from algorithmic description down to cycle accurate models.
Modeling a design at the transaction level has multiple benefits. It allows a system to be
modeled when there is very little detail available about the internal implementation of its
blocks. Also, given the low-level of detail in a transaction level model, it is possible to create
fast simulation models of the system that can be used by software developers to work on
software applications that will eventually run on the completed system. Given that the focus
in transaction level modeling is more on inter-block communication, these models are ideal
for doing system performance analysis and verifying that a system can meet the expected
performance requirements.
A transaction level model can be developed at the following levels of detail:
• Architectural view CAY)
• Programmer's view CPV)
• Programmer's view plus timing (PVT)
• Cycle callable (CC)
6 Verification Tools and Methodologies

Architectural view models a timeless communication mechanism between modules


where the focus is on the fact that a transaction is moved between blocks and not on how or
in what exact timeline the transfer takes place.
Programmer s view models inter-module communication explicitly using blocking
transactions between modules where a module waits for the other module to complete the
transaction before continuing with its operation. This approach, through its blocking mecha-
nism, provides a means of ordering module executions based on their communication
requirements.

Programmer s view with timing model improves programmer's view by modeling


inter-module communication using blocking and non-blocking interactions where exchange
of transactions include an estimate of transaction time. As such, in this model, module exe-
cution ordering is controlled by the timing estimates for transactions.
Cycle callable view further enhances programmer's view with timing with accurate
cycle level timing behavior. Beyond cycle callable view, a bus functional model is used to
model the interaction between modules.
A model developed at the transaction level can be improved as more detail about the
implementation of a block becomes available. As such, a transaction level model provides a
path to continually improving the system level model of a design.
The use of transaction level models in system modeling is an important concept from a
verification perspective. The reason is that these transaction level models are used for system
level modeling and as such, their implementation must also be verified in the same sense that
RTL descriptions of the design must be verified. In addition, transaction level models can be
leveraged during chip and system level verification to improve verification speed. Also, con-
cepts from transaction level modeling can be used for communication between verification
environment blocks.

OSCI I has standardized an API that describes predefined transactions that can be used
for TLM based interactions. These standard transaction types can be modeled in SystemVer-
ilog and for inter-module communication. The SystemVerilog implementation of this API is
described in chapter 9.

1.1.1.2 Design Implementation Flow


The goal of functional verification is to verify correct implementation of the design produced
by design engineers from the design specification. This step corresponds to the third step in
figure 1.1.

Figure 1.2 shows details of a typical flow used by designers to turn a functional specifi-
cation into a design implementation. This flow consists of the following steps:
• Architectural design
• Block design

1. The ideas and general guidelines of transaction level modeling have been standardized t>y the Open Sys-
terne Initiative (OSCII (http:www.systemc.org) who at the time of this writing. provides TL\I 2.0 as a
standard API for developing transaction le\'el models using SystemC.
Setting the Stage 7

• Module design
• Chip/System design
The very first step is to create the design architecture. This step identifies the individual
modules and blocks in the system and describes the functionality for each block. Only after
this architecture is decided can the design of these individual blocks get started. The next
step is to design the individual blocks identified during the architectural design stage. Blocks
are implemented as RTL descriptions, and then grouped together to form design modules.
Modules may optionally contain blocks described at the transaction level that model
non-digital parts of the system (e.g., analog parts, RF blocks, etc.). Modules are combined to
form the full-chip design, which is then combined to form the implementation description of
the entire system.

Architecture Design (identify blocks)


~ TLM Descriptions

Block Design
~ RTL Description

Module Design
~ +
~
Chip/System Design
~ +
~
Figure 1.2 Design Implementation Flow

This breakdown of the design implementation flow hints at the way verification activity
dovetails with design activity. The interaction between design and verification flows is
described in more detail in the next section.

1.1.2 Functional Verification: Gaining Closure


Product design flow, as, shown in figure 1.1, points at two stages which fundamentally lead to
functional errors and malfunctions in the final product:
• Translating the product intent into a functional specification
• Translating the functional specification into a design implementation.
These steps require manual processing of at times vaguely described ideas and goals in
non-exact natural languages by engineers and operators who can be prone to misunderstand-
ing descriptions and making errors in following well defined procedures. The error-prone
nature of this step is in contrast with the automated translation of design implementation into
8 Verification Tools and Methodologies

a netlist of gates. This step is perfonned by using synthesis and physical design tools which
are less prone to errors because of their high degree of automation and tool maturity.
The main source of functional errors in a design can be attributed to the following:
• Ambiguities in product intent
• Ambiguities in the functional specification
• Misunderstandings by designers even when the specification is clear
• Implementation errors by designers even when the understanding is correct
The primary goal offunctional verification is to verify that the initial design implemen-
tation is functionally equivalent to product intent. Or alternatively, proving the convergence
of product intent, functional specification, and design implementation.
Figure 1.3 shows a pictorial view of this concept. The process of functional verification
facilitates the convergence of product intent, functional specification, and design implemen-
tation, by identifying any differences between the three and giving the system and design
engineers the opportunity to eliminate this difference by appropriately modifying one, two,
or all three descriptions so the difference is eliminated. Verification closure is gained when
this convergence is proved with a high degree of confidence .

..
Functional
Verification

Figure 1.3 Design Intent, Specification, and Implementation

1.1.2.1 Black-Box, White-Box, and Gray-Box Verification


The ultimate goal of functional verification is to verify that a design works as expected when
stimUlated on its boundary. As such, any errors in the design that cannot be detected by stim-
ulating and observing its boundary can effectively be ignored. Such errors include:
• Errors that will never be activated
• Errors that can be activated but will never be propagated to design outputs
• Multiple errors that can potentially hide one another
Wack-box verification refers to \'erif\'ing a block or design functionalitv only through
its boundary signals. Figure 1.4 shows the gen~eral architecture for performing'black-box \';r-
ification. In this approach, stimulus is applied to both the design under verification (DUV)
and a reference model (i.e .. golden model). Outputs produced by the DUV and the golden
Setting the Stage 9

model are then checked to be equivalent within the abstraction level (e.g., transaction accu-
rate, instruction accurate, cycle accurate, etc.).

Black-Box Verification

Gray-Box Verification

White-Box Verification

Figure 1.4 Black-Box, Gray-Box, and White-Box Verification Approaches

Black-box verification suffers from the following major drawbacks:


• Difficult to verify features related to design decisions
• Difficult to debug errors
• Requires an accurate reference model
All implementations of the same design specification are not created equal. Each imple-
mentation contains many design decisions that give it the performance or efficiency that
make it different from other implementations. A CPU instruction pipeline is an example of
this type of design implementation feature. A CPU may appear to be working even if its
instruction pipeline is not working as expected, or not working at all. The performance of
such a crippled design will, however, be negatively affected. Other examples of such features
include the threshold settings for FIFO read/write operations or details of bus arbiter sched-
uling. It is very difficult to verify correct implementation of such features using the
black-box verification approach.
An added complication in using black-box Yerification is that depending on the com-
plexity of the DUV, it may take many cycles for the effect of an internal error to appear on
the design outputs, therefore requiring much longer simulation traces to expose an error.
10 Verification Tools and Methodologies

Even when an error is detected on the output, it is usually very difficult to trace the problem
to its root cause.
An added difficulty in using black-box verification is that it requires a reference model
implemented with enough accuracy to detect any and all internal bugs. Given that a model
with such strict accuracy requirements (e.g., cycle accurate) may not be available or perhaps
as difficult to build as the design itself, it is not always possible to rely completely on this
verification approach.
White-box and gray-box verification provide alternative approaches for addressing the
limitations of black-box verification. In white-box verification, no reference model is
needed, because correct design operation is verified by placing monitors and assertions on
internal and output signals of the DUV. Grav-box verification is a combination of white-box
and black-box verification approaches, where monitors and assertions on internal design sig-
nals are used along with a reference model. The use of monitors and assertions reduces the
accuracy requirements of the reference model and also reduces debugging effort when bugs
are found. These architecture used for white-box and gray-box verification approaches are
also shown in figure 1.4.
Table 1.1 lists challenges inherent in completing a verification project, and also lists the
degree of effort required to address these challenges when each of the verification
approaches discussed in this section are used. As shown, gray-box verification provides the
best balance of effort required to address these different challenges. As such, gray-box verifi-
cation is the approach intuitively chosen most often by verification engineers to carry out
their tasks. It should be emphasized that there are many shades of gray in gray-box verifica-
tion, where these shades refer to the balance of effort dedicated to reference model develop-
ment and monitor/assertion development. The exact shade of gray-box verification will
ultimately depend on the specific requirements of a verification project and the verification
engineer's experience from previous projects.

Verification Approach

Verification Challenges
....
=
.= ..=
~
~ !XI
..:t ;:.,
" :a ..."
I iii" I ~ ~

Effort to create reference model High None Medium


Effort to add monitors and assertions None High Low
Effort to trace bug on output to its source High Low Low
Effort to verify implementation related features High Low Low

Table 1.1: Verification Approaches and Effort Needed to Create Verification Implementation

1.1.3 The Verification Challenge


Functional verification is a simple problem to state but a challenging one to address. The
increasing size and complexity of designs and shortening time-to-market windows mean that
verification engineers must verify larger and more complex designs in a shorter time than in
Setting the Stage 11

previous projects. An effective solution to meeting this increased demand for achieving veri-
fication closure must address the following verification challenges:
• Completeness
• Reusability
• Efficiency
• Productivity
• Code performance
The challenge in verification completeness is to maximize the part of design behavior
that is verified. The major challenge in improving verification completeness is in capturing
all of the scenarios that must be verified. This, however, is a manual, error-prone, and omis-
sion-prone process. Significant improvements in this area have been made by moving to cov-
erage-driven verification methodologies. Coverage-driven verification approaches require a
quantitative measure of completeness whose calculation requires strict planning, tracking,
and organization of the verification plan. This strict requirement on verification plans natu-
rally leads to exposing the relevant scenarios that may be missing. Fine-tuned verification
planning and management methods have been developed to help with the planning and track-
ing of verification plans.
The challenge in verification reusability is to increase portions of the verification envi-
ronment infrastructure that can be reused in the next generations of the same project or in a
completely different project, sharing features that are similar with those in the current proj-
ect. A high degree of reuse can be achieved for standardized interfaces or functions. Blocks
can be reused in any project that make use of the same standardized interface. Beyond stan-
dardized interfaces, identifying common functionality in the verification environment plan-
ning stage can lead to further reuse of verification infrastructure.
The challenge in verification efficiencv is to minimize the amount of manual effort
required for completing a verification project. Clearly, manual efforts are error-prone, omis-
sion-prone, and time consuming. In contrast, automated systems can complete a significant
amount of work in a short time. Automated systems must, however, be built manually. As
such, improvements in efficiency must be made through careful analysis of the trade-off
between the extra effort required for building an automated system and the gains it affords.
Coverage-driven pseudo-random verification methodology is an example of a methodology
where making the effort to build an automated system for stimulus generation and automated
checking leads to significant improvements in verification efficiency, and hence productivity.
An important consideration in deciding the feasibility of building an automated system is
that such automation requires a consistent infrastructure on which it can be developed, and
also imposes a use model on how engineers interact with it. As such, deployment of an auto-
mated system requires consistency both in infrastructure and engineering approach, both of
which take time and targeted effort to achieve.
The challenge in verification productivity is to maximize work produced manually by
verification engineers in a given amount of time (e.g., number of failed scenarios debugged,
verification environment blocks implemented, etc.). Achieving higher productivity has
become a major challenge in functional verification. Significant improvements in the design
flow have afforded design engineers with much higher productiyity. Improvements in verifi-
cation productivity have, however, lagged those on the design side, making functional verifi-
cation the bottleneck in completing a design. Effective functional verification requires that
12 Verification Tools and Methodologies

this productivity gap between design and verification be closed. Productivity gains in verifi-
cation can be obtained by moving to higher levels of abstraction and leveraging reuse con-
cepts.
The challenge in verification code performance is to maximize the efficiency of verifi-
cation programs. This consideration is in contrast with verification productivity, which deals
with how efficiently verification engineers build the verification environment and verify the
verification plan. The time spent on a verification project is usually dominated by the manual
work performed by verification engineers. As such, verification performance has usually
been a secondary consideration in designing and building verification environments. An
important area in which verification performance becomes a primary consideration is in run-
ning regression suites where the turnaround times are dominated by how efficiently verifica-
tion programs operate. Expert knowledge of tools and languages used for implementing the
environment is a mandatory requirement for improving verification performance.
The methodologies and topics discussed in this book aim to address these verification
challenges through the following topics:
• Best-in-class verification methodoiogies
• Improved verification planning processes
• Best use of System Verilog features and constructs
• Reuse considerations

.!.2 Verification M..~1!i~~_____ . ___.___.____________________


Deciding which verification methodology is best suited for a given application should be
based on a set of well defined metrics. Such metrics relate directly or indirectly to verifica-
tion challenges discussed in section i.l.3. The following verification metrics are used to
facilitate this type of comparative analysis:
• Granularity
• Manual effort
• Effectiveness
• Completeness
• Correctness
• Reuse of verification environment
• Reuse of simulation data
Verification quality is the collective result of these individual metrics. These metrics are
used to compare and contrast the verification methodologies discussed later in this chapter.
The following subsections further discuss each metric.

1.2.1 Granularity
/'erification granularity is a qualitative measure of the amount of detail that must be speci-
fied in describing and implementing a verification scenario. Granularity directly affects veri-
Verification Metrics 13

fication productivity by considering the etTort required by verification engineers to deal with
verification objects.
Verification granularity is ditTerent from design granularity. Design granularity refers to
the abstraction level used at ditTerent stages of the design flow. For example, a transaction
level model is described in terms of transactions and, therefore, verification scenarios
described for such a model are also described at the transaction level. Verification granular-
ity refers to using a higher level granularity than design granularity in describing verification
scenarios. For example, for a USB port design described at the register transfer level (Le., in
terms of registers, combinational operators, and wires and buses), a verification scenario can
be described in terms of sending a bulk transfer instead of specifying the individual signals
that must be assigned in doing such a transfer. Describing scenarios at a higher level of
abstraction than the design abstraction requires an automated method of translating a higher
level statement (e.g., sending a bulk transfer) into signal activity at the USB port. In a verifi-
cation environment, a driver (section 3.2.2) is used to facilitate this type of translation.
Verification granularity can be increased by:
• Describing scenarios at a higher level of abstraction
• Implementing scenarios using higher level language constructs
By describing scenarios at a higher level of abstraction, a verification engineer is able to
focus on verification scenarios and not on low-level details of how that scenario is carried
out.
A verification engineer can produce and debug only a limited amount of verification
code in a day. Allowing verification engineers to implement a feature using higher level lan-
guage constructs leads directly to higher productivity in building the verification environ-
ment. System Veri log provides a number of language constructs (e.g., randomization,
coverage collection, property specification) aimed directly at allowing verification engineers
to describe higher level intent with fewer lines of code.

1.2.2 Manual Effort


Verification manual e(fort is a measure of time spent by engineers in completing the verifica-
tion project. Sources of manual effort are:
• Writing the verification plan
• Building the verification environment
• Executing verification scenarios
• Debugging failed scenarios
Some verification activities must be carried out by engineers. For example, extracting
the verification plan from the design specification and design implementation has to be done
manually and cannot be automated (at least not yet). Debugging also has to be done manu-
ally. It is, however, possible to make trade-otTs in carrying out some tasks manually versus
building an automated program for executing that task. For example, random generation of
stimulus requires that coverage be collected in order to measure verification progress. Extra
effort is required in building the infrastructure necessary to apply random stimulus and col-
lect coverage, but the degree of automation obtained through such initial etTort makes this a
14 Verification Tools and Methodologies

good strategy to follow. Note that such a trade-off may not exist for a very small verification
project where the manual effort for building an automated system is greater than the effort
required for completing the verification using directed-tests. In general, a trade-off analysis
should be done to decide how much and what parts ofthe verification environment should be
automated. Given the complexity of today's projects, more automation generally leads to
less overall manual effort and, therefore, more productivity.

1.2.3 Effectiveness
Executing a verification plan usually consists of multiple simulation runs, each consisting of
different verification scenarios. In general, not every simulation cycle contributes to verify-
ing a new scenario and not all simulation runs execute unique scenarios. Verification effec-
tiveness is a measure of how much of the simulation time contributes directly to covering
more scenarios. All simulation runs that only verify previously verified scenarios should be
removed from the simulation regression.

1.2.4 Completeness
Verification completeness is a measure of how much of the relevant design functionality is
verified. Verification plan completeness refers to how much of the relevant functionality of
the design is included in the verification plan. Ideally, a verification plan should be complete
in that it should include all relevant scenarios. Verification plans are, however, rarely com-
plete, since it is not possible to enumerate all corner cases of a complex design. Verifications
plans may also be incomplete because of poor verification management processes or lack of
time.

Verification methodology affects verification completeness. For example, if all scenar-


ios are verified using directed testcases, then verification completeness is only as much as
the directed scenarios that have been verified. In a methodology where stimulus is randomly
generated, scenarios that are not specified in the verification plan may be generated as a
by-product of the random generation process. In this sense, a random generation approach
provides better verification completeness than directed-test-based verification.

1.2.5 Verification Environment Reusability


Verification environment reusability is a measure of how much of the verification environ-
ment is reused in subsequent verification activities. Possibilities include:
• Reuse of environment across design generations
• Reuse of environment blocks across design stages (block to system level)
Changes to a design across successive generations includes the addition of new features
or changes in architecture. Such changes appear in two flavors: 1) those requiring architec-
tural changes in the verification environment, and 2) those requiring additional scenarios to
be verified using the same verification architecture. Verification environment reuse possibil-
ities based on these two flavors should be identified before the environment is built, by iden-
tifying design and architectural features that are expected to remain the same and those that
Interaction between Design and Verification 15

are expected to change. The architectural design of a verification environment should


include clear boundaries between these features so that the verification environment and its
blocks can be leveraged for verifying future generations of the design.
The more compelling motivation for reuse is leveraging verification pieces across the
design flow as the design integration is moved from block to module and to system level.
Verification infrastructure for blocks at the boundary of the system level design can directly
be reused in the verification environment for the next level of integration. After integration,
the verification focus for internal blocks moves from stimulus generation and collection to
monitoring and checking. As such, a general guideline for making reuse possible is to sepa-
rate stimulus generation functionality from checking and monitoring functionality.

1.2.6 Simulation Result Reusability


Simulation result reusability is a measure of whether data produced during design simulation
can be reused to answer questions raised about conditions that occurred during that simula-
tion run. In the extreme, any and all questions about a simulation run can be answered if a
global dump of all signal changes in the design is stored. This approach, however, is not fea-
sible for a real design with even moderate complexity. As such, simulation result reusability
must be considered as a factor when deciding how to track a simulation run.
Simulation result reusability is a factor that should be considered in coverage modeling.
A good strategy is to anticipate the types of questions that may be raised after simulation is
completed and to collect enough data during simulation so that answers to such questions can
be provided from the collected data. For example, a coverage collection model may collect
only information on what bus transaction types were observed during the simulation process.
After simulation is completed, a question may be raised about how many transactions types
were repeated back-to-back at least once.

1.3 Interaction between Design and Verification


Verification engineers depend on designers to provide them with the design implementation.
Design engineers in turn depend on verification engineers to provide them with the verifica-
tion environment they need to verify their blocks and clusters as they make progress in com-
pleting the design. As such, successful completion of a design project requires careful
orchestration of the interaction between system, design, and verification engineers.
Figure 1.5 shows an overview of design flow and each engineer's responsibilities as the
design is carried out to completion. Initially, all engineers participate in preparing a verifica-
tion plan. Once the verification plan is available, verification engineers first prepare the
interface verification components that will be used to interact with design boundary mod-
ules. Such interface verification components are readily available for standardized protocols.
The verification engineers will then prepare the module level testbench and the system leyel
testbench. While verification engineers are preparing the module level testbench. design
engineers implement system blocks and verify these blocks by writing assertions that are
16 Verification Tools and Methodologies

checked using a fonnal verification tool. These blocks are then combined to fonn design
modules. At this point, the module testbench developed by the verification engineers is used
by the design engineers to verify design modules. Modules are then combined by design
engineers to create the complete system. At this time, the system testbench created by the
verification engineers is used to verify the system level design. The system is modeled by the
system engineers at the architectural levels and using transaction level models. Architecture
verification continues by system engineers as work progresses on module and system test-
bench development where these testbenches are used by system engineers to further verify
architectural assumptions and perfonnance.

System Engineers Verification Engineers I Design Engineers


I

I Verification PlanninQ

Interface Verification
Component (SV)

Figure 1.5 System, Design, and Verification Engineer Interactions

Technologies available for performing functional verification fall into three categories:
• Fonnal verification
• Simulation-based verification
• Acceleration/Emulation-based verification
The following sections provide an overview of these approaches.
Verification Technologies 17

1.4.1 Formal Verification


Formal verification uses logical and mathematical formulas and approaches to prove or dis-
prove a given property of a hardware implementation. It is important to emphasize that since
formal verification operates on equations describing the system and not on test vectors, any
property proved by a formal verification tool holds for all possible test vectors applied to that
behavior.
Formal verification has two major advantages over other verification technologies:
• Formal verification techniques are able to make universal statements about a property
of a design implementation holding for all possible input streams
• Formal verification techniques do not require test vectors to be applied
These advantages of formal verification make this verification approach suitable for
gaining full confidence that a design property holds for all cases. Also, the second advantage
allows formal methods to be applied when a testbench and test vectors are not yet available.
Formal verification techniques fall into two major categories
• Equivalence checking
• Property checking
These approaches are discussed in the following subsections.

1.4.1.1 Equivalence Checking


In this approach, the equivalence checking formal verification tool proves that two hardware
implementations are functionally equivalent under all possible input combinations and
sequences. This means that, for example, a finite state machine will produce the exact same
output sequence under all possible input sequences and same initial conditions.
The input to an equivalence checking tool is two formal representations of a design
implementation before and after a given transformation. The equivalence checking tool cre-
ates a canonical model2 of each implementation. Every Boolean function has a unique
canonical representation under an assumed set of conditions (e.g., variable ordering). As
such, once the canonical representation of the implementation before and after the transfor-
mation is available, then under ideal conditions, proving the equivalency of the two represen-
tations is straightforward.
In practice, however, building an equivalence checking tool for real sized designs and
for transformations occurring in the design process is a difficult task. First of all, an equiva-
lence checking tool requires that a formal model of the implementation before and after a
transformation be available. Looking at the product design flow in figure 1.2, such models
are available only before and after the synthesis process where RTL descriptions are trans-
lated into a netlist of gates. The equivalence checking between an RTL and the netlist of
gates is, in fact, the most common application of equivalence checking tools.

~. A Canonical model ofa Boolean function is a representation of that function that is unique under a
given ordering of its variables. A Binary Decision Diagram is one such canonical representation for Bool-
ean functions and is used extensively in tools for formal verification of digital systems.
18 Verification Tools and Methodologies

Other challenges exist in building an equivalence checking tool. Building canonical


representations of a design is not practical for very large systems. This means that special
tricks must be deployed or manual intervention may be required to reduce the size of blocks
that will be fonnally checked. One technique for reducing block sizes is to identify intenne-
diate signals that are common between the before and after representations. Also, some
transformations such as re-timing (i.e., moving combinational logic across registers during
technology mapping and timing optimization) cannot be easily handled by implementations
of the straightforward equivalence checking approach explained above.
These challenges have been addressed by new tools targeting equivalence checking,
making them a required utility for synthesis and back-end flow. However, the need for for-
mal representation of before and after transfer-and the fact that such complete representa-
tions are usually not available before the RTL design stage-mean that equivalence checking
approaches cannot make a large contribution to the main challenge of functional verification
and are best suited for later stages of product design flow. A property checking approach to
formal verification, however, provides a very powerful technique for addressing the func-
tional verification challenge. This approach is described in the following section.

1.4.1.2 Formal Property (Assertion) Checking


Given a formal description of a design implementation (e.g., an RTL description), property
checking verifies that a given property described in a temporal logic3 holds for the given
implementation.
Figure 1.6 shows an example of how property checking is used to prove or disprove that
a given implementation meets an expected property. The property to be checked is summa-
rized in the top section of this figure. As shown, the described property can be reformulated
as an equation that should always evaluate to true when considering the relationship between
property variables imposed by the design (i.e., the equation describing the design). In this
example, the relationship between variables in the good design does, in fact, lead to the prop-
erty equation always evaluating to true. The same cannot, however, be shown for the bad
design, leading to the conclusion that the bad design does not meet the property required in
this example.
Other factors that make property checking a powerful and naturally suitable technique
for functional verification include:
• Properties can be described at any stage of product specification and design creation.
This includes properties defined at the system design stage all the way down to prop-
erties specified for micro-architecture design of blocks.
• Properties can be collected incrementally as specification and development proceeds.
• Properties can be used with formal property checking tools in the beginning stages of
the design process when a verification environment is not available to provide test
vectors.
• Properties can be used with simulation-based and acceleration-based verification.

3, A Temporal Logic represents a system of rules and symbols for representing and reasoning about rela-
tionships between logical variables in terms of time. For example, a temporal logic expression can state the
~,xpected relationship between two A and B \'ariables in different clock cycles.
Verification Technologies 19

Desired Property: if (A == I) => in the next clock cycle (e == 0)


or equivalently: (At_I == I) => (e l == 0)
or equivalently: At_I +C,= I
(Meaning formula At_I + Ct should always evaluate to True)

Good Circuit Faulty Circuit


Bug (inversion moved)

A I
~c A I -TI-c
D Q

CLK CLK

Property to prove: At_I + C, = I Property to prove: A,_, + Ct = I


for this circuit: e, = A,. At_I for this circuit: et = At. At_I
Therefore: At_I + Ct = A t., + At + At_I =I Therefore: A'_I + C t = At_I + At + At_I = At_I + A,!= I
Therefore property is proved Therefore property check is failed

Figure 1.6 Formal Property (Assertion) Checking Example

• Properties indirectly define coverage collection metrics that will be needed to check
verification progress.
Multiple languages have been developed to facilitate effective and powerful property
descriptions. Property Specification Language (PSL) is one such standard language. System-
Verilog also provides its own syntax for defining properties in the form of assertions.

1.4.2 Simulation-Based Verification


The fundamental step in simulation-based verification is the process of evaluating the next
state values of design signals given its current state and input values, and to schedule future
value assignments to design signals while considering signal delays. In a simulation tool,
each evaluation of the next state is referred to as a delta cycle. Simulation consists of con-
tinuing delta-cycle evaluations at the next immediate time for which a signal assignment is
scheduled. Simulation is completed when there is no future value assignment scheduled (i.e.,
no more changes in design signals are expected). Simulation can also be stopped explicitly
through program or tool control mechanisms.
A verification environment consists of a testbench and a design. The fundamental oper-
ation in simulation-based verification is to use the testbench to apply input values to the
design, compute the next state values of the design, and check to see if the next state is
indeed the expected state for that design. A verification scenario is then the process of con-
secutively taking the design through different states where the sequence of observed design
states corresponds to a verification scenario listed in the verification plan.
20 Verification Tools and Methodologies

Figure 1.7 shows a representation of this process. The total state space of the design in
shown in the figure. A simulation run starts at an initial state where the next state is identified
by the current state and input values to the design. A simulation trace corresponds to the set
of design values observed as a given path in the state space is traversed. A traversed path in
the state space is identified by the verification scenario carried out by the simulator.

-t:> traversed path


..... possible path

Ilimwn visited state


D design state

Figure 1.7 Simulation-Based Verification Flow

SystemVerilog provides a comprehensive set of constructs for modeling both the design
and the verification environment. As such, both the design and the verification environment
can be implemented completely using SystemVerilog. It is, however, important to note that
in gcneral, the distinction between a testbench and a design is a logical distinction, and the
simulator is oblivious to the boundary between the code implementing the design and the
code implementing the testbench. The equal treatment of design and testbench programs by
the simulator can lead to race conditions between the design and the testbench. As such, Sys-
temVerilog provides explicit means of separating the testbench from the design by introduc-
ing a program block (section 5.3).

1.4.3 Acceleration and Emulation


Formal and simulation-based verification techniques provide many benefits in the early to
middle stages of the design flow. The speed of these techniques, however, falls far short of
the simulation speed that is needed towards the end of the flow, when the entire system,
along with its application software, must be verified. In addition to the larger design that
must be simulated, system level simulations also need to run for many thousands or millions
of instructions so that the macro behavior ofthe application software can be verified. Accel-
eration and emulation techniques are developed to satisfy these requirements.
The fundamental idea behind acceleration and emulation is to map the design into a
configurable platform (e.g., FPGAs) so that the digital portion of the design can be simulated
at close to final product clock rates.
Hardware acceleration refers to the idea of using a configurable platform to simulate
the DUV. In hardware acceleration, the testbench program is running on a host computer. In
hardlrare emulation. however. the stimulus to the design is provided by real world interfaces
and verification is in general. limited to monitoring the verification progress and debugging
through software and hardware observation mechanisms.
Verification Methodologies 21

In using a hardware acceleration platform, the maximum acceleration is limited by the


runtime of the testbench on the host computer and the speed of the communication channel
between the host computer and the acceleration platform. As such, it is important to consider
hardware acceleration requirements when architecting a verification environment. To
achieve this end:
• Make the time consuming part ofthe testbench (usually the drivers) synthesizable so
that they can be placed on the acceleration platform as well.
• Use transaction level models to communicate between parts of the verification envi-
ronment that are on the host and the acceleration platform in order to minimize com-
munication overhead.

1.5 Verification Methodologies


-=------- ------------
Verification activities permeate throughout the design process. As such, multiple technolo-
gies and multiple facilities are used by those involved in the verification activities. Different
methodologies are, therefore, required to bring together these tools and facilities and to pro-
duce a predictable outcome for the project. Assertion-based verification and coverage-driven
verification are two such methodologies. Assertion-based verification focuses on how asser-
tions can be used consistently across the design flow and across mUltiple tools. Cover-
age-driven verification is concerned with best approach for architecting and completing the
verification project. These methodologies overlap since assertions can be considered as cov-
erage points that are used as part of coverage analysis.

These methodologies are discussed in the following subsections.

1.5.1 Assertion-Based Verification


Assertion-based verification is the practice of using assertions as an integral part of func-
tional verification flow. The main components of this methodology are:
• Identifying properties to be asserted
• Deciding when these properties must be asserted
• The verification tools used to confirm asserted properties
Properties that must be verified consist of these main categories:
• Operating environment assumptions
• Verification related assumptions
• Design specifications
• Design and implementation decisions
Environment assumptions specify the conditions that must be present in the verification
environment in which this design must operate. Some obvious operating environment
assumptions are that, for example, the traffic at an Ethernet port of a design must adhere fully
to the Ethernet protocol, or that reset conditions must be present before the device can be put
in the correct operating mode.
22 Verification Tools and Methodologies

Verification related assumptions refer to properties that must be maintained as part of a


constrained test in which a device is operated in only one or two of its possible modes. As
such, a verification related property for the test might be that the device mode is limited to
modes specified for this testcase.
Design specification properties refer to how the design is expected to react to stimulus
from its environment.
Design and implementation decision properties relate to specific implementation deci-
sions that have been made in the design process which are not necessarily present in the
design specification but must be satisfied during device operation (e.g., only one bit of a
one-hot state machine should be active at any given cycle).
Not all properties must be satisfied at all times during a device operation. For example,
some device properties may fail during the reset sequence and, therefore, it is not necessary
to assert such properties during the reset sequence. The same applies for verification related
assumptions. Verification related assumptions hold for specific scenarios only, and as such,
must be enabled only during the relevant scenarios.

1.5.1.1 Assertion Evaluation and Tool Flow


Assertions can be evaluated using a variety of verification tools and technologies. These
include:
• Formal analysis
• Simulation
• Acceleration
• Emulation
A simulation-based approach has the advantage of being able to verify all assertions
that can be specified using SystemVerilog's assertion language. It does, however, have the
disadvantage of verifying an assertion only for simulation traces that occurred during a simu-
lation run. As such, complementary approaches such as coverage collection are needed to
confirm that an assertion is in fact verified for all relevant simulation traces. As an example,
consider the following assertion:
env_prop: assert property (@(posedge elk) a ##1 (a II b) 1=> e);
This property requires signal c to be active in the cycle after either trace (a al or trace (a
b). This assertion will pass even if only one of these two traces occurs during simulation. As
such, assuming this property to have been satisfied simply because it passed for one of the
possible traces leaves some scenarios unverified. A more important observation is that ifnei-
ther trace (a al nor trace (a b) occur, then this assertion will never have an opportunity to fail.
As such, it is necessary for simulation-based assertions to include a coverage collection
mechanism to confirm scenarios that such a property must verify did indeed occur.
Fomlal verification has the advantage that it provides universal pass or fail confirma-
tions for a given property. Formal verification does, however, have limited application since
it cannot be applied to large designs or very complex properties. and it also requires that
assumptions about that property are fully described (see section 1.4.1). A major advantage of
formal methods. howe\"er. is that it does not require any stimulus. As such, formal methods
Verification Methodologies 23

are ideal for early stages of the design where small blocks are being created and no ve 'fi
tion environment is yet available. Assertions defined at this stage are usually rela~~~c:­
design decisions. 0

In addition to simulation and formal methods, assertions can also be evaluated us in


acceleration and emulation systems. Vendors for such systems provide tools that allow suc~
assertions to be compiled and placed on the acceleration platform along with the design. As
part of the tool selection process for a project, it is important to evaluate assertion accelera.
tion capabilities and limitations of prospective tools so that assertions can be used seamlessly
with those tools.

Block Level Design


~ AA A AA f-c A: Assertion
C: Constraint
Formal Analysis (assumption)

Module Level Design


Formal Analysis
Simulation
Acceleration

System Level Design


Simulation
Acceleration
Emulation

Figure 1.8 Assertion Evaluation and Verification Tools

Figure 1.8 shows stages of the design process and the tools that are used at each stage to
verify assertions. During block level design, assertions are specified by designers to identify
properties that must be maintained because of design implementation decisions and assump-
tions made about the boundary conditions of blocks. At this stage of design, there is usually
very little stimulus generation capability available, and blocks are small. As such, this stage
is ideally suited for using formal verification methods. In the next phase, blocks are grouped
to create modules. Assertions defined by block designers are carried along with the block to
this stage and aid in making sure that expected boundary conditions for each block are satis-
fied when blocks are connected, and that properties related to design decisions are main-
tained. Any failure of block level assertions aids in debugging by quickly pointing to the
source of the unsatisfied property. Additional assertions are added to the design when mod-
ules are created. These assertions relate to the end-to-end properties of the module. Modules,
along with their assertions, are then combined to form a system. At this level, new
24 Verification Tools and Methodologies

end-to-end assertions are added and assertions inherited from lower design levels (i.e., block
and module) are leveraged to identify and quickly locate the source of any problems.

1.5.1.2 White-Box vs. Black-Box Assertions


Concepts of white-box and black-box verification also apply to assertion-based verification.
White-box assertions are placed on properties that depend on internal signals of a block.
White-box assertions aid in debugging by locating a potential problem in its source without
the need for this error to be propagated to the design boundary. Additionally, assertions
placed on internal signals help in making sure low-level implementation features of the
design are working as expected. Black-box assertions are placed on the boundary signals of a
block. Black-box assertions are usually specified to check for protocols at the block bound-
ary or to make sure assumptions made by the designer are satisfied at its boundary. Figure
1.9 shows a pictorial view of white-box and black-box assertions.

DUV
Block B1

o white-box assertion II black-box assertion

Figure l.9 White-Box vs. Black-Box Assertions

1.5.2 Coverage-Driven Verification


Coverage-driven verification is a simulation-based verification approach specifically devel-
oped to address the productivity and efficiency challenges faced in functional verification
projects (see section 1.1.3). The productivity and efficiency gains achieved by using a cover-
age-driven verification approach leads directly to improvements in verification completeness
and correctness since these gains allow verification engineers to spend more time completing
the project, and they verify scenarios at a faster pace.
The centerpiece of a coverage-driven verification approach is the random generation of
stimulus. This random generation is the main source of productivity gained by following this
methodology. Coverage collection becomes mandatory only when stimulus generation is
randomized. The reason is that in the absence of coverage collection, no infonnation is avail-
able about scenarios covered during random generation.

Coverage-driven verification methodology brings together the following \'eritication


concepts and approaches:
• Transaction-driven verification
• Constrained random stimulus generation
Verification Methodologies 25

• Automatic result checking


• Coverage collection
• Directed-test-based verification
Transaction-driven verification allows scenarios to be specified at a higher level of
abstraction. Constrained random generation leads to productivity gains in generating the sce-
narios, and automatic result checking provides confidence that the design works for all ran-
domly generated scenarios. Coverage collection is mandatory, since without coverage
collection it is not clear which scenarios have been randomly generated. A
directed-test-based approach is also necessary since ultimately, not all scenarios can be gen-
erated efficiently using random generation techniques. These topics are discussed in the fol-
lowing subsections.

1.5.2.1 Transaction-Driven Verification


During simulation, all traffic in the design exists in terms of low-level signals of bit and bit
vectors. Dealing with verification scenarios at this level of detail leads to major inefficien-
cies. In Transaction-driven verification, low-level signal activity is abstracted into atomic
operations so that scenarios can be described using these higher level atomic operations.
Drivers are used to translate these abstracted activities in the verification environment into
low-level signal activity that can be understood by the DUV (see figure 1.10).

----. ,-__D_r_iV_9_r--,
;' _Euv
0010101
USB

Figure 1.10 Transaction-Driven Verification

Transaction-driven verification follows these guidelines:


• Data and traffic are specified at a higher level of abstraction (e.g., frames, packets).
• Verification scenarios are described at a higher level of abstraction (e.g., write to
memory, execute instruction).
• Transaction drivers are used to translate these abstracted data and activity into
low-level operations and signal values that the design can understand. Within the ver-
ification context, transaction drivers are referred to as bus drivers, interface drivers,
or simply as drivers.
Transaction-driven verification improves verification productivity by alJowing verifica-
tion engineers to deal with more abstracted items. It also facilitates interaction with architec-
tural models of the design developed at the transaction level, and provides for efficient
communication between verification environment components residing on the emula-
tion/acceleration and host computer platforms.
26 Verification Tools and Methodologies

1.5.2.2 Constrained Random Generation


A verification scenario is composed of a sequence of transactions where each transaction
requires data and parameter values to achieve the desired effect. For example, in verifying
that an Ethernet packet is routed correctly through an Ethernet switch, transactions consist of
sending and receiving Ethernet packets, where each transaction is identified by its data and
parameters, which include the destination address and the payload content of the packet.
Random stimulus generation refers to using random generation techniques for generat-
ing both the data content of a transaction as well as sequences of transactions that form a
given verification scenario. In its most complete form, random generation is used to generate
both data and scenarios. Random data generation (e.g., randomizing the source and destina-
tion addresses of an Ethernet packet) is simple to implement since it requires only data val-
ues to be generated randomly and it usually does not affect the scenario that is being carried
out. Random scenario generation, however, requires that the verification environment sup-
port the scenarios that are being randomly generated in both checking and coverage collec-
tion. As such, random scenario generation not only requires more effort to be implemented,
but also requires that necessary infrastructure for supporting its verification be available.
Random generation improves verification productivity because a single simulation run
can verify multiple scenarios and data value combinations, therefore reducing the time it
takes for a verification engineer to build, execute, and verify each scenario.
Figure 1.11 compares verification progress between directed-test-based verification
and random generation techniques. Each verification step in this figure corresponds to the
time it takes to complete that step versus the contribution made by that step to overall verifi-
cation completion. In a directed-test-based approach, verification progress is made almost
linearly throughout the project since every new directed test contributes to the overall verifi-
cation progress. A random generation based approach, however, has an initial lead time,
since it takes longer to build a randomized environment.

________ --l
100%
Random Generation

Direct Test Verification

Time
Figure 1.11 Directed-Test-Based Verification vs. Random Generation
Verification Methodologies 27

An added advantage of a randomized environment is that scenarios that were not speci-
fied in the verification plan may also be generated because of the combination of random
activity across the verification environment. As such, a random generation environment not
only improves verification productivity, but also helps with verification completeness, since
given extra time or computing cycles, further running of a randomized environment leads to
more unspecified scenarios to be generated.
A randomized environment is not completely random, since generated data and parame-
ters must remain within the legal set of values. Also, random scenario generation requires
that each simulation nm eventually be guided towards scenarios that are not yet generated.
As such, constraint definition is an important part of random generation utilities.
Building a randomly generated environment is not a trivial task and requires significant
changes over traditional directed-test verification techniques. Random generation of veri fica-
tion scenarios requires each randomly generated scenario to be automatically verified. As
such, automatic checking must be an integral part of any randomly generated environment.
This means that a randomly generated environment requires a reference model so that the
result of each randomly generated scenario can be predicted at simulation runtime. In addi-
tion, a randomized verification environment should be able to handle all types of behaviors
that may be generated by the random generation of scenarios. These behaviors include spe-
cial handling of error conditions in addition to normal operation of the design. Random gen-
eration of scenarios also means that verification progress must be measured automatically
through coverage collection mechanisms. This is because the exact scenario being generated
is not known before a simulation run is started. And different simulation runs ofthe same test
may create different scenarios and data combinations. In summary, a verification environ-
ment that contains random stimulus generation must also be a self checking environment and
must also include coverage collection.
Random stimulus generation is ideally suited to verifying a finite state machine. As an
example, random generation, automatic checking, coverage collection, and coverage analy-
sis for an FSM can be summarized as follows:
• Random generation:
• Initially, put the state machine in a random legal state.
• Generate random inputs.
• Automatic checking
• Check that the next state produced by simulation is the same as that specified.
• Check that the output produced by simulation is the same as that specified.
• Coverage collection
• Collect all states reached.
• Collect state transitions.
• Collect inputs applied in each state.
• Analysis
• Check that all valid states have been reached.
• Check that all state transitions have been made.
It is tempting to try to build a single verification environment that randomly generates
all possible verification scenarios. For a number of reasons. however. this is not a practical
goal for real designs. These reasons include:
28 Verification Tools and Methodologies

• A design usually has multiple modes and use models that require different verifica-
tion environment topology and configuration.
• Some comer cases have an extremely low probability of occurring under normal
design operations, so a special verification environment is required for generating
such scenarios.
• Running a single environment and a single test for a long time leads to many dupli-
cated scenarios.
Even the best designed verification environment architecture and generator is limited in
the types of scenarios that it can generate because the effort required for adding the missing
scenarios to the same environment is prohibitively expensive. Additionally, given the num-
ber of parameters and states in a large design, it is practically impossible to reach all comer
cases using a single randomized test and a single environment. In such cases, it is best to cre-
ate a specially constrained version of the environment and/or the randomized testcase
focused on the missing scenarios so that these scenarios occur with high likelihood.
Because of these limitations in covering all scenarios using a single test, a randomized
verification environment will usually have a number of randomized testcases that cover a
large part of the verification plan. The next set of randomized testcases will cover a smaller
set of scenarios, and ultimately, some testcases will be direct testcases that focus on only one
or two speCific scenarios.

1.5.2.3 Automatic Result Checking


As mentioned in the previous section, automatic result checking is a requirement for a ran-
domized verification environment.
Automatic result checking is performed using monitors and scoreboarding techniques.
Monitors are mostly used for protocol checking and collecting design traffic while score-
boarding techniques are used for end-to-end behaviors or data transfer checks.

1.5.2.4 Coverage Collection


Random stimulus generation has the potential to provide significant improvement in verifi-
cation productivity and efficiency. However, without a clear strategy to measure verification
progress, these gains may be difficult to realize. Accurate measurement of verification prog-
ress requires that the following analysis be made for each randomized simulation run:
• What scenarios included in the verification plan were generated in a simulation run
• What interesting scenarios not included in the verification plan were generated
• The contribution of each run to the overall verification progress
• Constraints modifications that are necessary for generating the missing scenarios
Coverage information collected during each randomized simulation nll1 is used to
decide the contribution of each simulation run to the m'erall verification progress. Additional
modifications in the simulation environment or new testcases are required when running fur-
ther cycles of existing testcases does not make any further contribution to the overall prog-
ress (see section 18.4).
Verification Methodologies 29

Coverage results for each simulation run can also be used to rank testcases for the pur-
pose of regression suite creation or ordering. Testcases with high contribution to overall
progress take priority in the regression environment.

1.5.2.5 Directed Tests


Not all verification scenarios can be created using a single randomized environment. As
such, multiple environment and/or testcase variations are usually created, with each new
variation focused on creating scenarios not covered previously. Ultimately, the last remain-
ing scenarios must be covered using directed testcases where either a very specialized envi-
ronment or a fully constrained testcase is created.
Adding a directed test, specifically one that requires a customized environment, causes
difficulties in managing the verification project. Such testcases require manual interaction to
create and verify, therefore, degrading overall productivity. In addition, such special tes-
tcases are difficult to use, to maintain, and to reuse across projects. As such, a verification
environment should be architected to minimize the number of such specially designed
directed testcases.

1.5.2.6 Coverage-Driven Verification Project Life Cycle


The life cycle of a coverage-driven verification project (see figure 1.12) consists of the fol-
lowing stages:
• Developing the verification plan
• Building the verification environment
• Bringing up the verification environment
• Running randomized testcases
• Creating directed tests for corner-case scenarios
In the first stage, the verification plan is developed according to the design specification
and appropriate feedback from system engineers, design engineers, and verification engi-
neers. The verification plan will be extended and completed throughout the verification life
cycle. Therefore, the main goal at this stage should be to have enough detail in the plan so
that a stable verification environment architecture can be created.
In the second stage, the verification environment is designed and implemented accord-
ing to the requirements identified from the verification plan. In stage three, tightly con-
strained verification scenarios are carried out in order to check for correct operation of the
verification environment and to debug and fix any existing problems. The goal in this stage
is to gain confidence in the correct operation of the verification environment, and verifica-
tion progress at this stage is of secondary concern. Multiple passes through stages two and
three may be needed before the environment is stable enough to be used in the next stage.
The goal in stage four is to cover as much of the verification plan as possible with each
simulation run. Coverage results are used to decide if consecutive runs of a testcase contrib-
ute to the overall verification progress. A new testcase. and potentially a \"ariation of the
environment is then created to target \"erification scenarios that remain to be generated. This
30 Verification Tools and Methodologies

stage is completed when the remaining scenarios must be considered individually and
through highly customized environments or testcases.
In stage five, the remaining verification scenarios are generated through directed tes-
tcases. As mentioned previously, the goal should be to minimize the number of scenarios
requiring a customized test.

Building Running
the Random Directed test '
: Bringup: Testcases , for corner cases:
Environment , ,
100% .
~--=~~..!.-~----~----
,

Time
Figure 1.12 Coverage-Driven Verification Methodology Stages

1.5.3 Metric-Driven Verification


Metric-driven verification (or specification-driven verification) refers to a coverage-driven
verification approach where the verification plan is in an executable format. This means that
the verification plan can be used directly to drive verification scenarios and the verification
environment can back-annotate the verification plan in order to measure verification prog-
ress and identify verification closure.
A metric-driven verification approach can further improve verification quality and pro-
ductivity by:
• Removing manual interventions that are inherently slow and error-prone
• Improving regression management environments through automatic sorting of simu-
lation run contribution to overall coverage and ordering regression priorities
CHAPTER 2 Verification Planning

The tenns testplan and verification plan have historically been used interchangeably to refer
to a list of scenarios that must be verified in order to complete a verification project. But
given the increasing complexity of designs, and the increasing number of abstractions,
actors, and technologies involved, the tenn verification plan is increasingly used to describe
the planning that goes into completing a verification project.
This chapter describes the motivation and the need for creating holistic verification
plans that act as the centerpiece and driving point of a verification project. This chapter also
describes the flow for creating such a verification plan. This flow takes into consideration
factors such as who must be involved in the creation of this plan, what content should be
included, what format should be used, how this content is used to guide the design of the ver-
ification and coverage plan, and finally, how it should be used to iteratively improve the ver-
ification plan and arrive at verification closure.

2.1 The Modern Verification Plan


-- - __ ._-_ .... _.
.. •.. _._ .... _._._-_...._ - -
Historically, verification was an organically grown activity where a bottom-up approach was
used to verify pieces of hardware until enough confidence was gained in correct operation of
the whole system. The popularity of coverage-driven verification methodologies, facilitated
by hardware verification languages, has motivated a shift to up-front planning of the verifi-
cation requirement, albeit mostly for the portion that lies within the scope of individual or
groups of verification engineers using the same verification technology (Le., formal, simula-
tion-based, acceleration). Holistic planning of a verification project, however, requires that
this up-front planning be extended to all verification aspects of the design flow. The general
flow of a design life cycle (see figure 1.1) consists of different levels of abstraction, different
verification technologies, and different types of engineers who collectively produce the final
product. As such, up-front verification planning should be extended to include all aspects of
a design flow.
32 Verification Planning

The verification planning methodology described in this chapter promotes a more elab-
orate up-front planning process that requires involvement by all decision makers in the
design and verification flow, and one whose result is intended to drive and manage verifica-
tion activity across the whole project life cycle. In this context, verification project manage-
ment refers to guiding a verification project through all of its distinct phases, including
planning, implementation of verification infrastructure, measuring verification progress in a
quantitative manner, and directions for how to react to these measured metrics. Additionally,
verification management includes guidance on resource allocation, anticipating project slips
and reporting progress in reference to given mi lestones.
In addition to acting as a project management baseline, the comprehensive nature ofthis
upfront planning process leads to further benefits in improving other aspects of product
development flow. These benefits include:
• Alignment of product functionality interpretations by the various actors
• Development of a common description of the desired functionality
• Capturing andlor definition of project milestones
• Definition of the engineering view of the plan, assigning resources to features
• Common understanding of project tasks, goals, and complexity by all actors
• Refinement of existing specifications (concept and design) due to identification of
holes, ambiguities, or misinterpretations
• Definition of verification approaches to tackle all, or a subset, of product features
Effective verification planning requires careful consideration of all factors that impact
this planning process or are affected by it. These factors include:
• Work of multiple specialists has to be tracked and merged
• Thousands of tests have to be managed and processed
• Multiple verification technologies have to be managed in the flow
• Planning changes, being costly in both time and resources, have to be minimized
• Multiple teams at multiple sites have to be coordinated
• Status must be collected and reported from across the design flow
• The inherent inefficiencies and unpredictability in project resources (tools and engi-
neers) must be managed
A verification plan is the natural place to centralize verification related procedures and
management related activities. In this sense, the verification plan should be developed with
the end in mind and as a document that is able to guide the verification project throughout its
lifetime. Verification planning challenges, goals, content, and life cycle are discussed in the
following subsections.

2.1.1 Verification Planning Challenges


The first step in developing a powerful verification plan is to understand the factors involved
in taking a design through the design flow. The main challenge in effective verification plan-
ning is to create a flow that addresses all of these factors. These factors include:
• Multiple actors are involved
• Multiple verification technologies are used
• Multiple DUVs must be verified
The Modern Verification Plan 33

• Multiple documents must be created, tracked, and updated, including:


• Descriptions of concept
• Requirements
• Verification scenarios
• Implementation
• Multiple dependencies on other teams must be managed
MUltiple actors influence the final content and form of product specification, functional
specification, and design implementation. These actors include concept, design, verification,
and software engineers. In fact, verification closure is defined as the convergence of these
three views of the product (figure 1.3). A verification plan must reflect the collective opinion
of all these actors as to what must be verified and how. Capturing this collective opinion in
one centralized place also helps in gaining verification closure by highlighting any differ-
ences that may exist between these actors. The interaction between these actors is explained
in section 2.2.3.
A holistic verification plan should also consider the different verification technologies
that are used throughout the design and verification flow. These include:
• System level simulation
• Formal verification methods
• Simulation-based verification
• Acceleration and emulation-based verification
The benefits of each verification technology must be leveraged to further improve veri-
fication quality (section 1.4).
Even though the tinal system will consist of one DUV, mUltiple intermediate DUVs
must be created in order to manage project complexity and to also allow for a hierarchical
design flow. In addition, each DUV must be verified with and without the target software
(i.e., either drivers or the application software). As such, the verification plan must consider
the existence of multiple DUVs, how each DUV is verified, and the contribution of veri fica-
tion results for each DUV to the overall verification progress.
A verification project depends on other teams for input and feedback. In addition, the
results produced throughout a verification project are usually used to support other teams in
their activities. Teams that a verification project interacts with include:
• Marketing team for feature requirements
• Software team for early validation, and tapeout
• IP providers
• Updates throughout the project from customers
A verification plan must consider interaction between multiple teams and how the
results produced or needed by other teams affect the overall schedule and ordering of priori-
ties.
Verification planning goals are decided to best address the challenges described in this
section. These goals are described in the next section.
34 Verification Planning

2.1.2 Verification Planning Goals


The design of a verification plan should both guide the technical aspects of the verification
activity (e.g., what scenario with what verification technology) as well as provide guidance
for project management. The following goals must be achieved when creating a verification
plan:
• Maximize quality and quantity of completed verification
• Mitigate project risks
• Forecast and track verification resource requirements
• Transparency of verification progress
• Early warning of slippages and issues
The first goal of maximizing quality and quantity of completed verification is the stan-
dard target for all verification plans. The remaining targets are meant to facilitate project
management aspects of the verification plan where project progress can be measured and
slippages can be countered by judicious use of resources and by shifting priorities. Methods
for achieving these targets are described while describing the flow for building a verification
plan (section 2.2).

2.1.3 Verification Plan Contents


To achieve the targets of a verification project, a verification plan should include the follow-
ing content:
• Features, including:
• Product features
• Implementation-specific features
• Use-cases and system level scenarios that the product must support correctly
• Verification technology used to verify features
• Milestones (what features/scenarios by what time?)
Product features refer to features that are defined in the functional specification of the
product. These constitute the low-level features of the device (i.e., data traffic format, regis-
ter bank structure, effect of read/write to these registers, etc.). Implementation-specific fea-
tures are decided by designers and reflect designer's knowledge about verifying performance
related features of the design and also low-level behaviors that expose the root cause of sys-
tem level failures.

Use-cases and system level scenarios refer to how software programmers and system
users view the design. Under ideal conditions where 100% of device features are verified,
this type of use-case analysis and verification is not necessary, but knowing that 100% of
device features cannot even be enumerated, let alone verified, it becomes necessary to verify
these larger scale use-cases.

Different verification technologies are used to verify different features enumerated in


the verification plan. For example, low-level design implementation features are verified in
the block design phase using formal methods, while a use-case scenario requiring many tens
of thousands of simulation cycles may need to be verified using acceleration or emulation
technologies.
Building the Verification Plan 35

Milestones should also be included in the verification plan so that a timeline is given for
verifying different features. This description of milestones will be needed in identifying proj-
ect slippages and how resources can be shifted to remedy the situation.

2.1.4 Verification Project Cycle: Gaining Closure


The ultimate goal of a verification project is to achieve complete verification of all design
features in as short a time as possible and within the applicable resource limitations. Given
the competing nature of these requirements, it is clear that a verification plan must strike a
balance between these goals. The situation is made even more difficult as parameters of this
problem may change throughout the flow. For example, features may be redefined or
changed, project deadlines may be shortened, or resources' availability may change. As with
all practical solutions to optimization problems, reaching this balance will require an itera-
tive process to reach a viable solution.
Figure 2.1 shows the steps involved in creating and executing a verification plan. These
steps are:
1. Build Plan
2. Build Environment
3. Execute
4. Measure
5. React to Measurements
Verification completion is checked within each iteration of this cycle and verification
closure is decided when the achieved verification progress reaches the expected guidelines.
These topics are discussed in the following sections.

l
~__R_e,a~c_t__~
Build Plan
Refine Plan
~--------~

Measure Execute

Figure 2.1 Verification Execution Cycle

2.~ Building the Verification ~!9.~_ ......._. . . _


Careful planning and execution places a verification project on very strong footing. Given
that the verification plan is used as the central repository for all \'eritication plans. activities,
36 Verification Planning

updates, and schedules, obtaining this strong footing is not possible without the creation of a
solid verification plan.

Figure 2.2 Verification Plan Development Cycle

Figure 2.2 shows the flow for creating a verification plan and the coverage plan derived
from that verification. The following steps must be completed when building a verification
plan:
• Identify all actors concerned with project execution
• Prepare for the planning sessions and planning document
• Brainstorm the product functionality
• Structure the verification plan, keeping in mind the necessary views and reuse
• Capture features and attributes
• Formulate the verification environments and coverage implementations
These steps are described in the following subsections.

2.2.1 Identify Actors


A holistic verification plan should take into account all decision makers at all levels of
authority (from overall product features to micro-architecture decisions) and at a11 stages of
the design and verification process. In this regard, everybody's opinion is important, since
verification plan thoroughness will depend on the collective knowledge of team members on
every aspect of the design.
The list of all actors in a project includes:
• Project managers
• Design architects
• Software/Firmware engineers
Building the Verification Plan 37

• Verification engineers
• Design engineers

Project managers will ultimately use the verification plan for tracking and resource allo-
cation. As such, their involvement in specifying project deadlines and milestones is needed.
Architects must confirm that product intent is indeed realized in the functional specification
and design implementation (e.g., latency, bandwidth targets are met, FIFO size, arbitration
algorithms implemented as specified). Software and firmware engineers must comment on
their view of the hardware and the support they need to verify the software even before the
final hardware is ready. Verification engineers are ultimately responsible for the quality of
sign-off and as such, are owners of the verification plan. Design engineers must comment on
implementation specific verification requirements. Table 2.1 shows a summary of verifica-
tion planning participants, input each is expected to provide, and benefits each will gain.

Actor Provides Expects


Project Manager/ VerificatIon plan for tracking status and
Schedules and priorities
Marketing aiding management
Architectural requirements such as Ensure design intent is realized in the
Design Architects
latency and bandwidth implementation
Concept Engineers Conceptual Scenarios, implementation Ensure scenarios and use· cases are cap-
System Engineers guidelines tured in verification effort
Ensure implementation is in line with origi-
Design Intent (assumptions about spec-
Design Engineers nal intent and verification plan sections that
ified features, design details
focus on design details
Ensure software scenarios are captured in
Sotlware Engineers Sotlware view of the system
the plan
Agreement from all actors on the veri fica-
Verification approaches (intent), expe-
Veri fication Engineers tion plan. Common status reporting lan-
rience in how to verify features
guage across all actors

Table 2.1: Project Actors and Participation in Verification Planning

Even though the completed verification plan will require feedback from all project
actors, initial meetings should be limited to those who can define the structure of the verifi-
cation plan and top level features that must be verified. This initial limit is necessary, since
reaching consensus becomes more difficult as the number of participants grows. As such, it
is better to create the first version of the verification plan with only the most relevant stake-
holders present, and then follow up in other meetings where everyone can comment and
extend the content of the verification plan. One example would be to first make a verification
plan that contains only the product intent, and then complement this plan by bringing in
hardware and software engineers, and project managers.

2.2.2 Preparing to Start the Brainstorming Session


Before a brainstorming session can be held, the verification plan owner must prepare for the
meeting. The following issues must be decided before starting a brainstorming session:
• Verification plan format
• Overall verification plan structure
• Verification plan naming conventions
38 Verification Planning

Verification plan format and structure and naming conventions ultimately will be
decided by the actual contents of the verification plan. It is, however, useful for the verifica-
tion plan owner to finalize on a general format and prepare templates that can be used as the
starting point.

2.2.3 Brainstorm about the Content


Discussions on functional verification can easily tum into engaging conversations about how
the product could have been, or things that could still be done, or changes that could be made
to make the design better. It is important to walk into such sessions focused only on what is
decided and to proceed to gain consensus over these finalized topics. The primary objective
of a brainstorming session is to identify all features that must be verified from all stakehold-
ers' perspectives.
The following guidelines help in improving the efficiency of an initial brainstorming
session:
• There are always more features than can be discussed in the allotted time for such a
session. Therefore, maximize the benefit derived from time spent.
• All stakeholders are born equal and their comments are relevant to the final quality of
the product.
• Do not discard any feature discussed at this stage.
• Do not discuss how to verify a feature, rather what features to verify.
• Identify product features.
• Identify system level scenarios and use-cases.
• Discuss what views should be included in the plan.
• Discuss what metrics should be collected during verification.
Once all features of interest are identified in the brainstorming sessions, they can be priori-
tized and redundant or unfeasible scenarios removed. The focus at this stage again is to iden-
tify all features and not how they will be verified. These decisions will be made at a later
time when the structure of the verification environment is decided.

2.2.4 Verification Plan Structure


Verification plan structure is identified by the following features:
• Verification plan outline
• Planning views
• Verification scenarios
• Verification build and run infrastructure
These topics are described in the following subsections.

2.2.4.1 Verification Plan Outline


Figure 2.3 shows a typical outline of a verification plan. In this outline, the verification plan
is divided into two main categories for functional requirements versus design requirements,
and interface versus core features. Functional requirements include features that are derived
Building the Verification Plan 39

from the functional specification of the design. Design requirements include features that
relate to how the design is implemented.

1. Introduction
2. Functional Requirements
2.1 Functional Interfaces
2.2 Core Features
3. Design Requirements
3.1 Design Interfaces
3.2 Design Cores
4. Verification Views
5. Verification Environment Design

Figure 2.3 Verification Plan Outline

Verification environment design relates to what stimulus and checking is required for
carrying out the features listed in this verification plan.

2.2.4.2 Verification Views


A verification plan should be useful to all actors in the verification project. Clearly, not all
actors view the plan in the same light. Design engineers are more interested in viewing sce-
narios listed for their blocks, a system level engineer is more interested in end-to-end scenar-
ios, a verification manager is more interested in a view of what part of the verification
project has been completed, and a CAD tool manager is more concerned about when the
tools should be available so that the verification technology listed for each scenario (e.g., for-
mal, emulation) is available when needed.

Verification Plan

f1
f1.1
f2
f2.1
F.!.2
f3
f4
f4.1

Isub A.1 I Isub A.211 sub A.31 Isub B.1 II sub B.21
I module A I I module B I

Figure 2.4 Verification Plan Layout

As such, it is important to decide up front what views of the verification plan will be
needed by different actors. This information will be useful in that it highlights any missing
information that should be included in the verification plan.
40 Verification Planning

Figure 2.4 shows a verification plan view that correlates each feature with modules in
the design. The organization in this figure shows the verification plan organized by features,
where each feature contains sub-features. It is possible to organize the verification plan so
that features are listed by the module that implements their functionality. A feature based
view is recommended since it allows features to be verified without the main focus being on
what module contains the logic for these features.

2.2.4.3 Verification Scenario Details


In general, a verification plan lists scenarios that must be verified about a design. More
details about each scenario should be included in the verification plan in order to make it use-
ful for all actors involved in the project. In general, the following information should be
included in a verification plan:
• Scenarios to be verified
• Design feature verified by each scenario
• Design blocks/clusters that are activated by each scenario
• Verification mode that is used for verifying scenarios (e.g., formal or simulation)
At an abstract level, designs are better described by summarizing their features than list-
ing scenarios that must be verified. In addition, verification priorities are more naturally
decided for design features than design scenarios. As such, organizing a verification plan by
the features provides a better view into the overall progress of verification.
In addition, in a large design, not all design blocks and/or clusters are available at the
same time. Clearly, for an end-to-end feature, most blocks in the design will be needed while
more local features are based on a few modules or blocks. As such, a verification plan should
include information about how to schedule the verification of scenarios listed in the plan. By
listing the modules needed for verifying each scenario, it becomes easier to synchronize the
verification activity with the design activity as new blocks become available.
Verification scenarios ultimately are verified using different verification technologies.
A block level feature is usually verified using formal techniques, while a system level
end-to-end feature is best verified in an emulation environment. As such, listing the verifica-
tion technology used for verifying a scenario makes it easier for that scenario to be scheduled
and assigned to engineers.

2.2.4.4 Verification Build and Run Infrastructure


A verification environment consists of many types of design, data, configuration, and script
files in different languages that must be brought together to start even the most simple of
simulations. Files in a verification environment include design files for the DUV and the ver-
ification environment, configuration files describing DUV initial conditions and verification
environment settings such as random seed values. data tiles such as initial contents of mem-
ory blocks. and program files such as assembly programs to be loaded into processor instruc-
tion memory and c/c++ programs describing golden models.

The running of the simulation is usually automated by using scripts that perform all of
the necessary tasks required for starting the simulation. Given the complexity of verification
Building the Verification Plan 41

environments consisting of different tools and languages, there are usually a number of dif-
ferent such scripts, each with different configuration switches. A verification plan should
clearly describe methods available for building and running the verification environment.
Consequently, each scenario should clearly indicate the build and run mechanism used for
that scenario.
The inclusion of build and run procedures in the verification plan provides two benefits.
First, it helps guide new engineers joining the project on how to get started with the verifica-
tion activity. And second, it helps as a central location for documenting the build and run
infrastructure for the verification environment.

2.2.5 Capturing Features


Two types offeatures must be identified in the verification plan:
• Product features
• System use-cases
The question to answer during this stage is "What is the product expected to do?" Engi-
neers naturally talk about verification in terms of how it is done and not what the target fea-
ture is. For example, a statement such as "Verify that the MAC state machine goes into state
5 if the packet is less than 48 bytes" should be turned into "Short packets must be dropped by
the interface." Concept engineers also tend to speak about the features at a very high level.
These statements should also be turned into statements more relevant to architecture of the
design. For example, a statement such as "The hard drive must be hot-swappable" should be
turned into "The SATA interface should initialize during system runtime within the tolerance
defined for the SATA interface initialization process."
Verifying system use-case scenarios would not be needed under ideal conditions where
a design is fully verified. The reality, however, is that not all scenarios can even be enumer-
ated for a reasonably sized design. As such, verifying system level use-cases allows for con-
fidence to be gained in the overall operation of the design.

2.2.6 Capturing Attributes


Features of a design can be divided into two views:
• Port-based view
• Function-based view
In port-based view, operation of the design is described from its interface perspective.
As such, the design is viewed as a black-box for the purpose of features that relate to its
interface behavior. The features listed for this view are extracted from the protocol that is
implemented by design interfaces. For example, for a USB module, the port-based feature
listing will include behavior of USB traffic as described in its documentation. The following
attributes must be listed for features described in this view:
• Attributes that are defined by pin values at a given time
• Attributes defined by data structures that pass through design pins across multiple
cycles
42 Verification Planning

• The interaction between such attributes

Attributes defined by design pin values consist of design attributes that are controlled
by design pin values. Examples include command value for a bus interaction, the reset pin
value, and interrupt pins being active or inactive. Attributes that are defined by data struc-
tures passing through design pins include design attributes that are extracted from such data
structures. For example, the destination address of an Ethernet packet arriving on a serial pin
of an Ethernet module and the type of memory access requested in a PCI-express transfer.

In function-based view, design features are extracted from internal functionality of


design blocks, design specifications, and design decisions. Attributes relating to func-
tion-based view are extracted from internal state of the design for each of the features identi-
fied in this view. For example, states of a state machine, and register values controlling
design operation.

2.3 From Plan to Environment


The verification plan is used to derive the stimulus needed to carry out the verification steps
for each feature. In addition, the plan is used to identify the checks that must be made for the
stimulus applied.

2.3.1 Identifying Required Stimulus


A carefully designed and described verification plan points directly at the stimulus needed to
verify scenarios in the verification plan. Scenarios in the verification plan identify stimulus
sequences that must be generated, while attributes for these scenarios identify the valid range
of values for the generated stimulus sequences.

Stimulus generation affects two aspects of the verification environment:


• Drivers
• Scenario generators (i.e., sequencers)

Drivers used for verification purposes have two specific purposes: first they must sup-
port interactions defined by the given interface, and second, they must be able to inject errors
that the design is expected to detect or recover from. As such, the scenarios described in the
verification plan should clearly indicate what error conditions should be injected at the
design interface which should necessarily be supported by the driver. Additionally, not all
modes of a design interface may be needed in a project. As such, the implementation of a
driver can be simplified by excluding such unused modes of operation.

Scenario generators are developed to directly support the generation of scenarios listed
in the verification plan. As such, rhe examination of the verification plan should immediately
lead to the types of sequences that must be generated in the verification environment.
Measuring Progress 43

2.3.2 Identifying Required Checkers and Collectors


Scenarios in a verification plan should list the checks that must be made to verify each sce-
nario. These checks can be divided into protocol checks and data checks. Protocol checks are
used to confirm adherence of design signals to specific timing requirements of the specitica-
tion (e.g., correct timing of bus handshaking signals). Data checks, however, verify correct
transfer or modification of data values by the design (e.g., correct transfer of Ethernet packet
payload from one switch port to another). Protocol checks are made using monitors, while
data checks are made using collectors and scoreboards.
When deciding where to perform a check identified by the verification plan, an effort
should be made to make this check as close to its source as possible, or in multiple places if
the problem can occur due to mUltiple failures. This approach improves design debugability
where bugs are identified as close to their source as possible.

2.4 Measuring Progress


_ _ _ _ _ _ _ _ _ ., __ ••• _ __ ___ " _,.. • ____ 0" _ _ • • • _ . ___ ,. _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ •••• _ _ _ _ • __• _, _ _ _ _ _ _ _ _ _ ~ _ _ _ • _ _ _ _ _ _ _ _ _ _ _ _ _ _ • _ _ _ _ _ • _ _ _ ~ _ _ _ _ • _ _

Progress of a verification project is defined as the portion of features that are already covered
compared to featured that must be covered for the current target milestone. Metrics to collect
for this purpose are:
• Code coverage
• Assertion coverage (for formal tools)
• Assertion coverage (for simulation/acceleration/emulation tools)
• Functional coverage
Code coverage is collected by the simulation tool. Assertion coverage is collected from
the results of formal verification used most often by designers during module and block
design. Assertion coverage is also collected during simulation and acceleration/emula-
tion-based verification, and is automatically collected once the assertions are built into the
environment. Functional coverage is collected during simulation-based verification and the
necessary infrastructure must be developed in the verification environment to collect this
information.

2.5 Reacting to Results


The results collected during the execution of a verification plan must be used to bring the
project closer to completion and decide on whether or not closure has been reached. Steps to
take based on execution results include:
• Milestone charting
• Simulation failure analysis
• Coverage hole analysis
• Regression ranking
44 Verification Planning

Milestone charting is used to measure milestone definitions (features that must be com-
pleted to reach specific milestones) against actual timelines. This analysis is used to adjust
resources and anticipate slippages, based on the amount of progress being made.
Simulation failure analysis is used to debug, identify, and fix problems that have led to a
failed scenario. The failure may be in either the verification environment or the DUV. As
such, this analysis leads both to maturity of the verification environment as well as design
stability and correctness.
Coverage hole analysis provides information about what scenarios have not yet been
exercised. This information is used in milestone charting to understand how the verification
environment must be extended in order to fill the coverage holes that must be met by the
given milestones.
Regression ranking is used to identify verification runs in the regression suite that con-
tribute the most to the coverage metrics. This ranking allows a few regression runs to be
selected that lead to most confidence about the design after any modifications. It also allows
designers and verification engineers to choose simulation runs that best target features about
which they are concerned in their current work.
CHAPTER 3 Verification
Environment
Architecture

The architecture of a verification environment should allow all scenarios in the verification
plan to be verified according to the guidelines of the target verification methodology. In gen-
eral, this target can be achieved through different verification environment architectures.
However, experience shows that as these seemingly different architectures mature into their
final form, common structures and features start to take shape. Advance knowledge of these
best-in-class structures and features is essential for building a verification environment that
can handle today's complex designs.
This chapter describes an overview of a verification environment architecture that facil-
itates the application of coverage-driven and assertion-based verification methodologies. The
discussion in this chapter is focused on the architectural blocks of the verification environ-
ment, and the general use model and features that should be supported by each block. This
discussion is independent of any implementation tool or language and describes each block
at an abstract level.

3.1 Abstract View of a Verification Environment


-"----------"--------
--- --"------- - - " - - - - - - - - -~-----

A verification environment connects to a DUV through that DUV's boundary signals. These
boundary signals can be grouped into interfaces, with each port representing interrelated sig-
nals that collectively describe an interface protocol supported by the DUV. This conceptual
grouping of signals into interfaces can be applied to any size design, from a simple block to
complex systems, with ports representing complex protocols such as a parallel bus (e.g.,
PCI) or a serial interface (e.g., PCI-Express), or even trivial behaviors such as a single wire
used for resetting the device. The discussion in this chapter views a DUV as a block with a
number of abstract interfaces.
This interface-based "ie\\' of a DUV suggests a layered architecture for its verification
em"ironment. In this architecture, shown in figure 3.1, the lowest layer components interact
directly with DUV interfaces, while each higher layer component deals with increasingly
higher Je\els of \erification abstraction. These higher levels of abstraction correspond to
46 Verification Environment Architecture

more complex verification scenarios composed of more complex atomic objects. This lay-
ered architecture leads to an intuitive path for extending a verification environment, as the
design flow moves from block to system level. In this flow, additional verification compo-
nent layers are added to the verification environment as design integration moves to the next

-
step.
Verification Environment DUV

Software
Stack

Hardware

Figure 3.1 Complete Architecture of a Verification Environment

Structurally, this verification environment is composed of interface and module/system


verification components. fnterface verification components provide an abstraction for the
physical ports with which they interact. This feature allows higher layer verification compo-
nents to interact with the DUV at the abstraction level provided by the interface verification
component. Interface verification components also contain additional features to monitor and
collect coverage information for the physical port they interact with. Software verification
components are a specialized type of interface verification components that interact with the
software stack of a system level DUV (section 3.3).
Module/system verification components contain module/system level scenario genera-
tion functionality and carry out end-to-end data checking. It should be noted that the internal
architecture of module and system verification components are similar since they both inter-
act with higher and lower layer verification components. As such, the distinction between
module and system verification components is intended to reflect the level of abstraction
each component deals with. Interface verification components have a different architecture
since they interact directly with DUV ports.
Verilication components can operate in two modes:
• Active mode
Interface Verification Components 47

• Passive mode
An active verification component generates traffic for lower layer verification compo-
nents, or at DUV ports in the case of interface verification components. A passive verifica-
tion component monitors only verification environment traffic and as such, does not contain
any stimulus generation capability. Reuse of a verification component when moving to the
next design integration step depends on correct implementation of these modes. As such,
careful attention should be paid to features that should be supported in each mode. These fea-
tures are discussed in the following sections.
Interface, software, and system verification components are described in the following
sections.

3.2 Interface Verification Components


An interface verification component is used to interact with one or more DUV ports that sup-
port the same protocol. Given that in general, carrying out verification scenarios requires
simultaneous interaction with multiple ports, the architecture of an interface verification
component is geared less towards generating full verification scenarios and more towards
providing an abstracted view of DUV ports to higher layer verification components and also
monitoring DUV port traffic through protocol checking and coverage collection.

Interface Verification Component DUV

t
o
c..
rno
'61
o Sequencer
....J

Figure 3.2 Interface Verification Component Architecture

The architecture of an interface verification component is sho\vn in figure 3.2. An inter-


face verification component can interact with mUltiple DUV ports that follow the same pro-
tocol. Each interaction of an interface verification component with a DUV port is carried out
using an agent. The implementation of an interface verification environment must support
48 Verification Environment Architecture

the creation, connection to DUV port, and management of one or more agents. Each agent
contains the following blocks:
• Driver
• Sequencer
• Agent and bus monitors
The driver block interacts with DUV ports to create the abstraction provided by the ver-
itication component. This block also provides additional verification related features which
are usually not defined as part of the protocol supported by the DUV port (e.g., injecting
errors). The agent monitor block provides the information that is required by protocol
checker blocks to verify that DUV port properties are satisfied during simulation runtime.
The information detected by a monitor block is also used by sequencer blocks when generat-
ing reactive sequences. The properties checked by this block are protocol related properties
that do not require global view and can be checked by observing only local port signals. Cov-
erage collector records information about the types of activities that are observed at DUV
ports. The bus monitor tracks activities that are common across all agents placed inside an
interface verification component. As an example, in a bus type connection where all agents
connect to the same DUV bus, most of the monitoring is done in the bus monitor, while in a
point-to-point type connection where each agent is connected to a separate DUV interface,
most of the monitoring is done in the agent monitor. The sequencer block is used to generate
multi-transaction behaviors at the DUV port where each transaction is defined at the level of
abstraction provided by the driver.
As an example, consider the interface verification component for an Ethernet port. The
driver block would provide atomic operations for transmitting an Ethernet packet to a given
destination address and receiving packets. The monitor block checks that each packet
received at DUV ports is a valid Ethernet packet. For scoreboarding purposes, the monitor
also collects packets received and transmitted at this port. The coverage collector records
information on what size data payloads, how many packets, and to what source and destina-
tion addresses were sent and received. The sequencer block is used to send a group of Ether-
net packets that collectively correspond to a TCP/IP data transfer.
The logical view of a DUV port, as presented by an interface verification component to
higher layer verification components, consists of a set of software-oriented constructs. These
constructs allow higher layer verification components to access status collected by the moni-
tor, and to interact with the driver and sequencer in the interface verification component.
This logical view is formed by combining the views provided by the monitor, driver, and
sequencer blocks.

Details of the logical view and blocks in an interface verification component are dis-
cussed in the following subsections.

3.2.1 Logical View


The logical view of an interface verification component prO\ides the following:
• Sequencer interaction and customization
• Driver interaction and customization
• !\10nitor status
Interface Verification Components 49

The sequencer in an interface verification component provides a set of default and com-
monly used sequences. These sequences are used when the interface verification component
is used to generate random traffic with only a local view of the DUV port it is attached to. An
interface verification component should provide a view of its predefined sequences, how
these sequences can be customized, and how new sequences can be added to the sequencer.
During module and system level verification, the sequencer in the interface verification com-
ponent interacts with the higher layer verification components to drive the intended traffic
into the DUV port. This interaction may be driven from higher layers or initiated by the local
sequencer. The logical view of a sequencer should provide a view of how this interaction can
take place and be customized for any specific application.
The driver in an interface verification environment is used mostly by the sequencer in
that interface verification component. In some cases, however, it may be necessary to drive
or configure this driver from outside the verification component. Additionally, when creating
new sequences for the sequencer in the interface verification component, detailed knowledge
of driver control and the configuration mechanism is required. An interface verification com-
ponent should, therefore, provide a detailed view of the mechanisms used for interacting
with its driver.
Monitors in higher layer verification components rely on information provided by the
monitors in lower layer verification components. As such, developing monitors in the higher
layer verification components requires detailed knowledge of the status information made
available by lower layer monitors. An interface verification component should, therefore,
provide a detailed view of conditions detected by its monitor during the simulation process
and how this information can be accessed. This view is also necessary when adding
sequences to the sequencer in an interface verification component.

3.2.2 Driver
A design communicates with the outside environment through its ports. The exact form of
this communication is decided by the protocol implemented by that port. A protocol usually
provides a set of high level atomic operations (e.g., send a packet, send a datagram) that in
reality are carried out through many cycles of complex handshaking procedures across multi-
ple design pins. An important observation is that the detailed steps of how each protocol
operation is carried out is not really relevant in any part of the verification environment
except that port. As such, it is possible to localize all low-level protocol related activities to
the block that interacts with that port. A driver implements the functionality that hides this
detail and provides an abstract view of the protocol at the logical port of the interface verifi-
cation component.
A driver used for verification purposes is more complex than a driver that just supports
the operations defined in a protocol. The reason is that a verification driver should not only
be able to produce and understand correct protocol behavior, but it should also be able to pro-
duce invalid traffic that the design is expected to detect. and also identify invalid behavior
that the design might produce at its port. The exact fonn and extent of invalid conditions that
should be detected and generated by a verification driver is detined by the verification plan.
Such extra requirements. however. can usually be deriwd by analyzing what protocol errors
can occur (e.g., CRC errors).
50 Verification Environment Architecture

A driver block should provide the following:


o A logical view of the physical port to higher layer verification components
o Ability to produce and understand all valid physical traffic at the DUV ports
o Ability to generate protocol errors that the DUV is expected to handle
o Ability to recover gracefully from incorrect DUV behavior
o A configuration interface
The logical view provided by the driver, should be complete to the degree that it is no
longer necessary to interact with the physical interface. Additionally, a verification driver
should be able to recover gracefully from errors on the physical interface, report the detected
failure in its status reporting mechanisms, and synchronize with the next valid behavior. The
types of error conditions injected by the driver should be the kind that relate to low-level sig-
nal activity of the protocol and not the types of errors that can be injected through interaction
with the logical port of the driver. For example, in sending an Ethernet packet, the driver
need not have a feature to insert a wrong tag in an Ethernet packet, since this can be set when
specifying a packet content through its logical port. The driver should, however, include fea-
tures for injecting a wrong preamble and set an invalid CRC before sending a packet.
A driver is identified by its feature set and user interface. These topics are covered in
the following subsections.

3.2.2.1 Feature Set


The features supported by a driver should be derived from the verification plan for the inter-
face protocol such that all scenarios can be implemented through the driver and without
interacting directly with the physical port.
The following guidelines should be followed when deciding driver features:
• Detine a set of atomic operations for the driver that can be combined in different
ways to create any desired behavior at that physical port.
o In the logical port of the driver, include physical port timing information that may be

useful to control or know about (e.g., idle cycles before an Ethernet packet).
• Define driver operations for injecting errors and driver status fields for recording
observed error conditions.
o Choose a parameterized operation over multiple operations that essentially create the

same behavior with minor modifications.


The set of atomic operations defined for a port is usually in direct correspondence with
the logical view of the protocol implemented at that port. For example, if a protocol is
defined in terms of different types of packets, then the atomic operations for the driver will
include sending packets of different types where the details of packet transmission is handled
by the driver.
The physical implementation ofa protocol often includes detailed timing and handshak-
ing mechanisms. For example, an abstract operation of writing a block of data to memory is
carried out through many cycles of finely orchestrated signal transitions. In general, this
detailed timing information is not relevant at the higher le\'e!s of abstraction. Sometimes.
however. it may be useful to include such information in the abstract definition of the logical
port of the driver. For example. in sending an Ethernet packet. the abstract operation is
Interface Verification Components 51

defined by the contents of the packet that must be sent or the contents of the packet that was
received. A useful addition to this interface is to also specify the number of cycles to wait
before the packet is sent. In the receive direction, it is useful to include in the received packet
the number of idle cycles that were detected before the preamble of the received packet was
detected.
The logical port of a driver should provide the ability to inject errors at the physical port
and provide information about erroneous conditions caused by the DUV and detected by the
driver. As an example, the Ethernet protocol requires that packet preamble to be at least 56
bits of alternating zeros and ones. In defining the operation to send a packet, it is useful to
define the length of the preamble as a parameter to the logical port so that this size can be
randomized to values smaller, the same, or larger than 56 bits. In the receive direction, a col-
lected Ethernet packet should also include a field that stores the number of preamble bits that
were detected before the collected packet was started. The inclusion of such features depends
on a careful analysis of what can go wrong at the physical port that is not considered in the
logical description of the protocol.
The features provided by the driver should be rich and comprehensive so that direct
interaction with DUV physical port is not required. This set of features should, however, be
well organized and grouped. As such, it is a good practice to define parameters at the logical
port in cases where groupings of different features as a parameter makes sense.

3.2.2.2 User Interface


The user interface for a driver block should allow other blocks to interact procedurally with
the driver. The procedural interface should include the following types of interactions:
• Transaction processing methods
• Configuration methods
Transaction processing is used for passing a transaction to the driver to be processed.
These methods are used in cases where a transaction is intended to be transmitted to the
DUV. This is in contrast with configuration steps that control driver behavior and do not
immediately result in activity at the DUV port.
Configuration methods for a driver are identified by the set of parameters that must be
set before the driver can interact with the physical port or accept transactions from its logical
port. For example, in a UART driver, the number of data bits and stop bits should be speci-
fied before the driver can receive a frame from the physical interface. The configuration
methods also control settings for transaction parameters that rarely change across multiple
transactions. For example, in a system bus driver, the burst size for writing to the bus may be
fixed across many bus transactions. Such parameters should also be included in the configu-
ration space. Configuration methods provides a mechanism for assigning and querying the
settings of these configuration space parameters.
Note that methods for accessing the internal status of a driver are not listed as a require-
ment and in fact, should not be pro,-ided. The reason is that when an interface verification
component is put in passive mode, the dri\"er block is removed. As such, no other block
should depend on status detected by the dri\"er block. Instead, all such status should be
52 Verification Environment Architecture

detected independently by the monitor block, which is always present in passive or active
modes. An example ofa user interface is shown in figure 3.3.

Logical View
User interface:
write_blkJmm(addr, data, cycles_wait_before_send)

.. ..
Physical
write_blk_posted(addr, data, cycles_wait_before_send) "- Port
C1)
read_block(addr)
I,;onllguratlon Interrace:
.f:
0
set_burst_words(size)
get_bursCwordsO

Figure 3.3 Driver Logical View

3.2.3 Sequencer
A sequencer is an intelligent block that generates random transaction sequences. Such
sequences should be created dynamically during simulation runtime and according to ran-
domizable parameters so that scenarios can be generated randomly during simulation run-
time. The detailed architecture of the sequencer and its use in generating scenarios is
described in detail in chapter 14.
The sequencer in an interface verification component generates a sequence of transac-
tions that collectively form a scenario described for the protocol supported by the design
port. The important observation about this sequencer is that, conceptually, it is not aware of
conditions at other DUV ports. Given that a meaningful scenario is usually defined in terms
of traffic through multiple DUV ports, this means that the types of scenarios generated by the
sequencer inside an interface verification block is limited in extent to scenarios that require
only local visibility. The task of creating scenarios that require interaction with multiple
DUV ports is assigned to module/system verification components that interact with multiple
interface verification components. The types of traffic generated by the sequencer in an inter-
face verification block is, therefore, limited to the following:
• Atomic operations defined at the abstraction level of the DUV port protocol
• A composite operation at the DUV port
• Responses to requests received from the design that do not require global view
Activity at a design port is usually abstracted into atomic operations that reflect the log-
ical view of the protocol implemented by that interface. For example, for a memory inter-
face, atomic operations include memory read/write operations and at an Ethernet port,
atomic operations consist of Ethernet packet receive and transmit operations. Composite
operations at a port refer to a sequence of atomic operations that collectively form an atomic
operation at a higher level of abstraction. For example, individual memory write operations
can be combined to write an arbitrary size block of data to memory, or multiple Ethernet
packets can be sent in order to transfer a large block of data. The composite operations gen-
erated by the sequencer in an interface \"erification component usually correspond to how
that interface is used at the system le\"el and. therefore, the composite operations correspond
to atomic operations at the system level.
Interface Verification Components 53

The sequencer in an interface verification component can also provide responses to


requests received from the design. These requests are at the abstraction level of DUV port
protocol (e.g., memory read request from DUV with the interface verification component
pretending to be the memory subsystem). The types of responses generated by the sequencer
are limited to replies that do not require knowledge of events or conditions at other DUV
ports.
Note that the limitations imposed on the sequencer in an interface verification compo-
nent, as described in this section, are a consequence of separating interface verification com-
ponents from module/system verification components. This separation requires that interface
verification components interact solely with the DUV port, and the features provided by this
interface verification component should be used by module/system verification components
to generate system-wide scenarios requiring interaction with multiple DUV ports. From an
implementation point of view, system level scenarios can be generated by the sequencer
inside an interface verification component if the effort is made to make system-wide infor-
mation available inside this block, but this approach would go against the logical separation
made between an interface verification component and a module/system verification compo-
nent.

3.2.4 Agent and Bus Monitors


Agent and bus monitor blocks in an interface verification component are used to collect the
information necessary by other blocks in the environment. These blocks include sequencers,
requiring port traffic information when generating reactive sequences, and protocol checkers
requiring port activity information for verifying protocol compliance at the port.
Monitors are passive components. This means that a monitor does not drive any port
signals and only monitors them. As such, a monitor block is present in both active and pas-
sive modes of a verification component. This means that data and status collected by moni-
tors in an active verification component can be used for data checking even when that
verification component is reused in passive mode in the next design integration step. This
approach is shown in figure 3.4. This example shows a DUV that interacts with two drivers
on its two physical ports. The drivers both drive and collect traffic from the interface so they
can pass the collected traffic to higher layer verification components. Data checking across
this DUV is, however, implemented by using traffic collected by the monitors. Given this
architecture, it is possible to migrate the same data checking implementation to the next
design integration steps, where DRIVER1 and DRIVER2 are replaced by actual design blocks.
The availability of block level data checking at higher levels of design integration leads to
more visibility into any design problems that may come up at this block boundary. Note that
the actual implementation of a scoreboard will be a part of the module/system verification
component that interacts with both interface verification components. In this case, the mod-
ule/system verification component implements the scoreboard by waiting on monitor events
provided at the logical ports of these interface verification components to grab the collected
packet and use it in the scoreboarding check.
Monitors check for t\\O types of signal attributes:
• Syntax checks
• Timing checks
54 Verification Environment Architecture

r-Lr4 Scoreboard
h r-
Monitor1 Monitor2
!-Er- r- ~

Driver1 1.0
I""
..
~
DUV
-- Driver2

Figure 3.4 Data Checking Using Monitors

Syntax checks verify that all signals at DUV port have valid values. Syntax checks are
easy to implement and define since they do not depend on timing information (e.g., burst size
should always be a valid burst size value). Identifying and implementing timing checks is
more involved, since such properties are defined across multiple signals and multiple time
cycles.
Similar to the sequencer, the monitor in an interface verification component tracks only
activities that can be extracted from the DUV port it monitors. As such, the monitor in an
interface verification component tracks protocol compliance at the port and collects atomic
operations at the abstraction level of the design port (e.g., memory write transaction for a
memory interface).
The coverage collector in an interface verification component mirrors closely the condi-
tions that the monitor tracks and the atomic transactions that the monitor extracts from the
DUV port. In general, coverage should be collected on any check that the monitor makes,
since such checks must have been a part of the verification plan or may be needed to be
implemented in the monitor. In addition, the coverage collector should track the types of
transactions collected by the monitor from the DUV port and all attributes of such transac-
tions. For example, for an Ethernet interface, the coverage collector should track Ethernet
packets collected from the interface, and for each packet it should track the source address,
destination address, packet type, data size, and whether the packet was corrupt or not.
Coverage should be collected for traffic initiated by both the DUV and the interface ver-
ification component. Traffic initiated by the DUV is used for identifying what design traffic
was checked by the monitor, and traffic initiated by the interface verification component is
used to identify what scenarios or sub-scenarios were generated at that interface.

3.3 Software Verification


---
Components ._----.--- - ----

Software verification components are necessary when yerifying system level designs that
include the software stack. Figure 3.5 shows an example of such a system. In this yiew. a
software verification component provides an abstraction of the software interface to the \"eri-
Module/System Verification Components 55

fication environment so that the module/system verification components can treat the soft-
ware port in the same manner as that of a physical port.

Figure 3.5 System Level Verification and Software Verification Components

A software verification component is an abstraction level that is traditionally provided


through hardware-software co-verification tools, such as Seamless from Mentor Graphics
and Incisive Software Extension (ISX) from Cadence Design Systems. Simulation tool ven-
dors provide custom solutions that can be used with SystemVeri log.
The exact method by which a software verification component interacts with a DUV is
beyond the scope of this discussion. What is important is that once this interface is in place, a
module/system verification component can use the software verification component to con-
figure, drive, and collect traffic through the software port in real time during simulation run-
time while interacting with the physical ports, thereby enabling it to carry out system level
verification scenarios through the software and physical ports.

3.4 Module/System Verification Components


Figure 3.1 shows a three layer verification environment composed of interface, module, and
system verification components. In practice, however, a verification environment may con-
sist of more layers. each corresponding to a new design integration step (e.g .. module, clus-
ter, chip, system, etc.). Each design integration step may also require the addition of more
than one layer of verification components. thereby adding to the overall number of layers.
The number of layers added in each integration step depends on the complexity of the newly
integrated DUV. Other than interface verification components. all other components have a
56 Verification Environment Architecture

similar architecture in that they interact with verification components at higher and lower
layers to create the desired scenarios and track DUV behavior. These components differ only
in that they operate on different size DUVs. In this section, the term "system verification
component" is used generically to refer to all such verification components.
The focus of a system verification component is generally on end-to-end behavior ofthe
DUV and not on individual blocks composing that DUV. This approach is based on the
implicit assumption that in a layered verification environment, smaller blocks have already
been verified to some degree and the focus of verification is on the current DUV (e.g., when
verifying a module, blocks are assumed to be verified). This assumption holds throughout
the design life cycle, as modules are created by combining blocks, and later when systems
are created by combining modules. This approach allows verification environment complex-
ity to be managed more efficiently where each new layer can deal with only the new features
added in the latest design integration step. In spite of this end-to-end focus of a system verifi-
cation component, this approach also provides further verification of features in smaller
blocks, since system-wide scenarios will ultimately result in activation of individual blocks
and modules.

Module/System Verification Component

Interface Interface
Verification DUV f-oIIIf-~ Verification
Component Component

Figure 3.6 Module/System Verification Component Architecture

Behaviors that a system verification component is focused on include:


• Bugs in modules that could be verified only as part of the overall system
• Wrong assumptions by designer about module operation
• Wrong wiring between system modules
• Problems with module interactions arising from protocol mismatches
The architecture of a system verification component is shO\\"11 in figure 3.6. This com-
ponent contains multiple agents where each agent prO\'ides the same functionality while
interacting with a different set of lower layer \'erification components. Each agent in a sys-
tem verification component contains the following blocks:
Module/System Verification Components 57

• Sequencer
• Verification environment (VE) monitor and coverage collector
• DUV monitor and coverage collector
The VE monitor interacts with monitors in lower layer verification components (e.g.,
system monitors track monitors in interface and module verification components) to provide
information about the current state of DUV as observed by lower layer verification compo-
nents. The DUV monitor tracks DUV internal signals since these signals cannot be tracked
through monitors attached to DUV ports. The combination of VE monitor and DUV monitor
allows a gray-box verification approach (see section 1.1.2.1) to be leveraged in this architec-
ture. Information provided by these monitors is further used by the sequencer to create
end-to-end scenarios.
These blocks are discussed in the following subsections.

3.4.1 Sequencer
Generally, the sequencer in a system verification component is responsible for the following
operations:
• Initializing DUV and verification environment
• Configuring DUV and verification environment
• DUV end-to-end scenario generation
These activities are discussed in the following subsections.

3.4.1.1 Initialization
Initialization refers to setting DUV and verification environment conditions that must be set
before simulation is started. Initialization settings include the following:
• DUV pins that must be set before a design can be activated
• DUV register settings that cannot be changed during device operation
• Memory pre-loading
• Verification component settings that must be made before simulation is started
The motivation for placing initialization steps in the sequencer is that such initialization
can be changed as part of scenarios and even randomized if necessary. If design and environ-
ment initialization is not done in the sequencer, then it becomes a part of the environment.
This means that running a scenario requiring a different set of initialization settings will
require not only a change in the scenario but the environment as well.
As mentioned previously, a sequencer generates scenarios through dynamic generation
of sequences during simulation runtime. This means that a scenario may be created only after
design initialization is completed at time zero and, therefore, not all initialization actions can
be performed within the sequencer. The goal. hmvever. should be to do as much of the initial-
ization in the sequencer to a\·oid having to change the environment for scenarios requiring
different initialization settings.
58 Verification Environment Architecture

3.4.1.2 Configuration
Verification environment configuration refers to settings in DUV and verification environ-
ment that can be changed during simulation runtime or as specified by design specification.
As such, resetting a design can be considered a configuration setting, since a device can be
reset at any time during the simulation runtime. In fact, resetting a design in mid-operation is
a required scenario that must be included in any verification plan.
The important consideration in building the configuration infrastructure is that often,
the state of the verification environment at the time of such reconfiguration becomes invalid
since normal device operation is interrupted. For example, upon reset of a design, all score-
board contents, monitor observed condition flags, etc. will have to be initialized to match the
design state right after reset.
Configuration may be needed as part o{verification scenario execution (e.g., resetting
the device, or changing device register settings), or used to simulate multiple scenarios
requiring different configurations during the same simulation run. In either case, special care
should be taken to update the state of the environment if a configuration step affects the state
of DUV such that such changes are not automatically detected by the environment.

3.4.1.3 Scenario Generation and Driving


The sequencer in a system verification component is concerned with creating end-to-end sce-
narios. At this level of abstraction, the sequencer deals with atomic operations defined at the
abstraction level of the design ports provided by interface verification components (e.g.,
packets, transactions, etc.). As such, the sequencer should generally describe its scenarios by
using the facilities provided by the sequencer in interface verification components. This
means that system level scenarios are created by appropriately timed and constrained execu-
tion of smaller sequences provided by the sequencer in interface verification components.
The sequencer uses status information provided by its monitors to synchronize the sequence
of activities that it produces.
It is important to note that a sequencer does not participate directly in data checking and
scoreboarding. This activity is delegated to the monitor that collects traffic flowing through
the environment. It is at times tempting to use the generator to inject expected values into a
scoreboard, since data to be injected is readily available. Doing so, however, limits the reus-
ability of the scoreboarding implementation when the DUV in this verification step is used as
a block in a larger system. Given that the sequencer will be removed in that environment, any
scoreboarding that depends on this sequencer will also become unusable. On the other hand,
since monitors stay in place as the DUV is placed inside a larger system, any scoreboarding
based solely on monitor collection can be reused (section 3.5).

3.4.2 VE Monitor
The VE monitor for a system verification component serves these purposes:
• Providing a pointer to lower layer monitors (e.g., interface monitors)
• Verifying DUV end-to-end behavior
• Scoreboarding
Module/System Verification Components 59

Blocks in a system verification component must have access to events, status, and
objects collected by monitors in alllower layer verification components. This information is
necessary for generating scenarios that depend on careful timing of activities generated and
tracked across the verification environment. As such, the VE monitor in a system verification
component should act as a central place for providing a link to monitors in lower layer verifi-
cation components.
End-to-end behaviors add semantics beyond those defined at DUV ports. These seman-
tics relate to behavior defined for the current level of design integration. Such behaviors can-
not be checked at the interface level or at levels of abstraction below the one for the current
DUV. As such, VE monitor for a system verification component should include checks for
all such new end-to-end behaviors.
The VE monitor in a system verification component is also the ideal place for score-
boarding, since this monitor has access to all lower layer verification components where data
objects are collected from the environment. Including the scoreboard in the monitor also
allows for easy reuse of the scoreboard when the DUV verified at this stage is integrated into
a larger system. More details on scoreboarding is provided in section 3.4.4.

3.4.3 DUV Monitor and Assertion Checker


Any new design integration step produces new end-to-end behaviors. These new behaviors
attach new meanings to signal values and conditions in internal DUV blocks. In addition, a
new DUV usually contains new internal blocks that have not been verified as part of any pre-
vious verification steps. The DUV monitor provides the following facilities to account for
these considerations:
• Information about internal state of DUV
• Protocol checks across signals spanning multiple internal blocks
• Central placeholder for internal block assertions
The sequencer in a system verification component may require information about the
internal state of the DUV in order to successfully generate a comer-case condition. All such
information should be provided in the DUV monitor. This approach centralizes all interac-
tion between the verification environment and DUV internal signals in DUV monitors allow-
ing for easier design change management.
Many bugs viewed on the boundary of a design originate deep within the DUV and
many cycles before such problem is observed at DUV ports. As such, it is necessary to pro-
vide early visibility into internal DUV problems that can be identified through checks made
across internal blocks interfaces. This approach improves verification debugging through the
visibility provided by this white-box verification approach. These types of check should be
added by system integrator.
Internal blocks should come with assertions that were defined and used by block
designer in the formal verification flow. The DUV monitor can be a central placeholder for
all such assertion packages that are not included in DUV monitor of any lower layer verifica-
tion component.
60 Verification Environment Architecture

3.4.4 Scoreboarding
Scoreboarding is used to check for the following potential problems as data objects are pro-
duced, consumed, and moved across a design:
• Data values being different than expected
• A packet being received when one is not expected
• A packet not received when one is expected
The monitors in interface verification components verify that all signal timings at DUV
ports match the specification. As such, data checking does not need to consider signal timing
and is done at the transaction level where· all signal and transfer timing information has
already been removed from the collected data object or transaction. For example, when
scoreboarding an Ethernet packet injected and expected to be received at a switch port, the
exact signal timings of the packet traveling through design interfaces is assumed to be cor-
rect and only packet content as abstracted by the Ethernet format is scoreboarded.
Scoreboarding requires a reference model so that the expected outcome of an activity
can be predicted. The general architecture of scoreboarding is shown in figure 3.7. In this
view, the monitor on the input side collects a data object moving into the DUV. It then com-
putes the expected output of the DUV based on that input and inserts the expected result in
the scoreboard. The output monitor then checks every data object collected at the output
against those already in the scoreboard.

Scoreboard

DUV
0000
Data Objects Going In •
Data Objects Coming Out

Figure 3.7 Scoreboarding Architecture

A scoreboard is essentially composed of a list of expected data objects where every data
object collected at the DUV output is compared against the next expected data object. The
matching of an actual object against an expected data object depends on a number of design
dependent factors. A generic scoreboard implementation should, however, provide the fol-
lowing functionality:
• Support ordered checks where order of expected and actual data objects should be the
same
• Support unordered checks where the collected data object may arrive OLlt of order
• Allow for initial mismatches to be ignored since many designs may take some cycles
to synchronize
• Perform end-of-simulation checks to make sure the scoreboard is empty and all
expected data objects have been matched against an actual collected object
Verification Component Reuse 61

3.4.5 Coverage Collector


Coverage information is collected as part of monitor implementations. Coverage collection
in a system verification component focuses on the following types of information:
• Basic traffic for each of the interfaces
• Combined effect of traffic at all interfaces
• Internal design states
• Sequences generated
• Delay and throughput information (performance information)
• Configuration modes
• Resets and restarts
• Errors observed and errors injected
Each system verification component should collect coverage information for the
abstraction level of its sequencer, monitors, and scoreboard. This information includes sce-
narios that are verified by its VE monitor and scoreboard and also internal design conditions
tracked by its DUV monitor. Once a verification component is made passive and reused in
the next design integration step, it will continue to collect the same coverage information
since its monitors and scoreboarding utilities are still operating even in passive mode. Using
this approach, coverage information in the final verification environment is collected in a
distributed fashion across all active and passive verification components.

3.5 Ve~!i!cation Component ~~!:,se ___________________ _


The architecture of a module/system verification component, as described in this chapter,
facilitates direct reuse of a verification component in the next design integration step. The
only change required before this reuse is to change from active to passive all verification
components that are no longer needed for stimulus generation. Making an interface verifica-
tion component passive means taking out the driver and the sequencer. A module/system
verification component does not contain a driver, and as such, making a module/system veri-
fication component passive only that its sequencer be disabled.
The composition of a module level verification environment is shown in figure 3.8. This
figure shows a module DUV1 that is connected to two interface verification components IVC 1
and IVC2 , and to module verification component MVC through these interface verification
components. All verification components in this structure are active and contain sequencers,
with interface verification components IVC1 and IVC2 also containing drivers.
The reuse of module level verification components at the system level is shown in fig-
ure 3.9. This figure shows module DUV2 which is constructed by combining module DUV1 and
blocks Block1 and Block 2 • The verification environment for DUV 2 is very similar in structure to
the one shown for DUV1 in figure 3.8, and consists of active interface verification components
IVC 3 and IVC 4 and system verification component svc. In addition. this system level verifica-
tion environment include the entire verification em'ironment for DUV1' with the following
modificatjons:
62 Verification Environment Architecture

Figure 3.8 Module Level Verification Environment Integration

• The sequencer in verification component MVC is removed


• The sequencer and driver in verification components IVC 1 and IVC2 are removed
Given the monitoring, scoreboarding, and coverage collection strategies described in
this chapter, all of these activities transfer directly from the verification environment for
DUV 1 to the verification environment for DUV2. While end-to-end behavior is being checked
by scoreboards in the verification environment for DUV2, end-to-end checks are being per-
formed for DUV1 as well. In addition, coverage is also collected by the monitors in all passive
verification components allowing for distributed collection of coverage as design integration
moves to the next step.
The reuse of verification environment for DUV1 in making the verification environment
for DUV2 can be repeated recursively, as design moves to the higher levels of integration.

Figure 3.9 System Level Verification Environment Integration


63

PART 2
All aboLlt SystemVeriiog
64
CHAPTER 4 System Verilog as a
Programming
Language

SystemVeriiog features and constructs have been defined because of the specific require-
ments of the latest design and verification methodologies. As such, learning SystemVerilog
syntax and programming without intimate knowledge of the methodology behind its incep-
tion would be as unrewarding as flying a supersonic jet with the piloting skills of a glider
pilot. Familiarity with the verification methodology and architecture described in part one of
this book is a first step in understanding the motivation behind the introduction of new Sys-
temVeri log constructs. With this methodology in mind, features of SystemVerilog discussed
henceforth will present themselves not as mundane details of a programming language, but
as facilitating constructs for the implementation of a modem verification environment.
This chapter discusses SystemVerilog syntax, flow, and structure. In doing so, this chap-
ter provides an overview on how to write simple programs in SystemVerilog. Chapter 5
describes SystemVeriiog features that are specially introduced or used to better facilitate
functional verification.

4.1 The Generalized Model of aPigita!..Syst~!!l _________._... __


Digital systems are created by essentially interconnecting individual blocks, each repre-
sented by a core functionality and input/output ports. This model of a digital system persists
at all levels of abstraction where at the highest level, blocks represent complex system level
functionality and at the lowest level, blocks represent transistors. Given that each block in
such a representation can be modeled as a single or the interaction of multiple threads of exe-
cution (i.e., processes) that act on the internal state of the block (i.e., data values), then it can
be seen that any digital system can be modeled by defining the following:
• Internal state of the block
• Threads of execution and their programs operating on the internal state of the block
(i.e., processes)
• Interconnection between these threads of execution
• Synchronization between these threads of execution
66 SystemVerilog as a Programming Language

A block's internal state is represented by data values that the block holds; its functional-
ity is represented by threads of execution that manipulate these internal data values; its
dependence on other blocks is represented by its interconnection infonnation; and its order
of execution, with respect to other blocks needed for deciding the order of execution for its
threads, is represented by its synchronization infonnation.
Hardware description languages are essentially created to implement this generalized
view of a digital system. For example, in Verilog, threads of exception include always and
initial blocks, data interconnection (i.e., dependence) between processes are either explicitly
defined (i.e., using sensitivity lists) or implicitly derived by deciding what variables from
outside a process are used in its description, and synchronization information is either
derived implicitly (by propagating value changes as events) or explicitly by using wait state-
ments based on events or absolute time values.

Thread 1

Illi~1
......~ ==== ~
Prog ram acting on Data

Thread2
Interconnection

I
-=== II I DatalI
Data21

I I ~ Program acting on Data


Program acting on Data

Figure 4.1 Generalized Model of a Digital System

The power of a modeling language to represent complex systems using this generalized
model and the productivity it affords is directly proportional to the following elements:
• Ability to represent complex and user defined data types
• Ability to represent interconnections at different levels of abstraction
• Expressiveness of the procedural language used to model the program executed in
each thread and the interaction between threads
• Ease of building a hierarchical model

Even though Verilog is a good language for modeling a digital system consisting of
wires. registers, and modules that are connected through ports, it quickly runs out of steam
when more abstract models must be built. Specifically, Verilog lacks the ability to specify
abstract data types. interconnection between modules represented as anything but bits and
bytes. and higher level software programming techniques that facilitate better modeling of
each thread of execution. and the synchronization between concurrent threads.
Structure of a SystemVerilog Program 67

The usefulness of a modeling language in building a complex verification environment


depends not only on the above factors, but verification constructs provided in the language to
facilitate verification activities such as:
• Constrained random stimulus generation
• Coverage collection
• Property checking
Verilog also lacks the support for carrying out these verification related activities.
SystemVeriiog has been defined in order to address these limitations ofVeriiog. To that
end, SystemVerilog provides the following broad enhancements beyond what Verilog offers:
• The ability to model systems at a higher level of abstraction by providing complex
and user defined data types and interfaces
• The ability to use software programming techniques such as recursive function calls
and pointer-based programming, leading to better programming expressiveness
• An object-oriented programming paradigm through introduction of classes and its
related semantics
• Verification related utilities such as constrained random generation, coverage collec-
tion, and property specification and checking
In the remainder of this chapter, a general view of the SystemVerilog language is pre-
sented. SystemVerilog's verification related features are discussed in the chapter 5.

Even though SystemVerilog adds many new features to Veri log, the structure of a System-
Verilog program remains essentially the same as that of a Verilog program. A SystemVerilog
program hierarchy consists of three fundamental entities:
• Module blocks
• Program blocks
• Procedural blocks (i.e., processes)
• Data objects
Module Blocks are used to model structural hierarchy of the design. In module-based
verification environments, module blocks are also used to model the hierarchy of the verifi-
cation environment (section 12.1). Module blocks can contain module blocks, procedural
blocks, and data objects. Program blocks are used to define a clear boundary between the
verification environment and the DUV (section 5.3). Program blocks can contain only type
and variable declarations and one or more initial blocks. Procedural blocks can contain pro-
cedural blocks and data objects. Data Objects can contain only data objectsl.
The following program shows examples of these relationships:

I. Classes are special types of objects that combine related data values and procedural blocks. Classes will
be discussed in section 4.7.
68 SystemVerilog as a Programming Language

·program 4.1: SystemVerilog program structure


1 typedef struct {int ii; int jj;} twovaU;
2
3 module leaf (input int aa, output int bb);
4 int cc=5;
5 assign bb = aa * cc;
6 endmodule
7
8 module mytop;
9 twovaUtv;
10
11 function int decrement (input int a);
12 int decval;
13 decval = 2;
14 decrement = a - decval;
15 endfunction
16
17 leaf leaU (.aa(tv.ii),.bb(tv.jj));
18
19 initial begin
20 int yy;
21 yy= 10;
22 tv.ii = 100;
23 #1 $display(tv.jj, decrement(yy»;
24 $finish;
25 end
26 endmodule

The new user defined type twovaU (line 1) is an example of a data obj ect containing (or
being composed of) other data objects. Modu Ie my top includes data objects (e.g., tv of type
twovaU), modules (e.g., leaU, an instance of module leaf), and procedural code in the form
of an initial block. Figure 4.2 shows a pictorial view of block relationships in this program
sample.

I 0 Data Object o Procedural Block 6 Module Block

6 mytop (top level module)

~
I\ I l ~f'.,
I \m".p'y('~fmod"'el
\neS19-25)

Dii Ojj Dyy o+ decrementO D cc


(lines 11-1)
Oline5

Ddecval

Figure 4.2 Hierarchy of a SystemVerilog Program


Data Types and Objects In SyslemVeriiog 69

4.2.1 Hierarchies of Different Types


Note that in the generalized view shown in figure 4.2, three types of hierarchies can be iden-
tified:
• Module hierarchy
• Procedural (i.e., function call) hierarchy
• Data object hierarchy
It is very important to have a clear understanding of, and the differences among, these
types of hierarchies when programming in SystemVerilog, since these hierarchies correspond
to ideas in different domains, consisting of hardware design (module hierarchies reflecting
design architecture) and software design (procedural and data object hierarchies, and eventu-
ally class-based hierarchies reflecting software organization).
A Module Hierqrchv corresponds to how modules are instantiated in other modules. It
is possible to have multiple module hierarchies if multiple top level modules ex.ist. Top level
modules are defined as modules that are not instantiated in any other module. Figure 4.2 has
only one module hierarchy rooted at module mytop. Module hierarchies are used for provid-
ing a structured programming environment by grouping closely related procedural blocks in
modules. The process of elaboration which takes place before simulation starts, removes the
module hierarchy and produces a set of interconnected procedural blocks similar to the one
shown in figure 4.1. Simulation is actually perfonned on this elaborated view of a System-
Veri log program.
Procedural Hierarchy corresponds to how procedural blocks call functions or tasks.
Figure 4.2 shows two procedural hierarchies: one rooted at the initial block (lines 19-25);
and the second rooted at the procedural block created because of the continuous assignment
statement (line 5), which makes no other procedure calls and as such, has no sub-hierarchy.
Data Object Hierarchv describes the composition of complex data types. Four data
object hierarchies in figure 4.2 are those rooted respectively at tv, yy, decval, and cc;
SystemVeriiog provides powerful constructs for specifying these hierarchies. The
remainder of this chapter describes these constructs. Section 4.3 describes data modeling and
manipulation. Section 4.4 describes procedural blocks composed of data objects and proce-
dural statements. Section 4.5 describes the use of procedural blocks and instantiation to cre-
ate module hierarchies. Synchronization between concurrently running threads of execution
is described in section 4.6. Object-oriented programming-which is the preferred program-
ming paradigm for building scalable and highly complex programs while maintaining high
productivity-is described in section 4.7.

SystemVerilog defines the following forms of data:


• Literals
• Parameters
70 SystemVeriiog as a Programming Language

• Constants
• Variables
• Nets
• Attributes
Literals refer to explicit representation of a value in program code such as integer literal
1 or string literal "Arbiter". Parameters are values that can be individual1y assigned for differ-
ent instances of a module and as such, allow for customization of different instances when
building the module hierarchy. Parameters are taken into consideration during program elab-
oration (creation of the module hierarchy) before simulation starts, and as such cannot be
changed during simulation runtime. Constants are represented by named objects whose val-
ues don't change throughout the simulation runtime. Variables are named data objects whose
values can change during simulation runtime. Nets are similar to variables, but more closely
model a physical wire, since the new value of a net data object is decided by resolving the
newly assigned value with the previous value of the net (this is in contrast with variables
where last assignment always prevails). Attributes are used to attach properties to objects
(i.e., modules, instances, wires, etc.) for later retrieval.
A data object can be fully described by the following properties:
• Lifetime: When it is created and when it is destroyed
• Update mechanism: How its value is changed
• Type: The type of data it holds
These aspects are discussed in the following subsections.

4.3.1 Data Lifetime


SystemVerilog defines two types of data lifetime:
• Static
• Automatic
An automatic variables is created when program execution enters the scope 2 in which
that variable is declared. For example an automatic variable inside a function call is created
when program execution enters that function and is destroyed when program execution
returns from that function call. Static variables are created at the beginning of program exe-
cution and hold their value throughout program execution until they are assigned a new
value. The major implication of this difference is that a static variable defined in a method
(i.e., function or task) will hold its value across multiple calls to that method, even when that
method is called from different places in the program flow. As such, static variables in a
function or task can be used to share data between consecutive invocations of that function or
task.
Variables declared inside functions and tasks are by default automatic variables. How-
ever, SystemVerilog provides the necessary syntax to mark all variables declared inside a

2. A scope defines an enclosing context within which a data object is \'isible and can be llsed in expres-
sions.
Data Types and Objects In SystemVerllog 71

program, module, function, or task as either automatic or static by appending the appropriate
keyword to that procedural block (see section 4.4).

4.3.2 Update Mechanisms


Update mechanism refers to how the value of a data object is changed when a new value is
assigned to it. The following data types each use a different update mechanism:
• Constants
• Variables
• Nets
The value of a constant data object cannot change throughout the program lifetime. The
value of a variable is updated to be the last value assigned to it. The value of a net is updated
according to a resolution function that takes into account the value of the net before the last
assignment and the last value assigned to it. Nets are meant to model physical wires in a
design while variables are meant for storing program data.
The update mechanisms for variables and nets, as described above and defined in the
Verilog language, impose restrictions on how values can be assigned to these data objects.
Assignments in Verilog are one of:
• Procedural assignments
• Continuous assignments
A procedural assignment is a one time action of putting a value into a data object. In a
continuous assignment, the assigned value is imagined to be continuously driving the data
object. In Verilog, a variable can be assigned only through procedural assignments and a net
can be assigned only through continuous assignments or module ports (which indirectly
imply continuous assignments). The reason for this restriction is that the effect of a continu-
ous assignment to a data object should be taken into account when computing its value after
any new assignment. This calculation requires a resolution function which is available only
for net type data objects. SystemVerilog relaxes this requirement slightly by allowing a vari-
able to be assigned by only a single continuous assignment. This flexibility does not violate
the overall assumption about needing a resolution function in handling continuous assign-
ments. The reason is that if a data object is driven with only one continuous assignment
throughout the simulation runtime, then it does not need a resolution function, and hence that
data object can be a variable.

4.3.3 Data Types


SystemVerilog extends the built-in data types defined in Verilog in the following ways:
• Introduces new built-in types
• Allows any number of unpacked dimensions for each variable
• Introduces enumerated data types
• Introduces user defined data types
• Introduces composite data types
• Introduces classes and object-oriented programming constructs
72 SystemVeriiog as a Programming Language

Classes and object-oriented programming is discussed in section 4.7. Other topics are
discussed in the following subsections.

4.3.3.1 Built-In Data Types


Table 4.2 shows a summary of syntax used for writing SystemVerilog constant values. The
following observations apply to these constants:
• Integer constants can be specified as either sized or un sized.
• A sized integer constant is sign extended to fill all of its bits (e.g., -4'b1 is sign
extended to fill four bits).
• A negative integer constant behaves differently when it includes a base specifier
(e.g., ·'d12 or -4'd12). Without a base specifier, the constant is assumed to be a signed
value when used in evaluating an expression. For example, expression (.2/2=·1),
since constant -2 is considered a negative value when evaluating the expression. With
a base specifier, however, the 2's complement value of the integer constant is
assumed to represent an unsigned value when used in evaluating the expression. For
example, expression (-'d2/2=2147483647), since constant ("d2) is first sign extended to
fill a 32-bit holder, producing value of 4294967294, and then used as an unsigned
value in evaluating the expression. Special care should, therefore, be applied when
using negative integer constants with a base specifier (either sized or unsized).

Constant Type Notation Examples


19813 (decimal, no x or z allowed)
'blOzl Ox 10 (binary including x, z)
'07z1x (octal including x, z)
Unsized
'dl92 (decimal)
'h 1zfaxfa (hex including x, z)
Integer -'hltbca (negative hex)
8'blOzl OxlO (binary including x, z)
.12'0721 x (octal including x, z)
Sized -IO'dl92 (decimal sign extended to 10 bits)
28'h I zfaxfa (hex including x, z)
-24'hltbca (negative hex sign extended to 24 bits) I

14.55
Decimal
0.45
Real I
l.4e13 (exponent must be an integer)
Scientific
I
0.55e·4 (exponent must be an integer)
"this is a text"
"In" (a new line character)
String "It" (a tab character)
Quoted
"II" (a I character)
"\" (a quote character)
"Iddd" (an octal digit for a character d<8)
I
Table 4.1: System Veri log Constant Types and Syntax

Table 4.2 shows a summary of built-in data types available in System Veri log. The fol-
lowing observations apply to data types shown in this table:
• Types reg and logic are exactly the same. Type logic is introduced in System Veri log in
order to better reflect the usage of this data type,
Data Types and Objects In SystemVeriiog 73

Signed
Form Type Values Packed Size New In SV
(default)

byte yes (signed) 0,1 8 bits yes


shortint yes (signed) 0,1 16 bits yes
int yes (signed) 0,1 32 bits yes
longint yes (signed) 0,1 64 bits yes
integer yes (signed) O,l,x,z 32 bits
bit yes (unsigned) 0,1 User Defined yes
logic yes (unsigned) O,l,x,z User Defined yes
reg yes (unsigned) O,l,x,z User Defined
Variables
time no O,l,x,z 64 bits
shortreal always signed float yes
real always signed double
realtime no
String NA
void NA yes
event NA
chandle NA yes
supplyO no 0, I,x,z, 7 strengths User Defined
supply I no O,I,x,z, 7 strengths User Defined
tri no O,l,x,z, 7 strengths User Defined
triand no O,I,x,z, 7 strengths User Defined
trior no O,I,x,z, 7 strengths User Defined
Nets
triO no O,I,x,z, 7 strengths User Defined
tril no O,I,x,z, 7 strengths User Defined
wire no 0, I,x,z, 7 strengths User Defined
wand no O,l,x,z, 7 strengths User Defined
wor no O,l,x,z, 7 strengths User Defined

Table 4.2: SystemVerilog Built-In Data Types

• Types bit, logic, and reg can have packed sizes of any dimension (see section
4.3.3.3.1 ).
• Types byte, shortint, int, longint, and integer are signed by default but types bit,
logic, and reg are unsigned by default. To change from the default, keyword signed
or unsigned must be used when declaring variables of this type.
• Some built-in data types can be described by using other data types. For example,
byte is the same as "bit [7:0]" and integer is the same as "logic signed [31:0]".
The following program shows example declarations for these data types.

'Program 4.2: SystemVeriiog example data type declarations


1 module top;
2 byte unsigned b1, b2;
3 shortint unsigned si1, si2;
4 in! unsigned i1 , i2;
5 longin! unsigned 1i1, li2;
6 integer unsigned ir1, ir2;
7 bit signed [10: 1][10: 1] bt1, bt2;
8 logic signed [10:1][10:1]lg1, Ig2;
74 SystemVerllog as a Programming Language

9 reg signed [10:1][10:1] rg1, rg2;


10 time t1;
11 shortreal sr1, sr2;
12 real r1, r2;
13 realtime rt1, rt2;
14 string str;
15 end module

4.3.3.2 Enumerated Data Types


An enumerated data type is represented by a set of named constants and a data type for the
values represented by these named constants. A variable declared to have an enumerated
data type is strongly typed and can only take values from tl:te set of named constants included
in the enumerated type declaration.
An enumerated type is represented by the following elements:
o Data type for values represented by named constants
o Name and value assigned to each named constant
The syntax for an enumerated type is as follows:
enum [data_type] {name1=value1, name2=value2,. .. ,nameN=valueN} var_name
The following rules apply to the above syntax:
o Names for the named constants (e.g., nameh name2, etc.) must be unique.
o If data_type is omitted, it is assumed by default to be of type into
o Values assigned to named constants (e.g valueh value2' etc.) must be valid values for
type data_type. For example, if data_type is bit, then valid values are 0 and 1.
o Two named constants cannot have the same value.
o If the value for a named constant is omitted, it is assumed to be the next value after
the value for the previous named constant.
SystemVerilog provides a shorthand notation for declaring an enumerated type where
names for a group of named constants can be represented as a range and values assigned to a
named constant can be assigned automatically. The syntax for shorthand representation of a
named constant or a group of named constants is:
syntax1: enumName[index1 :index2]=enumValue
syntax2: enumName[Numberl=enumValue
In syntax1, one named constant is assumed for each value in the range [lndex1:lndex2]
where each named constant is assigned consecutive values starting from enumValue. In
syntax2, the range is assumed to [O:Number·1]. For example:
enum int {red[1 :3]=1, green[2:31=5} colors;
is equivalent to:
enum int {red 1=1. red2=2, red3=3, green2=5, green3=6} colors:
The following rules apply to this shorthand notation:
o Either the range [index1:index2] or enumValue can be omitted.
o If range [index1:index2] is omitted, then only one named constant enumName is
assumed.
o Jf enumValue is omitted. then its value is taken from the last enum value assigned to
Data Types and Objects In SystemVerllog 75

the preceding named constant, with 0 assumed if it is the first named constant in the
list.
• Values x and z can be assigned to named constants if data type is four-valued (e.g.,
integer). In this case, the value for the named constant immediately following this
named constant must be explicitly specified.
SystemVerilog provides methods for iterating over the named constants of an enumer-
ated type. These methods are:
function enum firstO;
function enum lastO;
function enum next(int unsigned N = 1);
function enum prev(int unsigned N = 1);
function int numO;
function string nameO;

Function firstO returns the first named constant in the enumerated type declaration,
LastO returns the last named constant, nextO and previousO return the next and previous
named constants respectively, numO returns the number of named constants, nameO returns
a string containing the name of a named constant.
An example of using enumerated type and its functions is shown below.

'Program 4.3: Declaring and using enumerated types


1 module top;
2 initial begin
3 enum {asia=1, europe, africa=8, america, australia=44} location;
4 location = location.firstO;
5 for (int i = 0; i< location.numO: i++) begin
6 $display("%s:%d", location.nameO, location);
7 location = location.nextO;
8 end
9 end
10 endmodule

The output produced by the above example is shown below:


asia: 1
europe: 2
africa: 8
america: 9
australia: 44

As shown in the output, the constant value assigned to europe is 2, which is the next
value to that assigned to the previous named constant asia. Similarly, the constant value
assigned to america is 9.

4.3.3.3 Arrays
SystemVeri log provides the following types of array data types:
• Static arrays
• Dynamic arrays
• Associative arrays
• Queues
76 SystemVerilog as a Programming Language

Static arrays are multidimensional arrays whose size is explicitly specified when declar-
ing the array. Dynamic arrays have one or more dimensions with one dimension whose size
is undefined during declaration and is decided when creating the array. Associative arrays
allow access to array elements using a key that can have any data type. Queues are used to
model an ordered set of elements. These array types are described in the following subsec-
tions.

4.3.3.3.1 Static Arrays


A static array is a multidimensional array whose number of dimensions and width for each
dimension is specified in its declaration. SystemVerilog static arrays can have any number of
dimensions and can be operated on as whole arrays, slices, or individual elements.

SystemVerilog extends the classic notion of arrays by allowing the programmer to


de-fine how each dimension of an array is stored in memory. This decision provides a
trade-off between storage efficiency and access flexibility. Members of a packed dimension
of an array are grouped into memory words before being stored in memory. As such, a
packed array dimension provides good storage efficiency. Members of an unpacked dimen-
sion of an array are stored in individual memory locations. As such, an unpacked array
dimension can be accessed with great flexibility.
The syntax for a static array declaration is as follows:
elemenCdata_type [PRange11 ... [PRangeN) array-name [URange1j .. .[URangeM);

Dimensions specified before array-name are packed dimensions, while dimension spec-
ified after array-name are unpacked dimensions. The following guidelines apply to static
arrays:
• A single packed word is used to store all packed dimensions of an array.
• Array elements are accessed as shown below. As such, packed dimensions change
more frequently than unpacked dimensions.
array-name [URange1j ... [URangeM][PRange11 ... [PRangeN)
• An array dimension range can be specified as [index1:index21 or as [Number], where
[Number] is equivalent to [O:Number-11.
• If an array is declared as signed, then the memory word containing all of the packed
dimensions is assumed to be a signed value. A slice of a packed dimension is not a
signed value.
• Packed dimensions can only be specified for arrays of type bit, logic, reg, and wire.
Unpacked dimensions can be specified from any type.
Arrays can be accessed using the following mechanisms:
• Reading and writing the whole array
• Reading and writing a slice of array
• Reading and writing a variable slice of array (identified by another variable)
• Reading and writing an array element
• Equality operations on array or slice of array
It is important to note that dimension part-selects (slice spanning multiple elements of a
dimension) can be used only with packed dimensions. Examples of these access types are
shO\\'n below:
Data Types and Objects in SystemVeriiog 77

'Program 4.4: Array access types


1 module top;
2 initial begin
3 1/ dimension numbers
4 1/3412
5 bit [4:0][3:0] arr1 [2:0][1 :0];
6 bit [4:0][3:0] arr2 [2:0][1 :0];
7 int index;
8 index = 1;
9
10 arr1 = arr2;
11 if (arr11= arr2)
12 $display ("Arrays are not equal");
13 arr1[1][1] = arr2[2][0);
14 if (arr1[1][1)!= arr2[2)[O))
15 $display ("Arrays are not equal");
16 arr1[1)[1][1][2:1) = arr2[2][0][0][1 :0);
17 if (arr1[1][1][1][2:1)!= arr2[2][0][0][1 :0))
18 $display ("Array slices are not equal");
19 arr1 [1)[1)[1 )[index +:2] = arr2[2][0][0][(index-1) +:2);
20 end
21 endmodule

The above program shows examples of a whole array assignment (line 10), array slice
assignment (lines 13, 16), array variable slice assignment (line 19), and array compare oper-
ations (lines 11, 14, and 17). Dimension order for array element access is shown on lines 3
and 4.
SystemVerilog provides the following system defined methods for accessing array ele-
ments: $leftO, $rightO, $lowO, $highO, $incrementO, $sizeO, and $dimensionsO. See the
SystemVerilog reference manual for details of these functions.

4.3.3.3.2 Dynamic Arrays


Dynamic arrays are similar to static arrays except that they can have one unpacked array
dimension whose size can be decided during simulation runtime. Memory for a dynamic
array is allocated during simulation runtime. A consequence of this runtime allocation is that
the memory for dynamic arrays is not part of the program memory space.
The syntax for a dynamic array declaration is as follows:
element_data_type [pRange11 ... [PRangeNl array-name [];

SystemVeri log provides the following operators and methods for creating and interact-
ing with the dynamic arrays:
• Operator new
• Function sizeO
• Function deleteO
Operator new is used to allocate a dynamic array. Accessing a dynamic array before
memory is allocated results in a runtime error. Function sizeO returns the size of the
unpacked dimension of the array, which is the size specified when the array was allocated.
Function deleteO is used to reset the array size to o.
78 SystemVeriiog as a Programming Language

The rules of access for dynamic arrays (Le., reading and writing slices, etc.) are the
same as those for static arrays once the size of a dynamic array is known. Since this size is
only decided at program runtime, dynamic array access can lead to a runtime error message
while the same error types are checked at compile time for static arrays.
The following program code shows an example of dynamic arrays:

'Program 4.5: Dynamic array declaration and allocation


1 module top;
2 bit [4:0][3:0] arr1 0, arr2(];
3 =
int count 10;
4 initial begin
5 arr1 = new[countj;
6 $display(arr1.size()); 1/ displays 10;
7 arr2 = new[20](arr1);
8 $display(arr2.sizeO); 1/ displays 20;
9 arr1.deleteO;
10 $display(arr1.size(»; " displays 0;
11 end
12 : endmodule
~--------------------

Operator new is used (lines 5, 7) to allocate dynamic arrays of size 10 and 20 for arr1 and
Operator new allows an array to be specified for initializing the allocated array. This is
arr2'
shown on line 7 where elements of arr1 (10 elements) are used to initialize the first 10 ele-
ments of arr2'

4.3.3.3.3 Associative Arrays


In associative arravs, array elements are identified by a key value. This identification mech-
anism is in contrast with static and dynamic arrays where array elements are identified by
their position within a linear memory space. Associative arrays are ideally suited for sparse
linear arrays where not all array positions contain relevant values, or in cases where array
elements must be identified by a key and not by their position in the array.
Associative arrays are declared using the following syntax:
data_type array_name[key-type];

In this syntax, data_type is the type of each array element which can be any type allowed
for static and dynamic arrays, array-name is the name of the associative array being declared,
and key-type gives the data type for keys used to access array element.
Examples of associative array declaration, assignment, and usage is shown in the fol-
lowing program:

'Program 4.6: Associative array declaration and access


1 module test;
2 class profile;
3 int id_num;
4 string first_name;
5 string last_name;
6 function new(int id, string fn. string In):
7 = =
id_num id; first_name fn: last name =In;
8 endfunction -
9 end class
10
Data Types and Objects In SystemVeriiog 79

11 typedef bit signed [3:0] signed_nibble;


12 typedef bit [3:0] unsigned_nibble;
13 integer a_arrO[integer];
14 integer a_arr1 [string];
15 integer a_arr2[profile];
16 integer a_arr3[signed_nibble];
17
18 initial begin
19 profile prof1 ;
20
21 =
a_arr1["jack"] 1414;
22 a_arr1[""] = 2424;
23
24 prof1 = new(O, "FirstName", "LastName");
25 $display(a_arr2[prof1]);
26 a_arr2[prof1] = 123;
27 a_arr2[null] = 234;
28 $display(a_arr2[prof1]);
29 end
30 end module

The following comments apply to this example:


o Associative array a arrn uses predefined type integer as its key type (line 13).
o Associative array a_arr1 is declared to have a key of type string (line 14). This array
can be accessed by using string keys (lines 21-22). The empty string is a valid key
for an associative array (line 22).
o Associative array a_arr2 is declared to have a key type of user defined class profile.
Class profile is defined to hold fields of different data types (lines 3-5). The above
example shows the creation of data object prof1 (line 24) which is used as a key for
associative array a_arr2 (line 26). Null is a valid key for associative arrays having a
class key type (line 27).
o Reading an associative array element that is not yet initialized returns the default
value for the array element type. For example, reading array element a_arr2[prof1]
(line 25) returns an empty string since no value is assigned to a_arr2[prof11 previously.
o Associative array keys can be a user defined type. The declaration of associative
array a_arr3 is defined to have user defined type signed_nibble (line 11) as its key type
(line 16).
System Verilog defines a set of predefined methods for accessing associative arrays. The
function prototype for these methods are:
function int num(); II return number of elements in array
function void delete([input k]); IIdelete element at key k (key is optional, delete all if not given)
function int exists(input k); lire turn 1 if element at key k exists
function int first(ref k); IIreturn 1 if first key exists, assign k to first key
function int last(ref k); IIreturn 1 if last key exists, assign k to last key
function int next(ref k); IIreturn 1 if key next to k exists, replace k with key next to k
function int prev(ref k); IIreturn 1 if key prev to k exists, replace k with key prev to k

Examples of associative array method usage is shown in the following example:

'Program 4.7: Associative array method usage


1 module test:
2 integer aa5[integer]:
3
4 initial begin
5 integer aakey:
80 SystemVeriiog as a Programming Language

6 for (int i = 0; i < 5; i++) aa5[i] = 10'i;


7 $display(aa5.num());
8 aa5.delete(3);
9 $display(aa5.num());
10 $display(aa5.exists(3));
11 if (aa5.first(aakey» $display(nFirst key Is: n, aakey);
12 while (aa5.next(aakey» $display(nnext key is: n, aakey);
13 if (aa5.1ast(aakey» $display(nLast key is: n, aakey);
14 while (aa5.prev(aakey» $display(nPrev key is: n, aakey);
15 aa5.deleteO;
16 $display(aa5.num());
17 end
18 endmodule

First, associative array aa5 is initialized with 5 members (line 6). Function numO is used
to print the number of elements in this array, yielding a value of 5 (line 7). Function deleteO
is used to delete array member stored at key 3 (line 8), after which, function numO returns a
value of 4 (line 9), and function call aa5.exlsls(3) returns a value ofo (line 10). FunctionflrstO
is used to place the first key of aa5 (value 0) into variable aakey (line 11). Function nextO is
used iteratively to print consecutive keys for array aa5 which prints a sequence of (1,2,4)
(line 12). Function lastO is used to assign the last key for array aa5 (value 4) into variable
aakay (line 13). Function prevO is then used iteratively to continuously assign the previous
key to the key stored in variable aakay back into variable aakay and print the result, producing
output (2,1,0). Function deleteO is used to delete all elements of array aa5 (line 15).
An associative arrays can only be assigned to another associative array having the same
key and member data types.

4.3.3.3.4 Queues
A q.ueue is an ordered collection of homogeneous (I.e., all having the same data type) ele-
ments. Queue elements can be accessed in constant time3. In addition, a queue can be grown
or shrunk. on both ends in constant time. A SystemVeri log queue construct is suitable for
implementing FIFO (first-in-first-out) and stack (first-in-last-out) data structures.
Queues are declared using the following syntax:
data_type queue_name[$];
data_type queue_name[$:max_sizel
In this syntax, data_type is the type of each queue element which can be any type
allowed for static and dynamic arrays, queue_name is the name of the queue being declared,
and max_size (if provided) gives the maximum number of elements allowed in the queue.
SystemVerilog defines a set of predefined methods for manipulating and accessing
queues. The function prototype for these methods are:
function int sizeO; /lreturns number of elements in queue
function void insert(int index, queue_type item);/I inserts elements at position index

>. In this contc:xt. constant time refers to the computation el1'on required to process an entry. For example,
acccssing an element in a linked-list may in the worst case re:quire that aillinked·list elements be tra-
\·crse:d. Meanwhile. an array ele:ment can be accesse:d using its index. Therefore:. accessing array elements
can be done in constant time but access time for linked-list elements is proponional to the size of the
linkc:d-list.
Data Types and Objects in SystemVeriiog 81

function void delete(int index); IIdeletes elements at position index


function queue_type pop_frontO; IIremoves element at head of queue
function queue_type pop_backO; IIremoves element at tail of queue
function void push_front(queue_type item); IIadds element to head of queue
function void push_back(queue_type item); IIadds element to tall of queue

Examples of queue declaration, assignment, and usage is shown in the following:

'Program 4.8: Queue declaration, access, and manipulation


1 module test;
2 int intq[$];
3 int boundedjntq[$:2];
4
5 initial begin
6 int val, size;
7 size = intq.sizeO; II size=O, intq=O
8 intq.push_back(10); II intq={1 O}
9 intq.push_back(20); /I intq={10,20}
10 intq.push_front(30); II intq={30 ,1 O,20}
11 intq.push_front(40); /I intq={40,30,1 O,20}
12 Intq.insert(1, 50); II intq={40,50,30,10,20}
13 intq.insert(3, 60); /I intq={40,50,30,60,1 O,20}
14 val = intq.pop_back(); /I val=20, intq={40,50,30,60,10}
15 val = intq.pop_frontO; II val=40, intq={50,30,60,10}
16 intq.delete(2); 1/ intq={50,30,10}
17 val = intq[1]; "val = 30
18 val = intq[1001; "val = 0;
19 size = intq.sizeO; II size = 3
20 end
21 endmodule

This example, shows the declaration of unbounded queue intq and bounded queue
Ilnbounded_,ntq (lines 2 ~). Pledefined queue functions ale then used to add, remove, and
access queue elements (lines 7-19). The contents of queue after each operation is shown as a
comment on each line. Note that the first element shown in the contents of the queue is at
index o. Accessing a queue element at a non-existing index (line 18) returns the default value
for the type of queue element. Using function deleteO to delete a member at a non-existing
index results in a runtime warning message. Using function insertO to insert a value at a neg-
ative index or beyond the last element of the queue also results in a runtime warning mes-
sage.

4.3.3.4 Composite Data Types


SystemVerilog provides the struct and union constructs for declaring composite data types.
Members of a struct construct are stored in consecutive memory locations, while members
of a union share the same memory space whose size is the size of the largest member of the
union.
A slruct composite data can be declared as a packed struct. In this case, the memory
used to store the contents of this struct are packed into memory words (similar to packed
array dimensions). Packed structs can be accessed as a packed one-dimensional array whose
most significant bit contains the most significant bit of the first member of struct.
A union can also be declared as a packed union. In this case. all members of the union
must have the same size.
82 SystemVerllog as a Programming Language

A packed struct that has any four-state member is stored in a four-state memory word.
Otherwise, the packed struct is stored in a two-state memory word.
Use of packed structs is shown in the following example:

'Program 4.9: Packed struct declaration and assignment


1 module top;
2 initial begin
3 typedef enum [3:0] {READ, WRITE, IDLE} opcode_t;
4 struct packed {
5 opcode_t opcode1;
6 opcode_t opcode2;
7 bit [7:0] data;
8 } IR;
9 IIIR as a packed word: opcode1 [3:0]opcode2[3:0]data[7:0]
10 IR.opcode1 = READ;
11 if (I R[15: 12]!= READ) $display ("Assignment Failed");
12 end
13 endmodule

This example shows a packed struct declaration that defines instruction IR. Each opcode
is four bits (line 3). The packed struct, therefore, is 16 bits. Members of a packed struct can
be accessed either through member names or their relative position in the packed memory
word storing the struct data. As shown, IR.opcode1 is assigned on line 10 and then IR[1S:12] is
checked for correct assignment on line 11. Variable IR is stored as 2-valued data. If, however,
one of struct members (lines 5-7) were 4-valued, then IR would be stored as 4-valued data.

4.3.3.5 User Defined Data Types


A user can define a new type by using the typedej construct. Defining a new type can greatly
increase code readability as complex data types and array sizes can be defined as a new data
types with an easily understood name.
Defining a new data type is at times necessary since casting in System Veri log only
allows simple names to be specified for the type to be cast into. As such, a type whose
description consists of more than simple names must be defined as a new type before it can
be used in casting.
Some examples of new data types include:
typedef int my-favorite_type;
typedef enum bit [3:0] {READ, WRITE, IDLE} opcode_t;
typedef struct packed {byte opcode1; byte data;} instruction;
typedef bit [3:0][3:0] my-2packed_1 unpacked_array [1 :0];
typedef my_2packed_1 unpacked_array my-2packed_2unpacked_array [1 :0];
my-2packed_1 unpacked_array arr_2_1;
my-2packed_2unpacked_array arr_2_2;

Data types my_favorite_type, opcode_t, instruction, my_2packed_1unpacked_array, and


my-2packed_2unpacked_array are declared in the above examples. Note that in the above
examples. arr_2_1 is a three-dimensional array with two packed dimensions and one
unpacked dimension. and arr_2_2 is a four-dimensional array with t\\'o packed dimensions
and two unpacked dimensions.
Data Types and Objects in SystemVeriiog 83

4.3.4 Operators
SystemVerilog provides operators from both Veri log and C. SystemVerilog operators follow
the semantics of operators in Verilog. SystemVerilog operators are shown in table 4.3. The
following notes apply to operators listed in this table:

Operation Syntax Notes Operation Syntax Notes


I NOT !E NegatIOn -E
AND E) && E2 Add E) + E2
OR E)II E2 I Subtract E) - E2

-; Equality E) == E2 2 '="III I Multiply E) * E2


'o'"cn Inequality £) !- E2 2 e Divide E)I E2
of
..J Case equality 11 === [2 3 ·c Modulo S) % S2
-(
Case inequality I) !== 12 3 Power E) ** E2
Wild equality I) ==? [2 1,4 Increment ++ J
Wild inequality [) !=? 12 1,4 Decrement -- J
NOT -\ 5 Unsigned left 1)« 12

.
.~
AND
OR
I) & 12
[) 1'2
5
5
.;:
:c00
Unsigned right
Signed left
[»> 12
S«<I 1
!
= XOR I) A 12
11 _A [2
5

5
..:
eo
Signed right

Non-repeating
S»>I 1

XNOR {I!> ... ,I n}


II A-12
.,="
AND &1 6 U Repeating (C{II""'!n} }
NAND -&1 6 .... Member [C]
= ""
.~ lOR II 6 "il
00
Fixed part [C):C 2]
..,"
::I NOR -II 6 1:: Variable right [I+:C] 1
"
c::I: XOR Al 6 ~" Variable left [I-:C] I I
_AI ..,
XNOR A_I 6
U
= Conditional Select I?E) : E2 7

Less than E) <E2 Blocking V-E


~
"i
=
Less or equal E] <= E2 = Non-blocking V<=E
"
.
~
"
f-c::I: I
Greater than

Greater or equal
E) >E 2

E] >=E 2
E
=
.~
.';; Operators
+= -= *= /=
%=&=1="'=
«=»=
«<= »>=
I: An expression producing an unsigned integer J: A variable of integral data type
S: An expression producing a signed integer V: A variable of any type
R: An expression producing a real value C: An unsigned integer constant
E: One ofi,S,R
I I
Table 4.3: System Veri log Operators

• Note]: New operators introduced by System Verilog.


• Note2: Equality operators return a 0 or a 1 depending on the values of their argu-
ments. If any of the arguments contains an x or a z in any bit position, then the result
is anx.
• \:ote3: Case equality operators compare x or : \'alues in any bit position of their argu-
ments. As such. these operators only return a \"alue of 0 or 1.
• ~ote4: Wild equality operators treat x or : "a lues in any bit position of their argu-
84 SystemVeriiog as a Programming Language

ments as don't care values. These operators only return a value ofo or 1.
• Notes: If the arguments to the bitwise operators are not the same size, then the shorter
argument is zero-filled in the most significant bit positions.
• Note6: Reduction operators return a 0,1, or x depending on the individual bit values
of its argument. Value z is treated the same as x when combining individual bit val-
ues.
• Note7: If the condition expression for a conditional select operator evaluates to an x,
then the result is computed by bitwise combination of both its arguments, with the
exception that a value of 0 is returned if any of its arguments have a real data type.
The bitwise combination returns an x for any bit position if the values of arguments
in that bit position are different or a z.

4.4 Procedural Statements and Blocks


A procedural block has its own thread of execution where the statements inside the block are
executed sequentially, similar to that of a C or C++ program. Procedural blocks are com-
posed of procedural statements. SystemVerilog allows the following types of procedural
statements to appear in a procedural block:
• Assignment Statements
• Subroutine Calls
• Selection Statements
• Loop Statements
• Jump Statements
• Event Control Statements
These procedural statements are described in the following subsections.

4.4.1 Assignment Statements


Assignment statements are used to update data values in the program. System Verilog pro-
vides the following assignment statements.
• Blocking Assignments
• Non-Blocking Assignments
• Increment/Decrement Statements
• Procedural Continuous Assignments
The following program shows an example of these assignment statements:

'Program 4.10: SystemVerilog assignment statements


1 module top;
2 wire [9:0] net data;
3 initial begin -
4 logic [9:0] data [10:0}:
5 logic [9:01 var_data;
6
7 data[O] <= 'x; /I non-blocking assignment
Procedural Statements and Blocks 85

8 data[1J ='z: /I blocking assignment


9
10 data[2J =7:
11 data[2J ++: /I increment variable
12 data[2J--: /I decrement variable
13
14 assign var_data = data[2J: /I continuously assign value 7 to var_data
15 var data = 4: /I will not change var_data
16 deassign var_data: /I remove continuous assignment
17 =
var_data 5: /I now var_data will change
18
19 =
force var_data data[1J:
20 release var_data:
21
22 =
force net_data data[1J:
23 release net_data:
24 end
25 endmodule

Lines 7 and 8 show examples of non-blocking and blocking assignments. These types
of assignments have been covered extensively in introductory books on Verilog. SystemVer-
ilog introduces increment and decrement operators which assign the current value plus one
and current value minus one onto itself respectively.
Procedural continuous assignments include assign, deassign of variables and force and
release of variable and net data types. The procedural continuous assignment has been
placed on the deprecated list in SystemVerilog and is expected to be removed from the lan-
guage in future releases.

4.4.2 Subroutine Calls


System Veri log provides the following two subroutine call methods:
• Functions
• Tasks

These methods are described in the following subsections.

4.4.2.1 Functions
Functions have a list of arguments and return a value. Functions execute in zero simulation
time, which means that time control statements cannot be used inside a function.
Function arguments can be one of:
• input
• output
• inout
• ref
The semantics of each argument type is as follows: For input argument types. the value
of the argument is copied from the caller's context upon entering the function. The value of
an output argument type is copied upon completion of the function from inside of the func-
tion to the actual variable specified in the caller's context. The \"Blue of an illout argument
86 SystemVeriiog as a Programming Language

type is copied twice once upon entering the function and once upon leaving. No value is cop-
ied when using a ref argument type, and both the function and its caller use a reference to
data object passed in the argument. Therefore, any change that a function makes to the value
of a ref type argument is immediately visible outside the function. Both inout and ref quali-
fiers result in changes to the argument value made inside the function to become visible in
the caller's context, with the difference that changes made inside a function to a ref type
argument are immediately visible in the caller's context but changes made inside a function
to an inout type argument become visible only after the function returns.
A function can be declared as automatic or static. All variables local to an automatic
function are assumed to be automatic variables unless explicitly changed for each variable in
its declaration. All variables inside a static function are assumed to be static variables unless
explicitly changed foreach variable in its declaration. The value of static variables are main-
tained across calls to the same function. SystemVeri log allows individual variables declared
inside a static function to be marked as automatic. It also allows individual variables
declared inside an automatic function to be marked as static variables.
The following program shows an example of a function declaration and its usage.

I Program4.11: Declaring and calling funclions


1 Iypedef struct {int ii; int H;} twovaU;
2
3 module top;
4 function automatic twovaU twovaUncrement (
5 twoval I data,
6 ref twovaU increment,
7 inout bit er,
8 output bit 01);
9 static int local int;
10 Iwoval increment.ii = data.ii + increment.ii;
11 twovaCincremenl.H = data.jj + increment.H;
12 er=erIIO;
13 01 = IwovaUncrement.ii > 20 II twovaUncrement.jj > 20;
14 endfunction
. 15
16 function automalic int increment;
17 input inl data=O;
18 canst ref int incr;
19
20 int local val;
21 local val = data + incr;
22 return local val;
23 endfunction-
24
25 initial
26 begin
27 twoval I A;
28 twoval t incr;
29 bit err:Overload;
30 int i;
31
32 A.ii =1; A.jj=2:
33 incr.ii =10; incr.jj=21;
34 A = twovaUncremenl(A. incr. err. overload):
35 $display(A.ii. A.H. err. overload);
36 i = 4;
37 $display(increment(3,i)):
38 $display(increment(.i));
Procedural Statements and Blocks 87
~------------------------------------------------------------

39 end
40 ,
endmodule
~-------------------

The following properties apply to functions:


• Function declarations can follow an ANSI 4 format (lines 4-8) or Verilog style format
(lines 16-18).
• A function argument without a direction specifier is assumed to have the same direc-
tion as the last argument that had a direction specifier. In the absence of any such pre-
vious direction specifier, direction defaults to input (line 5).
• Argument default data type, ifnot specified, is logic.
• Arrays can be formal arguments to a function.
• A function can be defined as either static or automatic (hnes 4, 16) but variables local
to a function can be assigned a different lifetime (line 9).
• Functions do not need to be inside a begin-end block.
• A return statement can be used to return from a function (line 22).
• The return value of a function can be ignored by casting it to a void, as in
"void'(myfunc{))"
• A function having arguments of type inout, output, or ref cannot be used in event
expressions, in an expression within a procedural continuous assignment, or in an
expression that is not within a procedural statement.
• The return value of a function can be a composite data type (lines 10, 11).
• Only variables can be passed as ref type arguments. Nets cannot be used as ref type
arguments.
• The const keyword can be used to prevent a function from changing the value of a
variables passed to it as a ref type argument (line 18). This is useful when a large data
structure must be shared with a function as a refso that the entire structure does not
need to be copied but any changes to that data structure from inside the function must
be prevented.
• Arguments with default values can be omitted when calling a function (line 38).

4.4.2.2 Tasks
Tasks and functions are very similar, except in the following ways:
• A task does not return a value
• A task may have time consuming statements
The following program sample highlights these differences.

: Program 4.12: Declaring and calling tasks


1 typedef struct {in! ii; intjj;} twovaU;
2
3 module top;
4 task automatic twovaUncrement (
5 twovaU data,
6 ref twovaU increment,
7 inout bit er,
a output twovaU out):

4. A~SI: American :"atil'nal Standards Institute: http://www.ansi.org.'


88 SystemVerilog as a Programming Language

9 #14ns out.ii = data.ii + increment.ii;


10 #33ns out.jj = data.jj + increment.jj;
11 er = er II 0;
12 endtask
13
14 initial begin
15 twovaU A, B, incr;
16
17 A.ii =1; A.jj=2;
18 incr.ii =10; incr.jj=21;
19 twovaUncrement(A, incr, err, B); //task is called
20 $display(B.ii, B.jj, err); II prints "11230" at time 47.
21 end
22 endmodule

In this example, task twovaUncrement (lines 2-12) does not have a return value and
assignments performed inside the task have an associated delay value (lines 9, 10). Other
than return value behaviors, tasks have similar properties as those defined for functions.

4.4.3 Selection Statements


SystemVerilog provides the following selection statements:
• If-else statements
• Case statements
• randcase statements
These constructs are described in the following subsections.

4.4.3.1 If-Else Statements


An if-else statement consists of a set of condition predicates and a conditional statement
associated with each condition predicate. if-else statements allow program flow to be
directed to the statement for the condition that evaluates to true. SystemVerilog allows key-
words priority and unique to be specified for an if-else statement. An if-else statement has
the following forms:
• Normal form (no keyword): if-else statements behave the same as if-else statements
in Verilog. Conditional predicates, in programming order, are evaluated and program
flow continues into the conditional statement of the first condition predicate that
evaluates to true.
• Priority: The first if statement if prepended with keyword priority. The if-else state-
ment behaves mostly similar to its normal form, with the exception that at least one
ofthe condition predicates in the if-else statement must evaluate to true, or the if-else
statement must have a final else clause. An error message is reported if this condition
is not satisfied.
• Unique: The first if statement if prepended \\'ith keyword unique. In this form,
exactly one condition predicate in the if-else statement must evaluate to true. This is
in contrast with other forms where any number of condition predicates may e\'aluate
to true. An error message is generated if this condition is not satisfied.
Procedural Statements and Blocks 89

Priority and unique forms of if-else statements are closely related to their hardware
implementation. The implementation of a priority if-else statement contains special priority
decoding hardware, while the hardware implementation of a unique if-else statement does
not require such special hardware, since at most one of the condition Predicates are true.
Examples of these forms of if-else statements are shown below:

,,Program 4.13: if-else statements

1 int a, b, c;
2
3 if (a == 1) begin
4 $display("a is one");
5 $display("and b is", b);
6 end
7 else if (a ==
2) $display("a is two");
8 else $display("a is neither 1 nor 2");
9
10 priority if (a ==
1) $display("a is 1");
11 else $display("a is not 1");
12
13 unique if (a ==
1) $display("a is 1");
14 else $display("a is not 1");

4.4.3.2 Case Statements


Case statements are similar to if-else statements in that they provide a mechanism for selec-
tively controlling program flow. Case statements are, however, more limited in functionality
since all case item expressions are compared to the same case expression. Limiting this
usage, however, allows a more structured approach for writing such selection statements.
Similar to if-else statements, case statements can be marked with unique and priority
keywords. The effect of these keywords are the same as those for if-else statements. The fol-
lowing program shows an example of a case statement:

,'Program 4.14: case statements

1 int a, b, c, d;
2 case (a**2) 1/ can be written as "priority case" or "unique case"
3 c,(b+33): $display("a**2 is the same as either c or b+33");
4 d+5: $display("a··2 is the same as d+5");
5 9: $display("a**2 is 9");
6 default: $display("a is none of the above");
7 endcase

Case expression for the above example is expression (a**2). Case item expressions are
(c,(b+33»), (d+S), and (9). Keyword default is used to specify a statement for conditions where
none of the case item expressions match against the case expression.
Each case item expression is matched bitwise against the case expression. This means
that a case item expression does not match if any of its bit values faiI4-\'alued (i.e .. 0, 1. x. :)
90 SystemVerilog as a Programming Language

comparison with the corresponding bit in the case expression. System Verilog also provides
special case statements easex and easez to relax the matching requirements. In a easez state-
ment, value z is assumed to be a don't care and is matched against any bit value. In a easex
statement, both x and z are assumed to be don't cares and match against any bit value.

4.4.3.3 Random Case Statements


A random case statement allows program flow to be randomly guided in different directions,
according to a probability distribution. The SystemVerilog randease construct is used to
specify a random case statement. A randease statement does not have a case expression or
any case item expressions. Instead, each item is labeled with an expression that produces an
unsigned integer value. During each execution of the randease statement, the labels for all
items are computed. The probability of each item is then computed as the value of the label
for that item divided by the sum of all labels. The following program shows an example of a
randease statement:

I Program 4.15: randcase statement


I

1 in! a, b, c, d; II assume a=4, b=2 in curren! pass


2 rand case 11------------------------------------
3 a+b: $display("now in a+b branch"); II label =6 prob=6/18
4 a-b: $display("now in a-b branch"); II label =2 prob=2/18
5 aOb: $display("now in aOb branch"); II label =8 prob=8/18
6 alb: $display("now in alb branch"); II label =2 prob=2/18
7 endcase 11--------------------------------------
8 IISum=18

Note that for each pass through the case statement, the probability for each case item
may be different depending on the current values for the variables used for evaluating case
labels. As such, constants should be used as labels if a static probability distribution is
needed.

4.4.4 Loop and Jump Statements


SystemVerilog provides the following looping and jumping statements:
• forever statement
• repeat statement
• while statement
• for statement
• do-while statement
• jump statement
• disable statement
Jump statements include return, break, and continue keywords, which are familiar lan-
guage constructs for returning from a function or task, and breaking or continuing from a
loop structure. A disable statement is used to finish the execution of a block of code and
move to the statement immediately after the block. If the block is not currently executing.
Module Hierarchy 91

then this statement has no effect. If the block is a loop body, the disable statement acts the
same as a continue statement.
Examples of loop constructs are shown in the following example:

:program 4.16: SystemVeriiog loop constructs


1 module top;
2 initial begin
3 fork
4 forever begin #1 $display ("in forever loop"); end
5 repeat (10) begin #1 $display("in repeat loop"); end
6 while (1==1) begin #1 $display("in while loop"); end
7 for (int i = 0; i <= 10; i++) begin #1 $display ("in for loop"); end
8 do begin #1 $display("in do-while loop"); end while (1 ==1);
9 join
10 end
11 endmodule

Note that a delay value is included before each display statement, so that all threads
started by the fork statement become idle until their next execution time, so that other threads
have the opportunity to execute.

4.4.5 Event Control Statements


Event control is provided by the following types of statements:
• Event trigger statements
• Wait statements
Trigger statements are used to activate an event while wait statements specified using
the SystemVerilog wait keyword, are used to wait on an event. The use of these statements
are described in detail in section 5.4.1.

4.5 Module Hierarchy


The expressive power of SystemVerilog in modeling an architecture is a major leap over
what could be achieved with Verilog. The bulk of this improvement is due to one reason:
SystemVerilog removes the fundamental restriction in Verilog that module ports be modeled
as nets (i.e., wires and buses). In doing so, SystemVerilog allows module ports to be a vari-
able data type as well as a net data type. This means that modules in System Verilog can cor-
respond to blocks at any level of abstraction, and not just blocks that communicate through
wires and buses. With this enhancement, SystemVerilog can be used not only to create RTL
descriptions of a design but also the architectural description of the product with blocks
whose ports communicate at higher levels of abstraction than wires and buses.
SystemVerilog maintains Verilog's module hierarchy modeling approach but improves
expressiveness and efficiency in describing a model by:
• Allowing a module port to be a \'ariable data type as well as a net data type
• Allowing one module declaration to be nested inside another module
92 SystemVerllog as a Programming Language

• Providing shorthand notations for specifying port connectivities


• Allowing module ports to be bundled as an abstract interface object
As mentioned, allowing variable data types as module ports is the main reason for the
improvement in SystemVerilog's modeling capabilities. This flexibility is provided in Sys-
temVerilog by allowing variable data types to be driven not only by other variables, but also
through a single continuous assignment (section 4.3.2). Keeping this property of variables in
mind will be very helpful in understanding port connectivity rules described later in this sec-
tion.
SystemVerilog allows module declarations to reside inside other modules. The motiva-
tion for this enhancement is to help better reflect the environment hierarchy when imple-
menting the module hierarchy. In Veri log, all modules are declared at the top level and
instantiated in their parent modules when necessary. In this approach, visual inspection of
declared modules does not provide any hints as to the actual module hierarchy. In System-
Verilog, if a module is instantiated only once in its parent module, then it can be declared
inside its parent module.
Shorthand notations when specifying port connectivities allow top level description~ of
modules with many thousands of ports to take a less daunting appearance. The use of more
expressive constructs reduces the size of design descriptions, in tum leading to a lowered
chance of mistakes in making such connections. Given the large number of design ports and
also the fact that in most cases, the same group of ports connect multiple modules (e.g., ports
corresponding to bus signals), a natural next step in optimizing port definitions is to bundle
them into a larger abstract object. SystemVerilog provides the interface construct to support
this enhancement (see section 4.5.2).
Modules are the basic building blocks in the SystemVeriiog language and the module
hierarchy of a SystemVerilog program defines the structural hierarchy of blocks and inter-
connections between these blocks. The following general properties hold for the module
hierarchy in a SystemVerilog program:
• Top level programs can contain either packages or modules.
• Packages contain declarations for data, data types, classes, tasks, and functions, and
can be imported to any program to make these declarations available to that program.
• All data, functions, and tasks are declared, instantiated, and used inside modules,
except system task and functions, which are global.
• Modules can contain instances of other modules.
• Instance hierarchy is the expansion of the module hierarchy where each module
instance is replaced by its own module hierarchy (note that if every module is used
only once, then module and instance hierarchies would be the same).
• Any uninstantiated module (except when in a library or another module) is assumed
to be at the top level of the module hierarchy. As such, the module hierarchy can have
multiple top levels.
• Hierarchical names can be used to specify any named object from anywhere in the
instance hierarchy (section 4.5.4).
Module Hierarchy 93

4.5.1 Modules and Module Ports


A module may contain the following blocks:
• Instances of other modules
• Data Objects
• Task and function declarations
• always, initial, and final blocks containing procedural statements
In addition, a module can be assigned one of automatic or static properties. All objects
declared inside an automatic module are by default assumed to have automatic semantics.
Similarly, all objects inside a static module are assumed by default to have static semantics.
Module ports can be one of:
• Net
• Interface
• Event
• Variable of any type including arrays
• A structure or union
In addition, module ports can have one of the following directions:
• input
• output
• inout
• ref
The following program shows an example of using modules in SystemVerilog:

'Program 4.17: Module declaration and instantiation


1 typedef struct packed {int ii: int jj:} twovaU:
2
3 module leaf (input twovaU tv, input wire w, output logic I, inout logic cc):
4 assign I = w:
5 =
assign cc cc + 1:
6 endmodule:
7
8 module top:
9 !woval t tv: II a composite data variable
10 wirew; II a net
11 logic I: II a variable
12 wire I_cc: II a variable
13
14 leaf leaU (.*,.cc(Lcc)):
15 IIvariations on instantiating module leaf
16 I/Ieaf leaC2 (.tv(tv),.w(w),.I(I),.cc(Lcc)):
17 IIleaf leaC3 (tv, w, I, Lcc):
18
19 endmodule

The above example shows a module declaration having ports of type input, output, and
inout (line 3). This module uses a composite data structure tv as an input port (line 3).
94 SystemVerllog as a Programming Language

4.5.1.1 Port Connection Syntax


Ports can be connected using one of the following approaches:
• Positional port connection
• Named port connection
• Implicitly connected ports
Program 4.17 in the previous section shows an example of each of these notations.
Implicit port connection is indicated by (. *) shorthand notation. This notation connects all
local variables that have the same name as module's fonnal port names. Any local name that
is not the same can be specified at the end of port map list (line 14). Lines 15 and 16 show
examples of named and positional port connections respectively.

4.5.1.2 Port Connection Rules


Port connection rules define and limit how a module port can be connected to variable and
net data objects. Port connection rules are defined separately for nets and variables. Connec-
tion rules specify how a instance port can be connected to the outside environment and how a
port is used or assigned inside the module.
Port connection rules are derived from the following properties of variable and net
assignments and the implicit continuous assignments that occur when ports are connected:
• A variable data type can be assigned only by one continuous assignment or multiple
procedural assignments.
• Net data types can be driven by multiple continuous assignments but not by proce-
dural statements.
• An outside connection to an input port is assumed to be driving the input port through
a continuous assignment.
• An outside connection to an output port is assumed to be driven by the output port
through a continuous assignment.
• The inside and outside connections for an inout port drive each other through contin-
uous assignments.
Connection rules for ports having a variable data type are as follows:
• An input port can be driven on the outside by any expression of compatible type
where the expression can be composed of variables or nets. An input port cannot be
assigned inside the module. If an input port is left unconnected, then its value is the
default value of its data type.
• An output port can be driven from inside the module by one continuous assignment
or multiple procedural assignments. An output port can be connected on the outside
to either a variable or a net data of compatible type. If the output port is connected to
a variable data type, then no other procedural or continuous assignments are allowed
for that external variable. If it is connected to a net, then other continuous assign-
ments are allowed to drive that net but procedural assignments are not allowed.
• An inout port of an instance can only be connected to net data types on the inside and
outside of the module. The reason for this requirement is that an inout port drives its
connections using a continuous assignment. Therefore, ifit is connected to a variable.
then that variable cannot be assigned by any other means.
Module Hierarchy 9S

• A ref port can only be connected to an equivalent variable data type both inside the
module and when the module is instantiated. A ref port cannot be left open. Access to
a ref port is equivalent to a hierarchical reference.
Connection rules for ports having a net data type are as follows:
• A input port can be driven on the outside by any expression of compatible type where
the expression can be composed of variables or nets. An input port can also be
assigned inside the module. If an input port is left unconnected, then its value is set to
Z (tristated).
• An output port can be driven from inside the module by multiple continuous assign-
ments. An output port can be connected on the outside to either a variable or a net
data of compatible type. Ifthe output port is connected On the outside to a variable
data type, then no other procedural or continuous assignments are allowed for that
variable. If it is connected to a net, then other continuous assignments are allowed for
that net, but procedural assignments are not allowed.
• An inout port can only be driven from inside the module by a net data type. An inout
port of an instance can only be connected to an actual net data type.

4.5.2 Interface Blocks


A major task in creating a design is defining and describing inter-block connections. In spite
of the required effort in describing such communication mechanisms, design description lan-
guages have provided little more than single structural ports for specifying this inter-block
connectivity. SystemVerilog addresses this shortcoming by introducing the inteiface con-
struct. This construct is primarily used for the following purposes:
• Modeling communication between blocks at an abstract level as well as at a structural
level (i.e., wires and buses).
• Allowing for ease of structural connectivity between blocks by providing a means to
bundle signals.
• Allowing smooth migration from system level designs down to RTL descriptions.
• Facilitating design reuse by hiding communication details inside the interface.
Figure 4.3 shows the communication between a simplified CPU and a memory core.
The following program sample shows one possible implementation of these blocks and
their interface:

'Program 4.18: Module connection without an interface block


1 module mem_core (
2 input logic wen, input logic ren,
3 output logic mrdy=1, input logic [7:0] addr,
4 input logic [7:0] mem_din, output logic [7:0] mem_dout,
5 output logic status, input logic clk);
6
7 logic [7:0] mem [7:0];
8
9 task replLread(input logic [7:0] data, integer delay):
10 #delay;
11 @(negedge elk);
12 mrdy = 1'bO;
13 mem_dout = data;
96 SystemVerllog as a Programming Language

cpu core t
I wired or ~
mem core

wen
~

....
ren

mrdll
-..
addr
~

...... wdata

rdata
clk
......
~

t
Figure 4.3 Interface between a Memory and a CPU

14. @ (neg edge elk);


15 mrdy=1'b1;
16 endtask
17
18 always @(negedge ren) reply-read(mem[addr],1 0);
19 endmodule
20
21 module cpu_core (
22 output logic wen=1, output logic ren=1 ,
23 input logic mrdy, output logic [7:0] addr=O,
24 input logic [7:0] cpu_din, output logic [7:0] cpu_dout,
25 output logic status=O, input logic elk);
26
27 task read_memory(input logic [7:0] raddr, output logic [7:0] data);
28 @(posedge clk);
29 ren = 1'bO;
30 addr = raddr;
31 @(negedge mrdy);
32 @(posedge clk);
33 =
data cpu_din;
34 ren = 1'b1;
35 endtask
36
37 initial begin
38 logic [7:0] read_data;
39 read_memory (7'b00010000, read_data);
40 $dlsplay("Read Result", $time, read_data);
41 end
42 end module
43
44 module top;
45 logic mrdy; logic wen;
46 logic ren; logic [7:0] addr;
47 logic [7:0] d1; logic [7:0] d2;
48 =
wor status; logic clk 0;
49
50 mem_core mem (.*,.mem_din(d1),.mem_dout(d2));
51 cpu_core cpu (.*,.cpu_din(d2),.cpu_dout(d1 ));
52
MOdule Hierarchy 97

53 initial for (inti = 0; i <= 255; i++) #1 elk =!elk;


54 end module

The following observations can be made about this example:


o Most port signals connecting cpu and mem blocks are generated in one block and
used in the other block, without being needed by other blocks outside of cpu and
memo These signals need not be visible anywhere but inside the cpu and memo
o Some signals are generated outside the two blocks (i.e., elk) and used inside both cpu
and memo
o Some signals are generated inside one or all the blocks but used outside these blocks
(e.g., status).
o Each block could be implemented either at the structural level or at the behavioral
level. A behavioral implementation will use methods or procedure calls to drive or
respond to its ports, a structural description will contain an actual RTL implementa-
tion. The example implementation above uses a procedural interface.
o Some signals have different meaning to different blocks. For example, a signal may
be an output from one block and an input to another block (i.e., mrdy is generated by
mem and used by cpu).

These observations suggest the following requirements for specifying an interface


object:
o Ability to bundle signals into an interface
o Ability to specify the direction of interaction between signals in the interface and
blocks attached by this interface as input, output, or inout. Such specification may be
different for different blocks attached by the interface object.
o Ability to inject signals into the interface (i.e., elk) and extract signals from inside the
interface (i.e., status)
o Ability to encapsulate behavioral descriptions inside the interface.
A graphical view of this interface is shown in figure 4.4.

cpu_core tstatus mem core


membus

~ ~ I
t clk
Figure 4.4 Interface between a Memory and a CPU

An interface block, or an interface for short, is implemented using the SystemVerilog


interface construct. An interface block in SystemVerilog can include:
o Parameters
• Constants
98 SystemVerllog as a Programming Language

• Variables
• Tasks and functions
• Processes (always, initial blocks)
• Continuous assignments
A port list can be defined for an interface block. An interface can be instantiated inside
other interfaces (leading to hierarchical interfaces) and other modules. Modules cannot be
instantiated inside interfaces.
Interfaces can contain tasks and functions thereby, allowing an interface to be modeled
at an abstract level. This also allows for protocol checking routines to be included in the
interface descriptions.
Both the declaration and instances of interfaces can be customized for different con-
texts. Parameters allow different instances ofthe same interface declaration to be customized
When an instance is being created. The declaration of an interface can be customized by
declaring only the function or task prototypes (the list of arguments) in an interface and
defining the body in other modules. A module header can use a genetic interface when the
interface contents is not yet defined, allowing module implementations to proceed without
prior exact knowledge of the interface contents or composition of its signals.
The following SystemVeri log program shows the implementation of an interface block
in SystemVerilog:

'Program 4.19: Module connections with an interface block


1 interface membus (input logic clk, output wor status);
2 logic mrdy;
3 logic wen;
4 logic ren;
5 logic [7:0] addr;
6 logic [7:0] c2m_data;
7 logic [7:0] m2c_data;
8
9 task reply-read(input logic [7:0] data, integer delay);
10 #delay;
11 @(negedge clk)
12 mrdy = 1'bO;
13 m2c data = data;
14 @(negedge clk);
15 mrdy= 1'b1;
16 endtask
17
18 task read_memory(input logic [7:0] raddr, output logic [7:0] data);
19 @(posedge elk);
20 ren = 1'bO;
21 addr = raddr;
22 @(negedge mrdy);
23 @(posedge clk);
24 data = m2c_data;
25 ren = 1'b1;
26 endtask
27
28 mod port master (output wen, ren, addr, c2m_data, input mrdy, m2c_data);
29 modport slave (input wen. ren, addr, c2m_data, output mrdy, m2c_data);
30 end interface: membus
31
32 module mem_core (membus.slave mb);
33 logic [7:0] mem [7:0];
Module Hierarchy 99

34
35 assign mb.status = 0;
36
37 always @(negedge mb.ren) mb.reply-read(mem[mb.addr], 100);
38 end module
39
40 module cpu_core (membus.master mb);
41
42 assign mb.status = 0;
43
44 initial begin
45 logic [7:0] read_data;
46 mb.read_memory (7'b00010000, read_data);
47 $display("Read Result", $time, read_data);
48 end
49 endmodule
50
51 module top;
52 wor status;
53 logic clk = 0;
54 membus mb(clk, status);
55
56 mem_core mem (.mb(mb.slave));
57 cpu_core cpu (.mb(mb.slave));
58
59 initial for (int i = 0; i <= 255; i++) #1 clk =!clk;
60 end module

This example defines interface block membus which has two ports corresponding to sig-
nals elk and status (line 1). Interface block membus includes a list of data objects that corre-
sponds to signals passing through the interface and connecting different modules (lines 2-7).
This interface also includes procedural descriptions, allowing each module attached to it to
drive the interface signals through procedure calls (lines 9-26). In addition, membus includes
modport declarations specifying the port directions for different types of modules that can be
connected by this interface (lines 28, 29). These modport types are then used when declaring
the port list of modules that will be connected by this interface (lines 32, 40) and also when
connecting instances of modules and interfaces (lines 56, 57). Note that modules connecting
to this interface can use the tasks provided inside the interface to interact with the interface as
shown in this example (lines 37, 46). These modules can also directly drive the signals inside
the interface (lines 35, 42). This feature of the interface allows it to be driven both with struc-
tural and procedural modules.

4.5.3 Parameters
Often, it is necessary to customize different instances of the same object in a program's
object hierarchy (i.e., memory size inside a memory module). SystemVerilog provides the
following keywords for this purpose:
• parameter
• iocaiparam
• specparam
Parameters are used to perform customizations for the following System Verilog blocks
and constructs:
100 SystemVeriiog as a ProgrammIng Language

• Module
• Interface
• Program
• Class
Parameters have the following properties:
Each parameter must be assigned a default value when declared.
Parameters can be defined to have any data type. Parameters defined with no data
type default to type logic of arbitrary size.
Parameters are set during elaboration (creating the instance hierarchy) and remain the
same throughout the program execution.
Data types can also be defined as parameters so for example data objects in different
instances of the same module declaration can have different data types.
A parameter of integer data type can be assigned a value of"$".

4.5.4 Hierarchical Names


Hierarchical names allow objects to be referenced for reading or writing. Hierarchical names
are also referred to as nested identifiers.
A hierarchical name consists of object names separated by periods. Object names fol-
low these guidelines:
• The top of the program hierarchy is referred to as $root.
• A module object's name is the instance name used for instantiating that module.
• SystemVerilog syntax allows procedural blocks (e.g., initial, always blocks) to be
labeled. This label is used as the name for that procedural block.
• The name of a task or function is used to refer to that task or function.
• Data object names are used in accessing data objects. Arrays member names are
formed by using the array name and an index.
A hierarchical name provides an absolute path for accessing any object from any place
in the hierarchy. It is, however, best to limit the use of such hierarchical names as it bypasses
the modular programming style that should be followed while programming in SystemVer-
ilog.

4.6 Processes and Execution Threads


SystemVeriiog execution threads fall into two categories:
• Static processes
• Dynamic processes

Start and end time of static processes are determined at program start time. Creation of
dynamic processes and their termination conditions can be decided during program runtime.
Static and d~llamic processes are described in the fOllowing subsections.
Object-Oriented Programming and Classes 101

4.6.1 Static Processes


In SystemVerilog, slatic processes are created for each of the following constructs:
• initial block
• final block
• always block and its variations (e.g., always_comb)
• Continuous assignments
• fork-join statements
An initial block is always started at the beginning of the program execution. A final
block is always started before program termination and after all execution threads have ter-
minated. An always block and its variation always_comb block are started every time their
sensitivity conditions are satisfied and if the previous start of the thread has already termi-
nated (e.g., execution of an always block may be suspended midstream because of a wait on
an event). Continuous assignments are similar to always blocks where any change in the sig-
nals on the right-hand side of the assignment causes a new evaluation and assignment to the
left-hand side to take place. A new thread is started for each block in afork-join statement
once the fork-join statement is reached. The fork-join statement completes only after all
threads started by the fork statement have been terminated.

4.6.2 Dynamic Processes


In SystemVerilog, dynamic processes are created using the following constructs:
• fork-join_ any
• fork-join_ none
These fork statements are similar to the static fork-join statement. However, in a
fork-join_any statement, the parent process that contained the fork statement continues exe-
cution after anyone of the processes spawned by the fork statement completes. In a
fork-join_none statement, the parent process continues execution without waiting for any of
the processes spawned by the fork statement to complete. In case of afork-join_none state-
ment, the spawned processes do not start executing until the parent is suspended or com-
pletes.
fork-join_any andfork-join_none are considered dynamic processes since they lead to
new processes to be created in addition to the parent process that contained the fork state-
ment.

4.7 Object-Oriented Programming and Classes


..---.---.---.......... - -_._-.- ...,.- ....__ .. _--"

Object-oriented programming provides a programmer with the following benefits:


• Grouping data objects into more complex data structures
• Detining and grouping the operations that can be performed on these data objects
• Ability to hide the contents of an object from outside (other programmers) observa-
102 SystemVeriiog as a Programming Language

tion or modification, thereby providing predictable ways for the way data inside an
object can be modified
• Ability to handle objects of different types (i.e., different data structures) uniformly
(see section 4.7.4 on polymorphism).
• Ability to better deal with complex systems by modeling system components as sep-
arate objects.
SystemVerilog provides the class construct for building objects based on the object-ori-
ented programming paradigm. The main flow in object-oriented programming is as follows:
1. Create a class containing properties (i.e., data objects) and methods (functions and
tasks).
2. Optionally, create an extension of a previously defined class. This step will either
define new properties and methods, or redefine the previous definition of the class
that it extends.
3. Declare pointers that hold the address for instances of classes defined in the previous
step.
4. Create instances whose address are stored in pointers declared in step 3.
Figure 4.5 shows a graphical view of this process. In this example, first a base class is
defined. Classes Ch C2, and C3 are defined by extending class Co, and class C4 is defined by
extending class C3' Variables i1-i 10 are pointers to classes of types C1-C4, where each pointer
points at a memory location containing an object instance of that type. Note that a pointer
can have value null.

o Class Definition -t>- Inheritance


V Class Pointer ---.. Pointer Declaration
I 0 Class Instance -----;;.. Instance Creation

Figure 4.5 Class Inheritance and Instantiation Hierarchy


Object-Oriented Programming and Classes 103

The following SystemVerilog program shows an example of how classes, derived


classes, object pointers, and instances are created:

'Program 4.20: Examples of class declaration and creation


1 module top:
2 class packet:
3 integer data1:
4 bit flag:
5
6 task my_printO:
7 $display("Packet: ",data1):
8 endtask
9 endclass: packet
10
11 class bigger_packet extends packet;
12 integer data2;
13 byte flag;
14
15 task mLprintO;
16 $display("Bigger Packet: ",data1, data2):
17 endtask
18 end class: bigger_packet
19
20 packet p1, p2:
21 bigger_packet b1, b2, b3:
22
23 initial begin
24 p1 = new;
25 p1.data1 = 10;
26 p2=p1:
27 p1.my_printO:
28
29 b1 = new;
30 b1.data1 = 10:
31 b1.data2 = 20;
32 b2 = b1;
33 b3 = new b1;
34 b1.my_printO:
35 b1 = null;
36 b2 = null;
37 b3 = null;
38 end
39
40 end module

In this example, class packet is created by declaring a class and its contents (lines 2-9).
Class packet is then extended to declare a new class bigger_packet which includes a new data
item (data2) and to redefine the definition for data item flag and method my-printO (lines
11-18). In this example packet is the base class or super-class and bigger_packet is the sub-
class or derived class.
Pointers P1 and P2 to class packet are declared on line 20. Pointers b h b2 , and b3 to class
bigger_packet are declared on line 21.
The SystemVerilog new construct is used to create an instance of a class and to assign it
to a pointer. A new instance of class packet is assigned to P1 on line 24. Pointer P2 is pointed
to the same object that P1 is pointing to on line 26. Note that a new instance is not created, but
rather both P1 and P2 point at the same instance.
104 SystemVeriiog as a Programming Language

Line 29 creates a new instance of bigger_packet class and sets b1 to point to that instance.
Pointer b3 is set to point at the same instance as the one pointed to by b1' A new instance of
blgger.J)acket class is created on line 33 and contents of b1 are copied into this new instance.
Note that this is a shallow copy where the class objects pointed to by members of instance b 1
are not duplicated and only their pointer is copied.
The memory used for an instance of a class object is freed when no pointers point at that
object. This memory reclamation is handled automatically through garbage collection mech-
anisms built into the program execution engine and as such, memory leak issues are not a
concern when programming in SystemVerilog. In this example, the object created on line 29
is freed after executing line 36 since both pointers pointing to it have been set to a different
value. Similarly, the memory associated with the object created on line 33 is freed after exe-
cuting line 33 where the only pointer pointing to it is set to a different value.

4.7.1 Static Properties and Methods


Every instance of a class object creates dedicated memory for holding its data members and
allY values that must be maintained for its methods (i.e., static variables inside a method).
Now consider a class description where one of its data members never changes and retains its
value throughout the program execution, or a method that operates only on values that never
change. It would be a waste of programming resources to create this never-changing data
member or method for every instance. It is, therefore, helpful to specially tag such data mem-
bers and methods so that they are created as part of elaborating the class declaration and not
for every class instance. An additional advantage of this enhancement is that these properties
and methods become accessible before any instances of the class are created, because their
detinition is bound to the class declaration, which is available at the beginning of the pro-
gram when the program source is loaded into the execution engine.
SystemVeriiog provides the keyword static to mark such data members and methods.
Note that this keyword appears in the beginning of the declaration for such properties and
methods and as such, is different from the static property assigned to a method indicating that
all variables inside a method by default have a static lifetime.
The following program code shows an example of this concept:
r: .
.Program 4.21: Class slatlc data members and methods
1 module top;
2 class packet;
3 stalic integer dala 1 = 10;
4 bit flag;
5
6 Sialic task slatic my_print(lime t);
7 time prev_l;
8 $display("Packet: ",I, prev_l, dala1);
9 prev_t = t:
10 endtask
11 endclass: packet
12
13 packet p:
14
15 initial begin
16 , $display(p,datal): II p=null at this time
17 #1 p,my_print(Stime): IIprints 1 x 10
Object-Oriented Programming and Classes 105

18 #4 p.my-print($time); IIprints 5 1 10
19 #8 p.my-print($time); IIprints 13 5 10
20 end
21 endmodule

In this example, property data1 of class packet is defined to be static. This means that
this property can be accessed even without creating an instance and through a null pointer
(line 16). In addition, method my_printO of class packet is declared to be a static method (first
static keyword on line 6). Static methods can access only static properties of a class, as is the
case for mLprintO method accessing data 1 property of class packet. Note that variables inside
method my_printO are also declared to have static lifetime by default, as indicated by the sec-
ond use of keyword static on line 6.

4.7.2 Class Hierarchy, Inheritance, and Abstract Classes


The ability to model a number of similar behaviors as derived classes of a base class provides
a powerful mechanism for efficiently modeling complex behaviors. In using this modeling
paradigm, however, a number of recurring requirements can be identified. These require-
ments include:
• In some cases, there is a need to not only extend a base class with a new property or
method, but also to redefine a property or method from the base class in the derived
class. Consider an example where most variations of a packet have the same CRC
size except one variation. In this case, it is best to define the CRC size in the base
class and then redefine the size in the subclass corresponding to packet fonnat with a
different CRC. In System Verilog, both data member and method definitions can be
completely redefined in a derived class. Special keywords can be used to control the
flexibility provided in allowing this type of redefinition.
• In cases where a data member or a method in a parent class is redefined, it may be
necessary to access that data member or method as defined in the parent class. Sys-
tern Veri log provides the super keyword to support this requirement.
• Sometimes the base class for a group of objects is not a complete definition and as
such it is not meant to be instantiated. System Veri log allows such classes to be
marked as abstract classes by marking them with the keyword virtual.
• When defining a base class, the body of methods contained in that class may not be
known until further development. In other cases, it may be helpful to place class dec-
larations, including their method prototypesS and body definitions in separate files.
System Verilog provides allows keyword extern to be used to mark such methods.
• Base class methods and their prototypes can be completely redefined in a derived
class. This means that a method in a derived class can have a completely different
return type and list of arguments than the one declared in the base class. At times, it
may be needed to enforce the same method prototype for all new definitions of a base
class method in deri\'ed classes. In such cases, only the body of methods can be rede-
fined in deri\'ed classes. System Veri log allows such methods to be marked as virtual
methods.

A method prNl)type r~fers to its return type. name. list of arguments and each argument type.
106 SystemVeriiog as a Programming Language

These special features for managing the definition of a class hierarchy are described in
the following subsections.

4.7.2.1 Parent Class Scope: Super


SystemVeri log provides the super keyword to access parent class data members and methods
that have been redefined in a derived class. This keyword can be applied to only the immedi-
ate parent class of a derived class. Use ofthis keyword is shown in the following example:

'Program 4.22: Derived classes and super keyword


1 module top;
2 class delaLclass;
3 integer delay = 5;
4 function integer get_delayO;
5 geCdelay = delay· delay;
6 endfunction
7 endclass: delay_class
8
9 class delay2_class extends delaLclass;
10 Integer delay= 10;
11 function integer geCdelayO;
12 geCdelay = delay·delay + super.delay;
13 endfunction
14 endclass: delay2_class
15
16 delay2_class d;
17
18 initial begin
19 d = new;
20 $display(d.geCdelayO);
21 end
22 : endmodule
~---------------------
In this example, both data member delay and method get_delay() of parent class
delay_classhave been redefined in derived class delay2_class. Keyword super is used in
method geCdelay() of delay2_class to access delay data member of class delay_class (line 12).

4.7.2.2 Abstract Classes


SystemVeriiog provides the keyword virtual to mark classes that can be used only as a base
class for derived classes and cannot be instantiated directly. Classes marked as virtual are
known as abstract classes. An example of an abstract class is shown in the following pro-
gram:

'Program 4.23: Virtual Classes


1 module top;
2 virtual class base_packet;
3 integer data 1;
4 function void size(); /I body block is empty
5 endfunction
6 endclass: base_packet
7
8 class packet extends base_packet:
9 byte crc:
10 function integer size(int bitsJn_word):
11 size = ($bits(data1) + $bits(crc))/bits_in_word:
Object·Orlented Programming and Classes 107

12 end function
13 end class: packet
14
15 packet pp;
16 initial begin
17 pp = new;
18 $display(pp.size(4));
19 end
20 endmodule

In this example, base class base_packet is defined as a virtual class. It includes data
member data1 and method sizeO. Class packet is derived from class base_packet and adds a
new data member crc and completely redefines the definition for methods sizeO. A pointer to
class packet is declared on line 15 and an instance is created on line 17. An attempt to use
base_packet instead of packet on line 15 would lead to a compilation error.

4.7.2.3 Virtual Methods


As shown previously, both the body and the prototype of a base class method can be com-
pletely redefined in a derived class. Often, however, it is necessary to enforce the same
method prototype6 across all derived classes of a class declaration. SystemVerilog provides
the keyword virtual to mark such methods. Virtual methods and abstract classes are not
related. Virtual methods can be defined in both abstract and non-abstract classes.
The redefinition of a virtual method in a derived class must use the same prototype as
the one in its parent class if and only if that method is marked as virtual in any of its direct or
indirect parent classes. This means that using the same method prototype can be enforced
starting from anywhere in the class hierarchy and not necessarily from the root of that class
hierarchy. The following program shows an example of a virtual method declarations:

'Program 4.24: extern methods


1 module top;
2 class base_packet;
3 function void sendO; $display("no sendO function defined"); endfunction
4 endclass: base_packet
5
6 class derived-packet extends base_packet;
7 virtual function integer send(byte data); $display(data+1); endfunction
8 endclass: derived_packet
9
10 class derived_packet1 extends derived_packet;
11 function integer send(byte data); $display(data+2); endfunc!ion
12 endclass: derived_packet1
13
14 class derived _packet2 extends derived _packet1 ;
15 virtual function integer send(byte data); $display(data+3); endfunction
16 endclass: derived_packet2
17 end module

In this example. class base_packet contains a generic declaration of function sendO. This
function is not marked as virtual and. therefore. the redefinitions of this function in any

6. Method prototl'pl! oc method siglll.Jtw·" is gi\'~n hy the method name. return type if any, and argument
name(sl. directionls). and typeis),
108 SystemVeriiog as a Programming Language

derived class may have a different function prototype. Function sendO is redefined in derived
class derived_packet with a different function prototype and as a virtual function. The redefi-
nition of function sendO in any class derived either directly or indirectly from class
derived_class must have the same function prototype as the one in class derived_class. This is
shown in the redefinitions of this function in derived classes derived_packet1 and
derlved_packet2. Using keyword virtual for function redefinition in class derived_packet2 (line
15) is redundant since all function redefinitions in all derived classes of derived_packet must
use its prototype definition of function sendO.

4.7.2.4 Out of Block Declarations


At times, it is useful to declare the prototype for a class method and define its body outside
class body definition. This features allows the body of a method to be defined in a separate
file from where the class declaration is placed.
System Verilog provides the extern keyword for marking methods whose body is
defined outside the class block declaration. The method prototype for method declaration
inside the class and where method body is declared must be exactly the same.

'Program 4.25: extern methods


1 module top;
2 class packet;
3 integer data1;
4 byte crc;
5 extern function integer size(int bitsJn_word);
6 endclass: packet
7
8 function integer packet::size(int bitsJn_word);
9 1/ body not shown
10 end function
11 : endmodule
~--------------------

In this example, function sizeO is marked as an extern function in class packet (line 5).
The body of this function is defined later outside the class block (lines 8-10). Class scope
resolution operator "::" is used to identify the class containing the original method prototype
declaration.

4.7.3 Parameterized Classes


Parameterized classes use a set of parameters to create a customized declaration ofa generi-
cally defined class declaration. The use of parameters in defining classes is similar to using
parameters in defining a module block to customize the generic description of a module to
the actual requirements of its instances. A customization of a parameterized class with actual
values for its parameters is referred to as a class specialization.
The following example show the use of parameterized classes to define class objects
containing different types and sizes of tie Ids using the same class declaration:

'program 4.26: Parameterized class declaration and instantiation


1 module lest;
2 typedef bit [11:01 bits12;
3
ObJect·Oriented Programming and Classes 109

4 class pkt #(type T=byte. int 8=10);


5 T payload[8];
6 end class
7
8 class double_pkt #(type T1 =byte. type T2=bit. int 8=17) extends pkt #(T1. 8);
9 pkt #(T2. 8+12) pkt2;
10 end class
11
12 pkt #(bit. 12) pkt1 ;
13 pkt #(int) pkt2;
14 double_pkt#(real. bits12)dpkt;
15 double_pkt #(integer. integer) dpkt1;
16 endmodule

In this example, class pkt shows a simple example of using parameters to define a
packet whose fields can be customized to be of different type and size. This class defines
parameters T and s, each with a default value. Specialization "pkt #(bit,12)" (line 12) defines a
class that contains an bit array of size 12. Specialization "pkt #(int)", using the default value
for parameter 5, defines a class that contains an int array of size 10.
Parameterized classes can be extended. This feature is shown in the definition of class
double_pkt (lines 8-10). This class defines parameters T 10 T 2, and s. It uses parameters T 1 and
s to specialize the base packet it is extending, and it uses parameters T2 and 5 to define field
pkt2 whose type is class pkt specialized with parameters T2 and s.

4.7.4 Polymorphism: Uniform Access to Diverse Objects


In software programming, polYmorphism refers to the ability of one variable to hold data
objects of different types. A direct consequence of this flexibility is that objects of different
types can be passed to the same argument of a method (i.e., task or function), in tum allow-
ing a method to be generically used for a variety of data objects of different types. In this
approach, the exact handling of each object type by that method is defined when a decision is
made to support a new data type. Polymorphism provides great flexibility in both creating a
program when details are not yet known, and to also extend a program at a later time to sup-
port new data types.
In System Veri log, polymorphism is defined within the scope of a class hierarchy. Con-
sider a virtual base class B with a virtual method MO, a pointer P defined to have class type B,
and a derived class D with data member V and a redefinition of method MO. In System Verilog,
it is possible for pointer P to point to an instance of class D. In this case, pointer P behaves as
a pointer to class D, where P.MO refers to method MO defined in class D. and P.v refers to data
member V declared in class D. Generally, this feature allows pointer P to point to any class
derived directly or indirectly from class B, thereby providing polymorphism within the scope
of the class hierarchy rooted at class B. This concept is shown in the following example pro-
gram:

'Program 4.27: Polymorphism and class object pointers


1 module top:
2 virtual class B:
3 virtual task M(): endtask
4 endclass: B
5
6 class D1 extends B
110 SystemVeriiog as a Programming Language

7 task M(); $display("in class 01 "); endtask


8 endclass: 01
9
10 class 02 extends B;
11 task MO; $display("in class 02"); endtask
12 end class: 02
13
14 B B_ptr [1 :01;
15 01 01_ptr;
16 0202_ptr;
17
18 initial begin
19 01_ptr new; =
20 D2..J>tr = new;
21 =
B..J>tr[O] D2_ptr;
22 B_ptr[11 = 01..J>tr;
23 =
for (int i 0; i < 2; i++) " th is loop produces: in class D2
24 B_ptr[i].MO; /I in class 01
25 end
26 endmodule

In this program, pointer B_ptr is a two element unpacked array of pointers to class
objects of type B. Because of polymorphism, however, B_ptr can be used to point to class
objects of type 01 and 02 since these classes are derived from B (lines 21, 22). When access-
ing these objects, B_Ptr[O] will behave as if it was a pointer to 02, and B_ptr [1] will behave as
ifit was a pointer to 01' As shown in this example, this type of polymorphism allows an array
of pointers to hold objects of diverse types.

4.7.5 Data Hiding


Hiding data members inside a class and allowing their value to be changed or viewed only
through well defined methods also defined in that object leads to fewer programming errors
compared to where object data members can be changed in an ad hoc manner. SystemVerilog
provides the keywords local and protected to hide object data members. Properties marked
as local can be read and modified only by methods defined in the same class. Properties
marked as protected can be read and modified by methods defined in that and all classes
derived directly or indirectly from the class containing that property. The following program
segment shows this concept:

'Program 4.28: Local and protected class data members


1 module top;
2 class base_packet;
3 local byte partial_crc;
4 protected byte crc;
5
6 function void compute_partiaLcrc();
7 =
partial_crc 10; " replace this with actual crc calculation
8 endfunction
9
10 function byte getJjartial_crc();
11 geLpartial_crc = partial crc:
12 endfunction -
13 endclass: base_packet
14
15 class packet extends base_packet;
16 function void compute_crcO:
17 crc = 4: II replace this with actual crc calculation
Object-Oriented Programming and Classes 111

18 endfunction
19
20 function byte geCcrcO;
21 compute_partiaLcrcO;
22 compute_crcO;
23 =
get_crc crc + getJ)artial_crcO;
24 endfunction
25 endclass: packet
26
27 packet p;
28
29 initial begin
30 =
p new;
31 $display(p.get_crc(»;
32 end
33 end module

In this example, property partial_crc is only visible and editable inside the base class
base_packet. As such, methods computeJ)artial_crc() and geCpartiaLcrc() are provided for
updating and accessing this data value. crc is visible inside the derived class packet but not
outside this class. As such, methods compute_crc() and geCcrc() are provided in class packet
for accessing the combined crc value. Neither partial_crc nor crc can be accessed outside
these class definitions.
112 SystemVeriiog as a Programming Language
CHAPTERS System Verilog as a
Verification Language

SystemVeriiog provides major enhancements over Verilog. These enhancements can be


divided into two categories: I) features that improve the programming paradigm of the lan-
guage (e.g., complex data types, object-oriented programming, etc.), and 2) features that
improve verification quality. The programming paradigm of SystemVerilog is described in
chapter 4. This chapter focuses on verification related constructs and considerations of Sys-
temVeri log.

Verification related enhancements in SystemVerilog include:


Enhancing the scheduling semantics of the language beyond Verilog
Facilitating cycle-based verification semantics through clocking blocks
Promoting separation oftestbench and design by introducing the program block
Enhancing inter-process synchronization and communication mechanisms
Constrained random generation features
Native property specification constructs and assertion evaluation
Coverage collection utilities

The scheduling semantics of System Verilog are extended in order to allow for schedul-
ing of new language constructs (e.g., property evaluations) while maintaining backward
compatibility with Verilog. Cycle-based verification approaches are facilitated through the
introduction of clocking blocks that allow for automation of sampling and driving of design
signals with respect to sampling clocks. Separation between testbench and design is facili-
tated by the introduction of program blocks. Inter-process synchronization is enhanced
through the introduction of mailboxes and semaphores, thereby simplifying the interaction
between independently running processes. SystemVerilog also provides new constructs for
constrained random generation, property and assertion specification, and coverage collec-
tion.

Constrained random generation features of SystemVerilog are described in detail in


chapter 10. Property specification and evaluation is described in chapter 15. Coverage col-
lection syntax and semantics are described in chapter 17. This chapter describes the
enhanced scheduling semantics in System Veri log (section 5.1 ). the clocking block (section
5.2), the program hlock (section 5.3), and inter-process communication and synchronization
constructs (section 5.4).
114 SystemVerllog as a Verification Language

The scheduling semantics in System Veri log is fully backward compatible with the schedul-
ing semantics of Verilog. However, these semantics have been extended to provide support
for the new constructs in SystemVeriiog (e.g., program block, sequence evaluation). A good
understanding of the scheduling semantics of System Verilog facilitates better understanding
of the detailed operation of these new language constructs, and will help in avoiding poten-
tial programming pitfalls.
The semantics of SystemVerilog have been defined for event-driven simulation. A pro-
gram consists of threads of execution (e.g., process blocks, concurrent statements, forked
processes) that assign new values to data objects. A thread of execution is first started either
statically at simulation start time or dynamically by using fork-join statements (section 4.6).
A thread of execution is suspended either when its execution reaches the end of a block or by
an explicit wait for an event or delay period (e.g., "@rcv_pkt;" waits until event rcv_pkt is
triggered, "#3"s;" waits for 3 ns). The evaluation of a sleeping thread of execution is restarted
either when an explicit sleep time in its sequential flow has passed or when the conditions for
a wait statement are satisfied. A new evaluation thread is started either statically (e.g., initial
block), upon changes in any of the signals in a sensitivity list (e.g., always block), or through
fork-join statements. In this flow, time is advanced when restart times of all threads that are
scheduled to be restarted are at a time in the future. The next simulation time, is consequently
the next immediate time at which an execution thread restart is scheduled.
Each thread of execution evaluates its expressions by accessing values of data objects
used in expressions and updating data objects that appear on the left-hand side of these
expressions. Data values can be updated either with blocking assignments or non-blocking
assignments. A blocking assignments takes effect (i.e., the result of evaluating the expression
on the right-hand side of the assignment is moved into the data object on the left-hand side of
assignment) immediately during the running of its thread of execution, and as that assign-
ment is being carried out, whereas a non-blocking assignment takes effect only in the NBA
scheduling region (figure 5.1). In other words, the result of evaluating the expression on the
right-hand side of a non-blocking assignment is not moved into the left-hand side data object
until its thread of execution is suspended. As such, the value of a variable assigned using a
blocking assignment is immediately visible within the thread of execution after the assign-
ment is made, but the value of a variable assigned using a non-blocking assignment becomes
visible only after its execution thread is suspended.
A time-slot at a given simulation time Ts is the abstract unit of time within which all
thread restarts and data object updates for time Ts take place. In SystemVerilog, thread
restarts are given different priorities depending on the language construct that created the
thread (e.g., always block, assertion action block, etc.). In addition, data object updates are
grouped according to their update mechanisms (i.e., blocking vs. non-blocking). The sched-
uling semantics of SYstem Verilog uses the concept of scheduling regions to describe the
mechanism and ordering of thread restarts and data object updates within a given time-slot.
Figure 5.1 shows the System Verilog simulation reference model and its predefined schedul-
ing regions.
Scheduling Semantics 115

... time-slot ----I~


..

from previous
time-slot

Iterative
Region

to next
time-slot

Figure 5.1 SystemVerilog Time-Slot and Scheduling Regions

Scheduling regions in this flow consist of preponed, active, inactive, NBA (non-block-
ing assignment), observed, reactive, and postponed. Except for observed and reactive
regions, this flow essentially duplicates the standard simulation reference model in Verilog.
The preponed region is specifically used as a PLI callback control point that allows PLI rou-
tines to access data at the current time-slot before any net or variable values are changed.

5.1.1 Active Region


At the start of the current time-slot, the active region holds thread evaluation restarts sched-
uled for the current time-slot in any of the previous time-slots. Upon reaching the active
region, thread evaluations listed in this region are started. During each thread evaluation, any
non-blocking assignment is scheduled to be carried out in the NBA region of the current
time-slot. In addition, any "#0" delay causes the running thread to be suspended and sched-
uled for restart in the inactive region of the current time-slot.

5.1.2 Inactive Region


The inactive region holds all threads that are restarted in the active region of the current
time-slot and executed a "#0" delay statement after being restarted. The reason for placing
all such threads in the inactive region is that for all threads that are restarted in the current
time-slot, all statements before any "#0" delay statement must be executed before any of
116 SystemVerilog as a Verification Language

these thread evaluations progress beyond the "#0" statement. Once all threads that are
restarted in the active region are suspended, all threads scheduled for restart in the inactive
region are moved to the active region, and iteration repeats by restarting the flow in the
active region.

5.1.3 NBA Region


The NBA region is reached when no threads are scheduled for execution in the active or
inactive regions. The NBA region holds data objects that were assigned using non-blocking
assignments in threads executed so far in the active and inactive regions of the current
time-slot. Upon reaching this region, all such assignments are carried out and threads sensi-
tized to changes in these data objects are scheduled to be restarted. If any thread is scheduled
to be restarted in the same time-slot, then that thread is scheduled for restart in the active
region of the current time-slot. The scheduling of threads for restart in the current time-slot
causes another iteration of the current time-slot to restart from the active region.

5.1.4 Observed Region


The observed region is reached when none of the assignments made in the NBA region result
in scheduling a thread restart in the active region of the current time-slot. The observed
region is introduced specifically for handling clocked assertions. Evaluating a clocked asser-
tion requires that the clocking expression be evaluated, followed by its property expression
evaluation if a clock trigger is detected (section 15.4). The values used for computing the
property expression in a clocked assertion are sampled in the preponed region of the current
time-slot. This means that any changes of property expression variables in the current
time-slot are not visible to its evaluation in the current time-slot. The clocking expression of
a clocked assertion is, however, evaluated using the current values of variables in its expres-
sion. The required behavior is that a clocking condition for an assertion should only be eval-
uated using stable values of signals in the current time-slot, after all signal activity in the
current time-slot is completed and every data object has been assigned its final value. The
reason for this requirement is that using non-stable variable values for evaluating a clocking
expression may lead to false detection of a clock trigger. The observed region is introduced
specifically to allow for this behavior where a clocking expression is evaluated only after all
variable assignments for the current time-slot have been finalized.

5.1.5 Reactive Region


The reactive region is used for evaluating the pass/fail code attached to assertions. Assign-
ments made to nets and variables in the reactive region can potentially lead to new thread
evaluations to be scheduled in the active region of the current time-slot. This means that
another iteration of the current time-slot starting at the active region may again be needed.
Note that even if data objects used for evaluating the property expression of a clocked asser-
tion are changed in this next pass through the current time-slot. these changes "'ill not be \"is-
ible to property evaluation, since values sampled in the preponed region are lIsed for
evaluating property expressions. The property will. ho,,·ever. be ree\'aluated if its clocking
event is triggered. As such, it is important to take special care that assignments made in the
Clocking Blocks 117

pass/fail segment of a clocked assertion do not lead to triggering its clocking expression in
the current time-slot.

5.1.6 Postponed Region


The postponed region is specifically for a PLI callback control point that allows user code to
get suspended until all active, inactive, and NBA assignments have been completed. Signal
values in the postponed region of a time-slot are exactly the same as those in the preponed
region of the next time-slot. The lstep time delay provided by SystemVerilog is a conceptual
mechanism for sampling signal values during the postponed region of the previous time-slot.
This sampling effectively means that stable values of the signal are sampled before the cur-
rent time-slot is entered. Sampling in the postponed region of the previous time-slot is effec-
tively the same as sampling in the preponed region of the current time-slot.

5.2 Clocking Blocks


----------- -------

Interaction with a DUV for verification purposes often follows cycle-based semantics where
DUV signals are sampled and driven based on a clocking event. For verification purposes, it
is usually a good practice to take advantage of the setup and hold time specifications of a
design interface by driving DUV inputs slightly before a sampling event, and sampling out-
puts slightly after a clocking event in order to bypass any simulation-induced, spurious sig-
nal transitions at the exact time of the sampling event. An additional benefit offollowing this
approach is that using the actual setup and hold time requirementS of a design in sampling
outputs and driving inputs leads to verifying the required setup and hold time behavior of a
DUV.
As an example, consider a DUV whose outputs should be read at 2ns before the positive
edge of system clock and inputs should be driven 1ns after the positive edge of the same
clock (figure 5.2).

Sample at i Drive at

3";]~~]F
Figure 5.2 DUV Input/Output Sample and Driving Timing

The following program segment shows one approach for implementing this beha\'ior:

'p;;;gram 5.1: Signal sampling and setup/hold delays without a clocking block
1 module top:
2 bit clk=1;
3 reg [7:01 duvJn. duv_out, duvJo:
4 initial for (int i = 0: i <= 10; i++) #5ns elk = !clk: 1/ Generate elk
118 SystemVeriiog as a Verification Language

5
6 always @(negedge elk) begin
7 #3ns;
8 $display (duv_io, duv_out); /I read signal values at posedge - 2ns
9 @(posedge elk);
10 #1ns;
11 =
duv_in 10; 1/ write signals at posedge + 1ns
12 duv_io =20;
13 end
14 endmodule

In this example, an always block is used to wait for 3ns after the negative edge of clock
(line 7) so that design outputs can be read 2ns before the positive edge of clock, which occurs
at time 10n5 (line 8). Design input signals are then driven 1n5 after the positive edge of the
clock (lines Ii, 12).
SystemVeriiog provides the clocking block construct to facilitate easy and straightfor-
ward implementation of this type of interaction with a DUV A clocking block can only be
instantiated in a module, program block, or interface.
A clocking block is identified by the following aspects:
• Name: Name of the clocking block
• Clocking event: Event used as the reference for setup/hold time calculations
• Unclocked signals: Hierarchical name for environment signals that are to be sampled
or driven by the clocking block
• Clocked signals: A direction and an optional name for each unclocked signal man-
aged by the clocking block
• Default input/output skews: Default delay values, with respect to clocking events,
specifying timing for driving outputs and sampling inputs
• Skew overrides: An optional input and/or output skew override for each clocked sig-
nal
Figure 5.3 shows the relationship between the input skew, the output skew, and the
clocking event defined for a clocking block. The event used for sampling the inputs is
derived by assuming the input skew to be a negative value. The output driving event is
derived by waiting for output skew time after the clocking event.

I input I output I Clocking Event occurs at time T

I skew 1 skew I Inputs are sampled at T - Input Skew


1"'..
1__-1....ll.o.. ~1 Outputs are sampled at T + Output Skew
__- - J..
I 1 I

Figure 5.3 Clocking Block Timing Semantics

Figure 5.4 shows a pictorial view of the function of a clocking block. Any DUV or test-
bench signal can be managed by a clocking block both for sampling and driving. A clocking
block implicitly defines output d~iving clock and input sampling clock. Output driying clock
is triggered output-skew-de~ay hme after the clocking eYent, and input sampling clock is
triggered input-skew-delay time before the clocking e\'ent. A clocking block allows separate
Clocking Blocks 119

output and input skews to be assigned to any clocked signal. As such, any clocked signal has
its own dedicated and implicit output driving and input sampling clocks.

Clocking Block

Clocked Signal Unclocked


(design or testbench
signal managed by
Clocking Block)
Read Value (input)

Figure 5.4 Clocking Block Behavior

Any value written to a clocked signal of a clocking block is assigned to its associated
unclocked signal when its output driving clock occurs. This means that multiple writes to a
clocking signal are filtered and only the last assignment to the clocked signal before the
arrival of the output driving clock is driven to its associated unclocked signal. Reading from
a clocked signal of a clocking block supplies the last value of its associated undocked signal
before its last input sampling event occurred. As such, all transitions in an undocked signal
between two occurrences of input sampling clocks are filtered out by the clocking block and
only the last value of the unclocked signal before the last input sampling clock is available at
the clocked signal.
The separation between clocked and their associated unc10cked signals leads to some
unexpected results. For example, writing from a clocking block inout element and reading
the same value will not produce the value that was just written because the returned value is
a value sampled when the input sampling clock had occurred (figure 5.4).
A clocking block supports input, output, and inout clocked signals. Configuration
shown in figure 5.4 applies to inout elements. Input elements only have the input direction,
output elements only have the output direction behavior.
Program segment 5.2 below shows an example of a clocking block:

'Program
L 5.2: Sample clocking
________________ __ block implementation

1 clocking cb @(posedge clk);


2 default input #2ns output #1ns; I/default ilo skews
3 input cb_out = duv_out;
4 output duv_in;
5 =
inout cb_io top.module.duvjo;
6 input #1ns cb_out;
7 output #1ns duv_in:
8 input #1ns output #3ns cbjo:
9 endclocking
120 SystemVerllog as a Verification Language

Line 1 shows the clocking block name and clocking event. Line 2 shows the default
input and output skews. Lines 3-5 show examples of input, output, and inout element spec-
ifications. Each specification consists of the signal direction, clocked signal name (e.g.,
cb_out on line 3), and the unclocked signal (DUV or testbench signal) associated with this
clocked signal (e.g., duv_out on line 3). Note that the unclocked signal can be specified using
a hierarchical name (e.g., top.module.duvJo on line 5). Also, if the unclocked signal name is
omitted, it is assumed to be a signal with the same name as the clocked signal and in the
same scope as the clocking block (e.g., duv_ln on line 4). If the input and output skews are
different for an element, these skews can be specified explicitly. Input skew override is spec-
ified for input cb_out on line 6. Output skew override is specified for output duv_ln on line 7,
and input and output skew overrides are specified for inout cb_io on line 8.
The following program shows the use of clocking blocks to improve the implementa-
tion of the sampling and driving example shown in program 5.1:

'Program 5.3: Signal sampling and setup/hold delays with a clocking block
1 module top;
2 bit clk=l;
3 reg [7:0] duv_in, duv_out, duv_io;
4
5 initial for (int i = 0; i <= 10; i++) #5ns clk = !clk;
6
7 clocking cb @(posedge clk);
8 default input #2ns output #1 ns;
9 input cb_out = duv_out;
10 output cb_in = duvJn;
11 inout cb io = duv io;
12 input #los cb_out;
13 output #1 ns cb_in;
14 input#lns output#3ns cbJo;
15 endclocking
16
17 always @(posedge elk) begin
18 $display (cb.cb_io. cb.cb_out); /I read signal values at posedge - 2
19 cb.cb_in <= 10; /I write signals at posedge + 1
20 cb.cbJo <= 20;
21 end
22 end module

As shown in this example, the always block on line 17 is sensitive to the positive edge
of the clock elk. Upon entering the always block, the values of cb.cb_io and cb.eb_out are
read. The values read at this time, however, are those read 1ns before the positive edge of the
clock. Also, the values written to cb.eb_in and cb.cb_io on lines 19 and 20 will be applied to
duv_in and duv_io at 1ns and 3ns respectively after the positive edge of the clock.

5.2.1 Input and Output Skews


SystemVerilog provides a rich set of options in specifying the input and output skews for
clocking blocks. Some examples are shown below:
Example 1: input #lstep var_name;
Example 2: input #10ns var_name;
Example 3: input negedge var_name;
Example 4: input negedge #lns var_name;
Example 5: input #0 var_name;
Program Block 121

Example 1 shows the use of keyword #1step, which is a special SystemVerilog con-
struct for specifying that the input should be sampled in the postponed region of the previous
time-slot of the clocking block sampling event. This is essentially the same as sampling the
input in the preponed region of the time-slot for the sampling event of the clocking block
(section 5.1). Example 2 shows an absolute value input skew. Example 3 shows that the
sense of the clock can be changed from that of the sampling event for the clocking block. For
example, if the sampling event for the clocking block is (posedge elk), then the input skew
can be set to negative edge. Example 4 shows that the input skew can be set to an absolute
delay offset from a changed sense of the. sampling event. Example 5 shows the use of #0 in
specifying the skew. Inputs with explicit #0 skew are sampled in the observed region of the
time-slot for the clocking block sampling event. Outputs with explicit #0 are driven in the
NBA region of the time-slot for the clocking block sampling event. The default input skew is
#1step. The default output skew is #0.

5.2.2 Default Clocking


The sampling event for a clocking block can be declared as the default clock for the block
(e.g., module) that it is declared in. Given a default clock, the cycle delay operator "##" can
be used within that block to specify delays as a count of default clock cycles. The following
program uses the cycle delay operator.

'Program 5.4: Use of cycle delay operator


1 module top;
2 bit clk=1;
3 int cnt=1;
4
5 initial for (int i = 0; i <= 10; i++) #5ns clk = !clk;
6
7 default clocking cb @(posedge clk); endelocking
8
9 initial begin
10 $display ($time); IIprints 0
11 ##2;
12 $display ($time); IIprints 20 (2 cycles of the 10ns period clk)
13 ##(cnt+2);
14 $display ($time); IIprints 50 (cnt+2=3 cycles of the 10ns elk passed time 20)
15 end
16 endmodule

5.3 Program Block


SystemVeri log uses the same simulation semantics as that of Verilog. This means that Sys-
temVerilog inherits not only the expressive power of event-driven simulation, but also the
problem ofrace conditions that are an inherent part of event-driven simulation. as popular-
ized by Verilog. Designers have developed an extensiH set of design guidelines to avoid
race conditions in their designs. These guidelines are focused mainly on following a modular
and synchronous design style where, for example. the notion of combinational and sequential
logic are modeled using blocking and non-blocking assignments. The same design guide-
122 SystemVeriiog as a Verification Language

lines migrate directly to designs created in SystemVerilog and as such, dealing with race
conditions is not a main concern when creating designs in SystemVerilog.
For multiple reasons, however, the situation is different for verification engineers:
Verification engineers follow a more software-oriented programming style where the
notion of combinational versus sequential does not directly apply. This means that
selective use of blocking versus non-blocking assignments is not a natural fit for
avoiding races in testbenches.
Detailed evaluation order of design signals is not an immediate concern for verifica-
tion engineers. Verification engineers are more focused on verifying correct
cycle-based operation of the design, and as such prefer to avoid having to deal with
race conditions due to scheduling semantics of the simulator.
Introduction of new verification related features such as assertions and properties,
whose evaluation semantics are a new addition to SystemVerilog, require special
considerations for how testbench evaluation is scheduled along with the design.
Consider the following design and its associated testbench:

~m 5.5: Design/testbench having a race condition


1 module duv (input clk);
2 bit siga,sigb;
3 always @(posedge elk)
4 slgb <= !siga;
5 end module
6
7 module test;
8 bit clk;
9
10 initial for (In! I = 0; i <= 16; i++) #1 clk = !clk;
11
12 duv dl(clk);
13
14 always @(posedge elk)
15 di.siga <= di.sigb;
16
17 always@(negedgeclk) $dlsplay ($time, di.siga, di.sigb);
18 endmodule

The design to be verified is duv (lines 1-5). The testbench is implemented in module
test.The design assigns to sigb the complement of slga on every positive edge of clk. The
intention of this testbench is to take signal 51gb, produced at the output of the design and
assign it back to its input slga as the next test vector. This is accomplished on line 15 by mov-
ing the output value to the input at every positive edge of the clock.
The example, as shown above, has a race condition which leads to incorrect simulation
results. The race condition is due to the non-blocking assignment on line 15 using the previ-
ous value of 51gb before it is updated in the current cycle. As such, the value moved to the
input of the design is not the value produced in this cycle but its previous value. It may seem
that by changing the non-blocking assignment on line 15 to a blocking assignment, the prob-
lem would be solved, but this is not guaranteed. This is because the always blocks on lines 3
and 15 may be executed in any order at the positive edge of clk. A better fix would be to
change the sense of clock on line 14 to negedge which would then lead to correct simulation
results.
Program Block 123

As shown, the problem with this testbench can be solved after some analysis, but the
real issue is that first of all, these types of problems are very difficult to analyze for anything
but the most non-trivial designs. Second, it would be best to avoid getting into these types of
problems in the first place. SystemVeri log introduces the program block so that such prob-
lems can be prevented with ease.
The fundamental goal in introducing the program block is to provide verification engi-
neers with a well defined ordering for the evaluation of design versus testbench. In order to
achieve this goal, the following properties have been defined for a program block:
• A program block can contain only type and variable declarations and one or more ini-
tial blocks.
• A program block cannot contain always blocks, UDPs, modules, interfaces, or other
program blocks.
• Variables inside a program block can only be assigned from inside the same or other
program blocks (e.g., assignment to a program block variables from a module is i1\e-
gal).
• Variables inside a program block can only be assigned using blocking assignments.
• From inside a program block, variables on the outside that are not in any program
block, can only be assigned using non-blocking assignments.
• Statements from within a program block that are sensitized to design variables (e.g.,
@posedge of deslgn.elk) are scheduled for evaluation in the reactive region.
• Initial blocks inside a program block are scheduled for evaluation in the reactive
region. This is in contrast with initial blocks inside other blocks that are scheduled for
evaluation in the active region.
The benefits gained because of these properties are described in the following bullets.
In this description, design signals refer to any variable declared outside of program blocks.
• Suspended program block processes (e.g., an initial block suspended due to a wait on
delay or event) are scheduled to be restarted in the reactive region. An immediate
consequence of this property is that in any time-slot, the execution of code inside the
program block is started only after all design signal transitions, due to any source
other than the program block (e.g., scheduled from previous time-slots, O-delay prop-
agations in the current time-slot, etc.) are finalized.
• All program block variables are assigned using blocking assignments and all design
signals driven from inside the program block are assigned using non-blocking assign-
ments. An immediate consequence of this property is that once suspended processes
inside program blocks are restarted, all signal evaluations inside a program block are
finalized before any ofthe changes they cause in design signals cause design pro-
cesses to be restarted. The reason is that blocking assignments made inside the pro-
gram block take effect in the active region, and non-blocking assignments used for
design signals take effect in the NBA region.
• These interactions produce the following order of evaluation for program blocks:
• After entering the current time-slot, all design signal changes due to sources
other than the program block are finalized.
• Next, program block code is executed using the latest values of design signals in
the current time-slot.
• Next, all changes in program block variables are finalized. where all new values
124 SystemVeriiog as a Verification Language

to be applied to design using non-blocking assignments become available.


• New values for design variables are then applied to the design and their effect on
design is computed.
• Another iteration of this sequence occurs if changes in design signals due to val-
ues applied from the program block lead to further restart of suspended program
block processes in the next pass through the reactive region of the same
time-slot.
• A very important consequence of scheduling program block evaluations in the reac-
tive region is that results of property and assertion evaluations for the current
time-slot are visible inside a program block. This is because properties are evaluated
in the observed region, which occurs before the reactive region (figure 5.1). These
results are not available for the current time-slot outside the program block.
In the following example, a program block is used to fix the problem with the race con-
ditions in program 5.5.

'Program 5.6: Using a program block to avoid race conditions


1 module dut (input elk);
2 bit siga,sigb;
3 always @(posedge elk)
4 sigb <= !siga;
5 endmodule
6
7 program bench(input elk);
8 initial
9 forever begin
10 @(posedge clk);
11 test.di.siga <= test.di.sigb;
12 end
13 end program
14
15 module test;
16 bit elk;
17 initial for (int i = 0; i <= 16; i++) #10 clk = !elk;
18
19 dut di(clk);
20 bench bi(clk);
21
22 always @(negedge elk) $strobe ($time, di.siga, di.sigb);
23 endmodule

In this program, because ofthe semantics of the program block described in this section,
assignment on line II is guaranteed to take place after assignment on line 4 is completed,
even though they are both sensitized to the positive edge of the clock.
Design task and functions called from inside a program block are evaluated in the reac-
tive region. This is in contrast to design task and function calls from inside a design which
are evaluated in the active region. This means that in a given time-slot, the effect of
non-blocking assignments are visible to design tasks and functions called from a program
block but not visible to design and task functions called from outside the program block.
Additionally. a design task that suspends one or more times before returning (e.g., due
to a wait for delay or on eyent) and that is called from inside a program block follows the
scheduling semantics of the program block until it is suspended for the first time. However,
Inter-Process Communication and Synchronization 125

from that point on, it follows the scheduling semantics of its containing block. This behavior,
ifnot used carefully, can lead to unexpected results.

Given the scheduling complexity of design tasks and non-blocking assignments in


design tasks and functions called from a program block, it is recommended that only design
functions that use blocking assignments be called from a program block.

5.4 Inter-Process Communication and


Synchronization
.__._--------------------_._-------_..__ .__._----.-.-.--.__........... _ - - - - - - - - - -

It is often necessary to synchronize or communicate between two independently running pro-


cesses. SystemVerilog provides the following constructs to facilitate this requirement:
• Events
• Semaphores
• Mailboxes
Events and semaphores are used for thread synchronization, semaphores are also used
for mutual exclusion, and mailboxes are used for inter-process communication. These con-
structs are described in the following subsections.

5.4.1 Events
Events are objects that can be triggered, and waited on to be triggered. System Verilog
enhances the definition of Verilog events by defining an event to be a pointer to a synchroni-
zation object. As such, mUltiple event names can point to the same synchronization object,
event pointers can be assigned to one another, and event pointers can be passed to functions
and tasks. SystemVeriiog events have the following properties: .
• Can be triggered in the active region using the blocking trigger operator "->"
• Can be triggered in the NBA region using the non-blocking tri~er operator "-»"
• Can be waited on using the@ operator
• Can provide a persistent state in addition to its event trigger, lasting throughout the
time-slot in which it was triggered, that can be waited on using a wait statement
• Event variables can be assigned to one another
• Event variables can be passed to tasks and function and returned by functions
• Event variables can be compared using equality and inequality operators
The following program shows an example of using event triggering and waiting to syn-
chronize between two process blocks, and also using event pointers as task arguments.

'Program 5.7: Using events to synchronize between blocks


1 module top;
2 event e1, e2;
3 task trigger_event(event e); ->e; endtask
4
5 initial begin
6 #1; ->e1;
126 SystemVeriiog as a Verification Language

7 #1000 $finish;
8 end
9
10 always @(e1) begin
11 #10 trlgger_event(e2);
12 $display ("e1", $time);
13 end
14
15 always @(e2) begin
16 #10 trigger_event(e1);
17 $display ("e2", $lime);
18 end
19 endmodule

This example shows the implementation of task trigger_eventO that triggers the event
object that its argument identifies. Task trigger_eventO is then used to trigger different events
(lines 11, 16). Note that in this example, initial triggering of event e1 (line 6) is delayed by 1
time unit in order to remove the race condition between triggering and checking of event e1'
If on line 6, e1 is triggered at time 0, then this triggering may occur after the always block on
Ime 10 is activated, in which case, the trigger will not be seen by the always block and event
e2 never gets triggered.

The following program shows the use of persistent state of an event to eliminate this
type of race condition.

'Program 5.8: Events persistent states


1 module top;
2 event e1, e2;
3
4 initial begin
5 wait(e1.triggered);
6 $display ("e1 triggered in A");
7 -> e2;
8 @e1;
9 if (e1 == e2) $display ("e1 and e2 the same in A");
10 e1 = null;
11 -> e2;
12 end
13
14 initial begin
15 ->e1;
16 @e2;
17 $display ("e2 triggered in B");
18 e2=e1;
19 ->e2;
20 @e2;
21 $display ("e2 triggered in B");
22 if (e1 == nUll) $display ("e1 is null in B");
23 end
24 end module

The output produced by this example is:


e1 triggered in A
e2 triggered in B
e1 and e2 the same in A
e2 triggered in B
e1 is null in B
Inter-Process Communication and Synchronization 127

On line 5, the first thread waits on persistent triggered state of event e1. The persistent
triggered state of an event remains active throughout the duration ofthe time-slot in which it
occurs. As such, this trigger will be visible by any thread waiting to be executed in the same
time-slot (in this case thread 2 at line 15) regardless of which one is started first. Using the@
operator on line 5 may lead to a race condition where if thread 2 is started first, then the trig-
gering of e1 on line 15 will be missed by thread I. This program also shows examples of how
event pointers can be assigned to one another and compared against each other or the null
value. Note that in this example, the synchronization object pointed to by variable e2 on line
11 is the synchronization object that was initially pointed to by variable e1.

5.4.2 Semaphores
Semaphores are used for the following purposes:
• Mutual exclusion
• Thread rendezvous
SystemVerilog provides a built-in semaphore construct. SystemVerilog provides the
following methods for this construct:
function new (int keyCount = 0);
task put(int keyCount=1);
task get(int keyCount=1);
function trLget(inl keyCount=1);

Function new() is used to allocate a new semaphore. Tasks get() and put() are used to
procure and return keys to the semaphore. Function try_get() is used to check whether or not
the requested number of keys are available in the semaphore.
Use of semaphore in implementing process synchronization is discussed in the follow-
ing subsections.

5.4.2.1 Semaphore for Mutual Exclusion


Semaphores are ideal for providing mutual exclusion to a common resource. The program
region that accesses a common resource is referred to as the active section. As an example, a
driver interacting with a physical bus can potentially be called by multiple threads running in
parallel. It is clear that access to the driver should be limited to one thread at a time.
The following program shows an example of using semaphores to limit access to an
active section to one process at a time:

'Program 5.9: Using semaphores for mutual exclusion


1 module top;
2 semaphore sem;
3 initial sem = new(1);
4
5 task automatic driver (input Siring nn, input time delay1, input time delay2);
6 #delay1;
7 sem.get(1);
8 $display($time, nn, "entering active section");
9 #delay2;
10 $display($time, nn, "leaving active section");
11 sem.put(1);
128 SystemVeriiog as a Verification Language

12 endtask
13
14 initial
15 fork
16 drlver("AgentA", 10,30);
17 driver("AgentB", 20, 40);
18 join
19 end module

First, note that task drlverO on line 5 is specified to have automatic variables by default.
Otherwise, given that the default mode for task variables is static, multiple simultaneous
calls to this task would lead to problems. As shown in the example, the get() method of
semaphore is called before entering the active section and the put() method of the semaphore
is called after leaving the active section. This example produces the following output:
10 AgentA entering active section
40 AgentA leaving active section
40 AgentB entering active section
80 AgentB leaving active section

At time 0, the fork statement on line 15 starts two processes, each calling task drlverO
with different delay parameters. Given that process AgentA has a shorter initial delay time of
10 (line 6), it acquires the semaphore first (line 7). Process AgentB finishes its initial delay at
time 20, and attempts to acquire a key from the semaphore, but suspends since the key is
already held by process AgentA, which is still in its active section. At time 40, process AgentA
leaves the active section and returns its key to the semaphore (line 11). At this time, the key
becomes available to process AgentB, which then enters the active section at time 40, leaving
it at time 80, and returning the key to the semaphore.
Note that any number of processes can be started by calling driverO and this implemen-
tation would guarantee mutual exclusion across all processes for the active section in this
task.

5.4.2.2 Thread Rendezvous


Thread synchronization can be modeled as a producer-consumer type relationship. In this
model, producer threads can only proceed if their product is consumed by a consumer, and a
consumer thread can only proceed when a product is available for it. This concept can be
generalized to multiple producers and consumers where a producer can proceed when its
product is consumed by any of the consumers, and a consumer can proceed when a product
is available from any of the producers. Figure 5.5 shows a pictorial view of this relationship.

Producer_1 ............ ~ Consumer_1


~ ----.... 'I--s-em-a-p-h-or-e--""I ~ ~
Producer_N - - - ----.... Consumer_N

Figure 5.5 Producer-Consumer Model of Process Rendezvous

The following program shows how a rendezvous between multiple threads is imple-
mented using two semaphores:
Inter·Process Communication and Synchronization 129

'Program 5.10: Using semaphores for rendezvous between multiple threads


1 module top;
2 =
semaphore seml new(O);
3 semaphore sem2 = new(O);
4
5 task automatic producer (input string name, input time delay1, input time delay2);
6 while (1) begin
7 #delayl;
8 /I make product and place in mailbox
9 seml.put(l);
10 sem2.get(1);
11 $display ($time, name, ": Product consumed, now making a new one");
12 #delay2;
13 end
14 endtask
15
16 task automatic consumer (input string name, input time delay1, input time delay2);
17 while (1) begin
18 #delayl;
19 sem1.get(1);
20 sem2.put(1);
21 $display ($time, name, ": Got product, now consuming");
22 /I take product from mailbox and consume
23 #delay2;
24 end
25 endtask
26
27 initial begin
28 fork
29 producer("Producerl", 10, 30);
30 consumer("Consumerl", 20, 60);
31 consumer("Consumer2", 20, 60);
32 #150 $finish;
33 join_any
34 end
35 endmodule

Note that both semaphores are initialized with zero keys (lines 2, 3). In implementing a
rendezvous, producers issue a putO on sem1 and a getO on 5em2 before assuming their prod-
uct is consumed, and consumers issue a getO on sem1 followed by a putO on sem2 before
assuming a product is available. This program produces the following output:
20 Consumerl: Got product, now consuming
20 Producerl: Product consumed, now making a new one
60 Consumer2: Got product, now consuming
60 Producerl: Product consumed, now making a new one
100 Consumerl: Got product, now consuming
100 Producer1: Product consumed, now making a new one
140 Consumer2: Got product, now consuming
140 Producerl: Product consu med, now making a new one

Note that two slow consumers (turnaround time of 80) are started for a relatively faster
producer (turnaround time of 40). Also note that any number of producers and consumers can
be started in the fork statement (line 28) and the rendezvous mechanism would still guaran-
tee that one consumer proceeds for anyone produced item.
A simple case for using the above approach is where a thread can continue beyond point
A in its program flow only when another thread has reached point B in its program flow and
vice versa. Tn this case, the first thread can be assumed to produce when reaching point A,
and the second thread is assumed to consume when reaching point B.
130 SystemVeriiog as a Verification Language

5.4.3 MaHboxes
Mailboxes are used for passing messages between independently running processes. Mail-
boxes provide the following features:
• Ability to pass messages from one process to another
• Behave like a FIFO, where messages placed first are retrieved first
• Can have bounded or unbounded size
• Message sender can suspend if a bounded mailbox is full
• Message receiver can suspend until a message becomes available
SystemVerilog provides a built-in mailbox construct. This construct provides the fol-
lowing methods:
function new (int bound = 0);
function int numO;
task put(slngular message);
function Int tryJlut(singular message);
task get(ref singular message);
function try_get(ref singular message);
task peek(ref singular message);
function int try_peek(ref singular message);

Function newO is used to allocate a new mailbox. Function num() returns the number
of messages in the mailbox. Tasks putO, getO, and peek() are used to place a message in a
mailbox, retrieve a message from a mailbox, and retrieve a copy of a message from a mail-
box without removing it. These tasks suspend execution if the mailbox is full (for put()) or if
the mailbox is empty (for getO and peekO). Tasks getO and peekO produce a runtime error
message if the mailbox content message type does not match their argument type. Functions
Iry...put(), try_getO, try"'peekO return 1 upon success, and 0 when the operation fails. Func-
tions try-KetO and try"'peekO return ·1 when a type mismatch is detected between mailbox
content and the argument passed to these functions.
SystemVerilog mailboxes can be parameterized where the message type of the mailbox
is specified when the mailbox is declared. This allows for message type checking to occur
during compilation and prevents runtime errors because of message type mismatches.
The following program shows an example of using a mailbox to pass messages between
a producer and a consumer.

I Program
5.11: Using a mailbox to pass messages between processes
1 module top;
2 class envelope;
3 int letter;
4 function new(int Itr); lelter=ltr; endfunction
5 end class
6
7 task sender(input string name, input time delay1);
8 envelope envlp;
9 int count;
10 while (1) begin
11 #delay1;
12 envlp = new(count);
13 mbox.put(envlp);
14 $display ($time. name, ": sent msg: ". count);
15 count ++;
16 end
Constrained Random Generation 131

17 endtask
18
19 task receiver (input string name, input time delay1);
20 envelope envlp;
21 while (1) begin
22 #delay1;
23 mbox.get(envlp);
24 $display ($time, name, ": received msg: ", envlp.letter);
25 end
26 endtask
27
28 mailbox mbox;
29
30 initial begin
31 mbox =new(O);
32 fork
33 sender("Producer1", 10);
34 receiver("Consumer1", 35);
35 #50;
36 join_any
37 $display("Letters in mailbox before exit: ", mbox.num());
38 $finish;
39 end
40 end module

This example shows mailbox mbox declared (line 28) to transfer messages of type class
envelope (lines2-5). Tasks sender() and receiver() are defined to place objects of type envelope
inside mbox (line l3) and collect objects of type envelope from mbox (line 23), respectively.
Task sender() operates faster than task receiverO so that objects of type envelope accumulate
inside mbox. Tasks sender() and receiverO are started in independent processes (lines 33, 34)
and tenninated at time 50ns. Before ending the simulation, function num() is used to print the
number of objects inside mbox.

5.5 Constrained Random Generation


Constrained Random Generation is presented in chapter 10.

5.6 Property
_. --_._--_
_."
Specification
---
.. _.-.----_and
__ Evaluation
- ---
.-_._., ,-.- -------------_ .... ... .-_--------_.-._---_._--_.. - - - - - ---_. __._---
Property specification and evaluation is presented in chapter 15.

5.7 Coverage Collection


Coverage collection is presented in chapter 17.
132 SystemVerllog as a Verification Language
133

PART 3
Open Verification Methodology
134
CHAPTER 6 DVMInfrastructure

SystemVerilog provides a powerful programming environment for building an advanced


verification environment. The rich set of constructs available in SystemVerilog provide a
great deal of flexibility in building a verification environment. In spite of this flexibility, and
because of the existence ofa language independent verification methodology, the same set of
approaches and practices for building verification environments appear repeatedly across
diverse projects. Documenting these practices and capturing their implementation as reus-
able classes provides benefits by both reducing duplicated effort across projects, and also
providing a framework for new engineers to leverage existing expertise in the field.
The Open Verification Methodology (OVM)1 is an open-source, SystemVerilog-based
class library developed based on extensive experience in building state-of-the-art verification
environments. The primary purpose of OVM is to enable a greater number of verification
engineers with varied degrees of programming experiences to quickly build well-con-
structed, object-oriented verification environments. In addition, the availability of predefined
classes for building verification environments and writing tests allows verification engineers
to meet their verification goals sooner and with higher confidence.
The OVM class library objects and classes are specially defined for implementing a
multi-layer verification environment (chapter 3), and follows the coverage-driven verifica-
tion methodology (chapter 1). As such, a good understanding of these topics is needed to
fully realize the benefits of this class library.
The features of the OVM class library are divided into the following categories;
• Creating and managing class objects in the environment.
• Building and configuring the verification environment hierarchy, and managing the
simulation runtime phases.
• Using TLM (transaction) interfaces for connecting verification environment blocks.
• Generating transaction sequences for modeling verification scenarios.

1. OV~I is an open source veritication methodology and class library developed jointly by Cadence
Design Systems and Mentor Graphics Corporation. Visit http:'/w\\,w.ovmworld.org to download the OVM
package and to join the 0'"\1 community of users and contributors.
136 OVM Infrastructure

This chapter describes an overview of the OVM class librari, and provides details of
the core utilities provided in the library. The use of the OVM class library for building the
verification environment hierarchy is described in chapter 7. Implementing transaction inter-
faces with the OVM class library is described in chapter 9. Transaction sequence generation
using the OVM class library is described in chapter 8.

6.1 Verification Class Library Goals


The implementation of a verification environment using class objects requires that the fol-
low·ing implementation issues be addressed by its predefined objects and classes:
• Class object utilities (e.g., creation, access, modification, configuration)
• Component hierarchy creation and configuration
• Transactions
• Transaction ports and interfaces (communication)
• Sequences of transactions (stimulus generation)
• Simulation phases
• Reporting and messaging
A class library should have the infrastructure to support the creation, modification, and
management of class objects created throughout the lifetime of a verification environment. A
verification class library should, therefore, provide the necessary infrastructure to support
these core operations.
Creating a block hierarchy is the first step in creating a verification environment. In
building a class-based block hierarchy, each block is modeled as a class object, with the
object instantiation hierarchy (figure 4.5) representing the block hierarchy. In this implemen-
tation approach, many low-level details are shared between all blocks in the hierarchy (e.g.,
utilities to identify sub-blocks, parent blocks, blocks names, etc.). A verification class library
should provide the base classes that provide these core features.
Verification components modeled using class objects should be implemented as inde-
pendent objects where each component interacts with the outside environment only through
its interfaces. The implementation of this behavior using class objects requires the use of
transaction level model (TLM) interfaces. TLM interfaces (also known as transaction inter-
faces) allow each component to communicate with other components without any specific
knowledge about the internal implementation of these other components. A verification class
library should define the base classes that are needed for implementing transaction inter-
faces.
Interaction between verification environment blocks should take place through transac-
tions. Transactions may carry data, status, or instructions from one block to another. It is in
fllet these transactions that are passed through transaction interfaces that connect verification

, The presentJti<'1l <,ftbe 0'·\1 class library in this book is based <'n 0\"\1 1.tl rekas".
OVM Class Library Features 137

environment blocks. A verification class library should define the classes that provide these
transaction level models.
Complex scenarios require the creation and synchronization of complex sequences of
transactions, advancing in lock-step at multiple interfaces of the DUV. The modeling and
creation of these complex scenarios is not a trivial task and requires its own te,chniques and
utilities. A verification class library must provide the base classes and the necessary infra-
structure for supporting the creation of such complex scenarios.
In System Veri log, simulation time is advanced without any consideration of abstract
phases that may exist in the verification flow. Progression of time in a verification environ-
ment is, however, managed in phases where different sets of activities must take place in
each phase (e.g., initialization phase, running phase, checking phase). A verification class
library should define the infrastructure necessary for managing this high level view of simu-
lation phases and the implicit synchronization requirement it imposes on concurrently run-
ning threads in different blocks of the environment.
Producing informative messages about the state of verification is an essential part of
carrying out verification activities. Messages may be produced in different blocks and at dif-
ferent levels of severity. A verification class library should define the base utilities for speci-
fying such severity levels and enabling or disabling the generation of such messages.
Features of the OVM class library are defined to address these requirements ofa verifi-
cation related class library. These features are summarized in the next section.

6.2 DVM Class Library Features


Features of the OVM class library are divided into the following broad categories:
• A: Object and component factory
• B: Core utilities
• C: Reporting utilities
• D: Hierarchy creation and management
• E: Thread synchronization
• F: Verification environment components
• G: Transaction objects
• H: Transaction interfaces
• I: Transaction channels
• J: Transaction items and sequences
• K: Sequence interfaces
The factOlY provides the ability to create objects during the simulation runtime while
providing special features for overriding its default behavior. Core utilities provide a com-
mon set of utilities (e.g .. comparing. printing) for lise across objects in the environment.
Reporting utilities allow global management and handling of reports generated by individual
objects in the environment. Hierarch 1, creation lind management features introduce the con-
cept of "containment" where one block is contained in another block. thereby providing the
Derived Parameterized Class L Derived Class I ~ Base Class
-
W
QO

~
5'
@
;
;r
1/1

ovm_objecl
2
!l
c::
@
0)1 ov~
~
I l~~quence item

I
--~"'~ 'I
I
Il"".m _ compone~t-regiSI
L___ . ._....._...__
I I ~L--I -----'

I
10 E~uence
- _......_ .._. _ _ _ _1

I ovm random sequence '1


ovm ..exhauslive_sequence

ovm_re~rsp_sequence I

Figure 6.1 DVM Class Hierarchy


OVM Class Library Features 139

ability to create a hierarchical environment. In addition, this feature introduces the ability for
each block to have its own thread of execution, and also introduces the concept of phasing by
dividing the life cycle of each thread into multiple phases (e.g., run, extract, report, etc.), and
synchronizing the start of each phase across multiple threads. In contrast with the phasing
where the life of a thread is divided into multiple stages, thread synchronization features
allow for synchronization between any two or more threads (e.g., using a semaphore to guar-
antee mutual exclusion between two or more threads). Verification environment components
provide containers that model typical components in a verification environment (e.g., agents,
monitors, scoreboards, etc.) and provide the control mechanisms for configuring, building,
and controlling the simulation flow.
The OVM verification methodology promotes the use of transaction-based communica-
tion for modeling traffic flowing between components. To support this methodology, the
OVM class library provides transaction objects for modeling traffic between components,
transaction interfaces and transaction channels for modeling connectivity between compo-
nents, and sequence interfaces to facilitate the generation of verification scenarios.
Figures 6.1 and 6.2 show an overview of the class inheritance hierarchy for the OVM
class library. The classes shown in these figures are placed into groups that correspond to
groups defined in the beginning of this section. Appendix A provides a full listing of all class
declarations in the OVM class library, indicating the parent class, parameters for each class,
and whether a class is a virtual class.
The core utilities of the OVM class library are described in this chapter.

L..-_---II Base Class

Derived Class

Derived Parameterized Class

Figure 6.2 OVM TLM Interfaces Class Hierarchy


140 OVM Infrastructure

6.3 Object and Component Factories


A class-based verification environment is composed of hierarchical and non-hierarchical
objects. Component objects modeling verification environment blocks represent structural
hierarchy. Data objects represent transactions and environment properties and are considered
as non-hierarchical objects. A common operation in building a verification environment and
running scenarios is the creation of these components and data objects. The localization of
all such object creation operations in a central factory provides not only a uniform approach
for creating different kinds of objects, but also provides the ability to make global and
instance-specific changes in how objects are created by the factory (e.g., type and instance
overrides in section 6.3.1).
In its simplest form, afaetory is a class-based object that provides specialized function-
ality, through its method interface, for creating objects of specialized types during simulation
runtime. The following example illustrates the implementation of a simple factory:

'Program 6.1: Simple implementation of an object factory


1 module top;
2 virtual class base_pkt;
3 string name;
4 rand byte payload;
5 function new(string pname=""); name=pname; endfunction
6 virtual task printO; endtask
7 endclass
8
9 class eth_pkt extends base_pkt;
10 constraint c1 {payload == 10;}
11 task printO; $display("Ethernet packet Payload: ", payload); endtask
12 function new(string pname=""); super.new(pname); endfunction
13 endclass
14
15 class usb_pkt extends base_pkt;
16 constraint c2 {payload == 20;)
17 task printO; $display("USB packet Payload: ", payload); endtask
18 function new(string pname=""); super.new(pname); endfunction
19 endclass
20
21 class Simple_factory;
22 static function base_pkt create_pkt(string pkUype_name, string pkt_name);
23 case (pkUype_name)
24 "eth_pkt":begin eth_pkt pkt; pkt=new(pkt_name); return pkt; end
25 "usb_pkt":begin usb_pkt pkt; pkt=new(pkt_name): return pkt; end
26 end case
27 endfunction
28 endclass
29
30 initial begin
31 base_pkt pkt;
32 pkt = simple_factory::create_pkt("eth_pkt", "packet1");
33 assert(pkt.randomize());
34 pkt.print(); /I prints "Ethernet packet Payload: 10"
35
36 pkt = simple_factory::create_pkt("usb_pkt", "packet2");
37 assert(pkt.randomize());
38 pkt.printO; /I prints "USB packet Payload: 20"
39 end
40 end module
Object and Component Factories 141

This example defines base_pkt, a base packet data structure containing fields name (line
3) and payload (line 4), function newO (line 5), and virtual function printO (line 6). Derived
classes eth_pkt and usbJ'kt each provide a different implementation of virtual task printO
(lines 11, 17). Each derived class also provides a different constraint for field payload of base
class baseJ'kt (lines 10, 16). This example also shows the implementation of simple_factory, a
simple packet factory that provides function create_pktO (lines 22-27). This function returns
a newly created packet data object whose type and name are provided by arguments
pkCtype_name and pkt_name respectively. Note that this function is implemented to return an
object of type base_pkt but because of polymorphism (section 4.7.4), this function can also
return an object whose class type is derived from base_pkt (i.e., a specialized type). This
means that in this example, function create_pkt() can return objects having types eth_pkt (line
24) and usb_pkt (line 25) since these classes are derived from class base_pkt.
Function create_pkt() (line 22) is defined as a static function, therefore this function can
be called by using the class identifier (i.e., simple_factory) and the class scope resolution
operator "::". This approach allows function create_pkt() to be called without creating an
instance of this object factory.
This factory is used on line 32 to create a packet of type eth_pkt with name packet1,.
Randomizing this packet uses the constraint block specified for class eth_pkt (line 10). There-
fore, randomizing this object sets the value of field payload to value 10. The use of this fac-
tory for creating an object of type usb_pkt is shown on line 36.
Note that function prlnt() is defined as a virtual function in the base class base_pkt (line
6). As such, calling this function through a pointer to an object of type base_pkt (lines 34, 38)
has the same effect of calling the actual definition of this function for the true type of the
object being pointed to by the pointer. This means that calling function pkt.prlnt() (lines 34
and 38) results in calling function print() of the derived class that pkt is pointing at.

6.3.1 Type and Instance Overrides


The implementation of a factory in program 6.1 points to the following two benefits of using
an object factory, namely the ability to define:
• Type overrides
• Instance overrides
A t.J!pe override from type T1 to T2 configures a factory to return an object of type T2
anytime an object of type T1 is requested. An instance override for instance INST from type T1
to T2 configures a factory to create an object of type T2 anytime an object of type T1 is
requested for instance INST. For non-hierarchical objects (i.e., objects that do not represent
environment hierarchy), an instance is identified by instance name and the name of its hierar-
chical anchor component. For hierarchical objects (i.e., component objects), an instance is
identified by full hierarchical name of that component. The implementation of the object fac-
tory shown in program 6.1 can easily be extended to include type and instance overrides by
providing a task to define such overrides and then checking arguments pkUype_name and
pkt_name in function create_pktO (line 21) for any such oyerride before the actual objects are
created.
142 DVM Infrastructure

The OVM library includes a built~in factory that provides the features presented in this
section for both registering classes with the factory and also for defining instance and type
overrides for registered classes. This factory can be used for the creation of objects and hier-
archical components. These features are described in the following sections.

6.3.2 OVM Object Factory


In OVM, class ovm_object is used to model non-hierarchical objects, and provides the core
utilities of the classes in the library (figure 6.1). All hierarchical and non-hierarchical classes
in the OVM class library are derived from class ovm_object, thereby making these core utili-
ties available to all the predefined classes of the library. The examples provided in this sec-
tion derive from class ovm_object since the focus here is to highlightthe utilities provided by
this class. When building a verification environment according to OVM guidelines, verifica-
tion environment elements should be derived from the predefined classes of DVM that are
best suited for the intended implementation (e.g., a packet should be derived from the pre-
defined class ovm_sequence_item). Chapters 13 and 14 show the appropriate base classes for
verification environment element through an example implementation of a verification envi-
ronment and its stimulus generator.
Any class derived from ovm_object can be registered with the OVM factory by using
macro ovrn_object_utils() in its declaration. This usage is shown in the following example:

'Program 6.2: Registering a new class with the object factory


1 class packet extends ovm_object;
2 rand int unsigned size;
3 'ovm_object_utils(packet)
4 constraint c1 {size < 1000;}
5 endclass

The above program first defines class packet derived from base class ovm_object (line
I). This definition uses macro ovrn_objecCutils() to register class packet with the OVM fac-
tory (line 3).

The begin/end variation of macro ovm_object_utiisO can be used to apply field automa-
tion 6.4) to fields in this class. This usage is shown below:

'Program 6.3: Field automation for class fields


1 class packet extends ovm_object;
2 rand int unsigned size;
3 'ovm_object_utils_begin(packet)
4 'ovm_field_int(size, OVM_ALL_ON)
5 ovm_object_utils_end
6 constraint c1 {size < 1000;}
7 endcJass

The implementation of macro ovrn_objecCutils() is based on class ovm_object_wrapper,


where registering a new class is done by extending from class ovrn_objecCwrapper and defin-
ing functions create_object(), create_componentO, and other helper functions. The use of this
macro is, however, preferred since it simplifies the process of registering a new class with the
factory.
Object and Component Factories 143

Once a class is registered with the OVM factory, the following predefined methods of
the OVM factory are used for creating objects and specifying object type and instance over-
rides:
Methods of class ovm_factory:
static function ovm_object create_obJect(
string obUype. string ins\..path= ..... string inst_name=.... )
static function void seUnst_override(string insUd. string from_type. string to_type)
static function void set_type_override(string from_type. string to_type. bit replace=1)
static function void prinLall_overrrides(bit aIUypes=O)

Function create_obJectO of the factory is used to create an instance of type obLtype


whose name is assigned to argument InsLname. Optionally, argument InsLpath of this func-
tion can be used to provide the hierarchical name of the block that contains the newly created
object. This name can then be used to selectively apply type and instance override to objects
based on their location in the hierarchy. Functions seUnst_overrlde() and set_type_overrlde()
are used to specify instance and type overrides. These functions must be called before
objects are created. Calling task prlnLall_overrldes() with no arguments prints all overrides
defined for the factory, while setting all_types to 1 results in the name of all classes currently
registered with the factory to be printed. These methods are defined as static methods of
class ovm_factory and can therefore be called directly through the class identifier ovm_factory
and operator "::".
The following example shows the declaration of example packets derived from class
packet(defined in program 6.2), and the use of macro ovm_obJect_utils() to register these
newly defined classes with the OVM factory:

'Program 6.4: Packet declarations to be created by the factory


1 class short_packet extends packet;
2 'ovm_object_utils(short_packet)
3 constraint c1 {size == 10;}
4 endclass
5
6 class long_packet extends packet;
7 'ovm_object_utils(long_packet)
8 constraint c1 {size == 100;}
9 endclass

Derived classes short_packet and long_packet each provide a different specialization of


class packet by constraining the packet size to different values. Each of these classes are also
registered with the OVM factory by using macro ovm_object_utlisO (lines 2 and 7).
The use of OVM factory to create objects of this kind is shown in the following pro-
gram:
'Program 6.5: OVM Object Factory Example
1 module top;
2 'include "ovm.svh"
3 'include "program_6.2" /I packet class
4 'include "program_6.4" /I derived packet classes
5
6 packet pkt[5];
7 task create_packetsO;
8 Dvm_object obj;
9 obj = ovm_factory: :create_object("packet". "top". "pp 1");
10 $cast(pkt[O]. obj);
144 OVM Infrastructure

11
12 $cast(pkt[ 1]. ovm_factory: :creale_object("packet", "top", "pp2"));
13 $cast(pkt[2J, ovm_factory::create_objecl("packet", "top", "qq3"));
14 $cast(pkt[3]. ovm_factory: :create_object("short_packet", "top", "pp4 "));
15 $cast(pkt[4], ovm_factory::create_object("long_packet", "top", "pp5"));
16 endtask
17
18 initial begin
19 ovm_factory::seUnst_override("top.pp·", "packet", "short_packet");
20 ovm_factory::seUype_override("packet", "shorCpacket");
21 ovm_factory: :set_type _override("packet", "long_packet");
22 ovm_factory::seUype_override("short_packet", "packet");
23 ovm_factory: :set_type _override("short_packet", "long_packet", 0);
24 ovm_factory: :seUype_ override("long_packet" , "short_packet");
25 ovm_factory: :print_all_overrides(1);
26 create-packetsO;
27 end
28 endmodule

Task create_packetsO (lines 7-16) shows the use of function create_object() ofOVM fac-
tory for creating new objects. This task creates five new objects. The creation of the first
object shows the details of object creation and pointer casting (lines 8-10). First, a pointer to
an object of type ovm_object is declared (line 8), then a new object is created as having type
packet, residing in block top with name pp1 (line 9), and then cast to pkt[O] which is a pointer
of type packet (line 10). The next four objects are created (lines 12-15) using a short hand
notation of directly casting the created objects to their corresponding pointers.

Task create.J)acketsO is called on line 26 after specifying a number of overrides (lines


19-24). Task prinCall_overrides() of the factory is called on line 25 to print a summary of all
overrides specified for the factory. Override examples shown here illustrate the following
features of override specification:
• Wild-card characters "*,, and "?" can be used when specifying an instance name,
where "*,, matches any string of characters and "?" matches a single character. This
means that instance name "top.pp·" (line 19) matches all object names except
"top.qq3" .
o Both object type and instance name arguments of function seUnsCoverride() must
match for an instance override to take effect. As such, instance override on line 19
applies only to instances "top.pp1" and "top.pp2".
o By default, a new type override overwrites the previous override for the same type.
This means that the type override specified on line 21 overwrites the type override
specified on line 20.
o Instance overrides take precedence over type overrides. This means that even though

the type override specified on line 20 applies to instances "top.pp1", "top.pp2", and
"top.qq3", this type override applies only to "top.qq3" since the first two instances are
a match for the instance override specified on line 19.
• The default behavior can be changed ~o that a new type override does not take effect
if a type override has been defined previously. By passing a value of 0 as the last
argument of function seUype_override(), the type override on line 23 does not take
effect, since a type override is specified for type short_packet on line 22.
o Type overrides do not chain. This means that specifying a type override from packet 1

to packet2 and a type override from packet2 to packet3 does not result in a type over-
ride from packet1 to packet3 · In this case, any request for objects of type packet1 pro-
Object and Component Factories 145

duces an object of type packet2 , and any request for an object of type packet2 produces
an object of type packet 3 . The same rule applies to instance overrides.

Object types generated by the above example without overrides (excluding lines 19-24)
and with overrides is summarized below:
Name No Override With Overrides
pkt[O] pp1 packet short_packet (line 19)
pkt[1] pp2 packet short_packet (line 19)
pkt[2] qq3 packet long_packet (line 21)
pkt[3] pp4 short_packet packet (line 22)
pkt[4] pp5 long_packet short_packet (line 24)

6.3.3 OVM Component Factory


In OVM, hierarchical components are derived from base classes ovm_component and
ovm_threaded_component. Macro ovm_component_utils() is used to register a class derived
from these classes with the OVM factory. Once a component is registered with the OVM fac-
tory, the same factory methods used for creating objects (section 6.3.2) can be used for creat-
ing components. However, OVM defines the following methods for the ovm_component base
class in order to simplify the creation of components:
Methods for class ovm_component:
function ovm_component create_component (string camp_type, string inst_name);
function ovm_object create_object (string obUype, string jnst_name=""):
function void seUnst_override (string inst_path, string from_type, string to_type);
static function void seUype_override (string from_type, string to_type, bit replace=1);

These methods call method create_objectO of the OVM factory with the appropriate
value for its inst_path argument derived from a component's hierarchical position. Compo-
nents are class objects that represent hierarchical blocks and as such, include an explicit
pointer to their containing block (i.e., parent component). Function create_componentO cre-
ates a new component of type comp_type and name inst_name assumed to be structurally con-
tained in the component from which this function is called (i.e., while this function calls
function create_componentO of ovm_factory, the inst_path of the new component is given as
the full hierarchical path of the calling component). Function create_objectO creates a new
object of type obLtype and name insCname, assumed to be structurally contained in the com-
ponent from which this function is called. Functions seUnst_overrideO and set_type_overrideO
behave the same as functions in ovm_factory except that parameter inst_path passed to
seUnsCoverrideO of ovm_component is given relative to the hierarchical path of the compo-
nent from which this method is called (i.e., parent component).
In OVM, the root of a component hierarchy represents a testcase that contains the veri-
fication environment, as well as any customization of this environment for that specific test.
The top level component representing this test is created by calling global function run_testO
(section 7.4). The name of the class representing the top level component is either passed
directly to this function as an argument, or specified as a plus-argument (section 7.4.1). This
function uses global function create_component() defined by OVM. This function can also be
used directly to create a top level component when no other component yet exists:
Methods for class ovm_factory:
static function ovm_component create_component (
string camp_type, string insLpath="", string inst_name, ovm_component parent):
146 OVM Infrastructure

The following example shows the use ofOVM factory to register and create verification
environment blocks derived from class ovm_component, and examples of overrides that can
be specified when using this factory. It should be noted that this example is provided to show
the use of OVM factory features, and the creation of a verification environment hierarchy
according to OVM guidelines is shown in section 13.6.

Iprogram 6.6: OVM Component Factory Example


1 module top;
2 'include "ovm.svh"
3 'include "program_6.2" /I packet class
4 'include "program_6.4" /I derived packet classes
5
6 class child_comp extends ovm_component:
7 packet pkt[3]:
8 'ovm_component_utils(chitd_comp)
9
10 function new (string name, ovm_component parent):
11 super.new(name, parent);
12 $cast(pkt[O], create_object("short_packet", "p1"»;
13 $cast(pkt[1 j, create_object("longJjacket", ·p2"»;
14 $cast(pkt[2], create_ object("shorCpacket", "p3"»;
15 endfunclion
16 end class
17
18 class rooCcomp extends ovm_component;
19 child_comp ccomp;
20 'ovm_component_utils(rooCcomp)
21
22 function new (string name, ovm_component parent);
23 super.new(name, parent);
24 seUnst_override("s1.p1", "short_packet", "long_packet"):
25 seCtype_override("lon9_packet" , "short_packet");
26 $cast(ccomp, create_component("child_comp", "cc1"));
27 endfunction
28 endclass
29
30 rooCcomp rcomp;
31 initial
32 $cast(rcomp, ovm_factory::create_component("root_comp", "top", "rcomp", null»;
33 endmodule

The above example defines class child_camp derived from class ovm_component. This
class is registered with the GVM factory by using macro ovm_component_utiisO (line 8). The
predefined function create_object() of class ovm_component is used to create three instances of
types short_packet, long_packet, and short_packet respectively (lines 12-14). Also note that
the argument list for function create_objectO of ovm_component is different from the one
defined for the GVM factory (as shown in section 6.3.2) where it does not require the name
of the containing component. The reason is that function create_objectO of ovm_component,
automatically uses the name of the calling component (i.e., parent component) as the name
of the component containing the newly created object.
Class is also derived from ovm_component and contains an instance of the
root_comp
class This class is registered with the OVM factory by using macro
child_camp.
ovm_component_utils() (line 20). Functions seUnst_override() and seCtype_override() of
ovm_component are called in the constructor for class rooccomp (lines 24, 25) before an
instance of class child_comp is created by calling function create_componentO of
Field Automation 147

ovm_component (line 26). Note that instance name for the instance override (line 24) is given
relative to the calling component.
The top level component is created by calling function create_componentO of the OVM
factory (line 32). The name of the top level module is used as the instance path of this new
component (i.e., "top"). Also, this is a top level component so the parent component is set to
null.
It should be noted that the OVM class library provides a specialized mechanism for cre-
ating the component hierarchy corresponding to a verification environment, and the imple-
mentation shown in the above example is provided only to illustrate features of the OVM
factory and the facilities it provides. Section 7.2 describes the steps for creating a component
hierarchy following the OVM guidelines. Section 7.4 describes the mechanism used for
instantiating the verification environment hierarchy which is done as part of running a test.

6.4 Field Automation


The implementation of core utilities of the OYM class library (e.g., copy, clone, etc.) is based
on the concept of field automation. Field automation is a powerful feature that provides a
declarative approach for specifying class fields that participate in operations such as copy-
ing, comparing, etc.
The following program shows an example of using field automation to decide which
fields of a data packet should be printed when the predefined prlntO method of a packet
derived from class ovm_sequence_item is called:

lprogram 6.7: Example of field automation printing fields


1 module top:
2 'include "ovm.svh"
3 typedef enum {SHORT, LONG} packeUype:
4 class simple_packet extends ovm_sequenceJtem:
5 rand bit error;
6 rand byte size:
7 rand packeUype ptype:
8 rand byte addr:
9 rand int unsigned data[J:
10 constraint payload_size {data.sizeO == size:}
11
12 'ovm_object_utils_begin(simple_packet)
13 'ovm_fieldJnt(slze,OVM_NOPRINT)
14 'ovm_field_enum(packeUype, ptype, OVM_PRINT)
15 'ovm_fieldJnt(addr,OVM_PRINT)
16 'ovm_field_arrayJnt(data,OVM_PRINT)
17 'ovm_objeccutils_end
18 endclass
19
20 simple_packet pkt = new;
21 initial begin
22 void'(pkt.randomizeO with {size == 4:}):
23 pkt.printO:
24 end
25 end module
148 OVM Infrastructure

This example shows the declaration of class simple_packet derived from base class
ovm_seqence_ltem, containing fields error, size, ptype, addr, and data (lines 5-9). Field auto-
mation macros ovm_field_lnt(), ovm_field_enum(), and ovm_fleld_arrayJnt() are used (lines
13-16) to specify that field size should not be printed but fields ptype, addr, and data should
be printed when method print() (predefined for class ovm_object) is called (line 23). No macro
is specified for field error, and therefore, this field does not participate in field automation at
all. For all classes derived from ovm_object, field automation macros should be placed inside
predefined macro pair ovm_object_utils_begln() and ovm_object....utlls_end (lines 12 and 17).
The OVM class library provides field automation macros for different types of fields.
Table 6.1 shows a summary of field automation macros and the type of fields that are sup-
ported.

Field Kind Field Automation Macro Prototype Element Type


ovm_lleld_mt (FIELLJ, FLAG) any mtegral type (no enum)
'ovm_field_enum (TYPE, FIELD, FLAG) enum type
Scalar
'ovm_field_object (FIELD, FLAG) derived from ovm_object
'ovm_field_string (FIELD, FLAG) string
'ovm_field_sarraLint (FIELD, FLAG) any integral type (no enum)
Static Array 'ovm_field_sarraLobject (FIELD, FLAG) derived from ovm_object
'ovm field_sarray_string (FIELD, FLAG) string
'ovm_field_array)nt (FIELD, FLAG) any integral type (no enum)
Dynamic Array 'ovm_field_array_object (FIELD, FLAG) derived from ovm_object
'ovm_field_array_string (FIELD, FLAG) string
'ovm_field_queue)nt (FIELD, FLAG) any integral type
Queue 'ovm_field_queue_object (FIELD, FLAG) derived from ovm_object
'ovm_field_queue_string (FIELD, FLAG) string
key: 'ovm_field_aa_string_int (FIELD, FLAG) string
integral 'ovm_field_aa_objecUnt (FIELD, FLAG) derived from ovm_object
'ovm_field_aa_inUtring (FIELD, FLAG) any integral type
Associative Array key:
'ovm_field_33_object_string (FIELD, FLAG) derived from ovm_object
string
'ovm_field_aa_string_string (FIELD, FLAG) string
key: Note I 'ovm field aa_int <key type> (FIELD, FLAG) any integral type
Notel: <key_type> can be one of: mt, mteger, mt_unslgnea, mteger_unslgned, byte, byte_unslgnea, sMortmt,
shortint_unsigned, longint, longinUmsigned.
Note2: Fields handled by ovm_field intO must be packed integral fields of at most 4096 bits

Table 6.1: Field Automation Macros

Field automation attributes (argument FLAG in table 6.1) are used to include or exclude
fields from the set of predefined operations. These attributes are shown in table 6.2. Multiple
attributes can be specified by combining attributes using the bitwise-OR operator (e.g.,
"OVM_ALL_ON I OVM_DEC"). Alternatively, attributes can be added arithmetically, but adding
the same attribute mUltiple times will result in unexpected behavior (e.g., "OVM_ALL_ON +
OVM_DEC + OVM_READONLY").
Core Utilities 149

Feature Attribute Description


OVM_COPY Copy this fIeld (default)
Copy
OVM_NOCOPY Do not copy this field
OVM_COMPARE Compare this field (default)
Compare
OVM_NOCOMPARE Do not compare this field
OVM]RINT Print this field (default)
OVM_NOPRINT Do not print this field
OVM_NODEFPRINT Do not print the field if it is the same as its default value
Print
OVM_BIN,OVM_DEC,
OVM_UNSIGNED,OVM_OCT,
Radix settings for integral types (default: OVM_HEX)
OVM_HEX,OVM_STRING,
OVM_TlME,OVM_NORADIX
OVM_PACK Pack and unpack this field (default)
OVM_NOPACK Do not pack/unpack this field
PacklUnpack
OVM]HYSICAL Treat as a physical field
OVM_ABSTRACT Treat as an abstract field
OVM_READONLY Do not apply configuration settings (section 7.2)
Misc OVM_ALL_ON Set all operations on (default)
OVM_DEFAULT Use the default flag settings

Table 6.2: Field Automation Attributes

The OVM class library defines composite attributes in order to simplify the setting of
common combinations of field automation attributes. These composite attributes are shown
in table 6.3.

~
~

~u
U f-<
~ ...l
<: u
>- ~
~ ~
~ ~
~

~ ~
;:E
0 ~
~ u Sol
Predefined Flags >- U ;:E
0
u ~ 1:1 ~ ~ (/)

~
~ (/)
0 ffi
f.I..
0 0 0 0 0 0 I:Q
ZI ~ ~ ZI 0 1 ~I
~\ ~ ~I
Z\ U ZI ZI ~

~I ~I
0 0 0
;:E ;:EI ;:E ;:EI
;> ;> ;> ;>
0 0 0 0
;:E
;>
0
;:EI
;>
0
gg g 0
;:EI
;>
0 0
OVM_DEFAULf il!l il!l il!l il!l il!l il!l ilQ ilQ

OVM_ALL_ON Ii:! Ii:! Ii:! Ii:! Ii:! Ii:! Ii:!


OVM_FLAGS_ON Ii:! Ii1I Ii:! Ii:! Ii1I Ii1I
OVM]LAGS_OFF

Table 6.3: Field Automation Composite Attributes

6.5 Core Utilities


The OVM class library provides the following core utilities for class ovm_object:
• Copy
• Clone
• Compare
150 OVM Infrastructure

• Print
• Pack/Unpack

Object cloning is implemented by creating a new object and using the copy utility to set
the contents of the newly created object to the contents of the source object. Copy, compare,
print, and pack/unpack utilities are described in the following subsections.

6.5.1 Copy
OVM defines the following predefined methods to support the copy operation for classes
derived from ovm_object:
Methods defined for class ovm_object:
function void copy (ovm_object rhs)
virtual function void d030PY (ovm_object rhs)

Function copy() is used to copy all automated fields of rhs that are configured to partici-
pate in the copy operation. The recursion behavior of this function is controlled by the recur-
sion policy for object fields (e.g., OVM_SHALLOW, OVM_REFERENCE). Function do_copy()
provides a callback mechanism for handling fields of rhs that require special handling, or are
not configured by field automation macros to be included in the copy operation.
The following program shows an example of using these predefined functions:

'Program 6.8: Using copy() and do_copy()


1 module top;
2 'include "ovm.svh"
3
4 class payload extends ovm_sequencejtem;
5 rand byte capacity;
6 rand byte size;
7
8 'ovm_object_utils_begin(payload)
9 'ovm_field_int(capacity,OVM_ALL_ONIOVM_NOCOPY)
10 'ovm_field_int(size,OVM_ALL_ON)
11 'ovm_object_utils_end
12
13 function void do_copy (ovm_object rhs);
14 payload pi; $cast(pl,rhs); /I rhs is guaranteed to be non-null
15 if (pI, capacity < 10) capacity = pl,capacity;
16 size = 100; /I size will be over-written by automated field copy
17 endfunction
18 endclass
19
20 class hier_packet extends ovm_sequencejtem;
21 time collection time;
22 rand byte addr:
23 =
rand payload pload new;
24
25 'ovm_object_utils_begin(hier_packet)
26 'ovrn_fieldjnt(addr,OVM_ALL_ON)
27 'ovm_field_object(pload,OVM_ALL_ON)
28 'ovm_object utils end
29 end class --
30
31 =
hier_packet pkt1 new;
32 hier_packet pkl2 = new;
33 initial begin
34 vOid'(Pkt1,randomize());
Core Utilities 151

35 pkt2.copy(pkt1);
36 end
37 end module

This example shows the implementation of hierarchical object hler_packet having fields
collection_time, addr, and pload. Field collection_time is not automated, but fields addr and
pload are both automated for all operations by using attribute OVM_ALL_ON (lines 26, 27).
Field pload is an instance of class payload having fields capacity and size. Both fields capacity
and size are automated for printing (lines 9, 10), but capacity is excluded from the automated
copy operation (line 9) since it should be copied only if its value is less than 10. Function
do_copy() of class payload is defined to perform this conditional copy operation for field
capacity. Note that function argument rhs should be casted to the derived type payload (line
14) so that access to the fields of the derived type are possible. Function do_copy() is called
before automated copy operations take place, therefore writing to fields that are automated
for copy operation (line 16) will not be visible outside this function.

6.5.2 Compare
OVM defines the following predefined methods to support the compare operation for classes
derived from ovm_object:
Compare Methods defined for class ovm_object:
function bit compare (ovm_object rhs, ovm_comparer comparer=null);
function bit do_compare (ovm_object rhs, ovm_comparer com parer);

Function compare() is used to compare all automated fields of rhs that are configured to
be included in the compare operation. The recursion behavior of this function is controlled
by the recursion policy for object fields (e.g., OVM_SHALLOW, OVM_REFERENCE). Function
do_compare() provides a callback mechanism for handling fields of rhs that require special
handling or are not configured by field automation macros to be included in the compare
operation.
The following program shows an example of using these functions:

'Program 6.9: Field automation for compare, and using do_compareO


1 class simple_packet extends ovm_sequence_item;
2 rand byte addr:
3 rand byte data;
4
5 . ovm_objecCutils_begin( simple_packet)
6 'ovm_fieldJnt(addr, OVM_ALL_ONIOVM_NOCOMPARE)
7 'ovm_field_int(data, OVM_ALL_ON)
8 'ovm_object_utils_end
9
10 function bit do_compare(ovm_object rhs, ovm_comparer comparer);
11 simple_packet pkt;
12 $cast(pkt. rhs): /I rhs is guaranteed to be non-null
13 do_compare = (data == 1) ? 1 : (addr != pkt.addr);
14 endfunction
15 endclass

This example shows the implementation of class simple_packet having fields addr and
data. Both fields are automated (lines 6, 7) but field addr is not included in the automated
152 OVM Infrastructure

compare operation (line 6). The implementation of do_compareO for this class compares the
values for addr only if data has a value of 1 (lines 10-14).
The use of compare operation is shown in the following program:

'Program 6.10: Comparing two objects using the bullt·ln function compareO
1 module top;
2 'include "ovm.svh"
3 'include "program_6.9" /I simple_packet class
4
5 simple_packet pkt1 = new;
6 simple_packet pkt2 = new;
7 initial begin
8 'message(OVM_LOW, (pkt2.compare(pkt1)))
9 end
10 end module
L--_______________________

The compare operation is carried out by calling the compare function of one of the
objects with the other object as its argument (line 8). Generally, the compare operation is
symmetric, This means that function compare() of either of the two objects being compared
can be called with the other object as an argument. In this example, however, the compare
operation is not symmetric because of the special implementation of function do_campa reO
where the value of field data of the calling object is used to guide the compare operation.
The compare operation makes use of compare policy object ovm_comparer, This object
contains settings for guiding the compare operation, as well as predefined methods for com-
paring objects or fields (one method for each type). It is possible to define a new policy
object that redefines the settings of the default comparer and hence achieve a modified com-
pare behavior. The following program shows an example of using a modified ovm_comparer
object:

'Program 6.11: Using a modified ovm_comparer


1 module top;
2 'include "ovm.svh"
3 'include "program_6.9" /I simple_packet class
4
5 class custom_comparer extends ovm_comparer;
6 int unsigned show_max 1;= /I Maximum miscompares to print
7 int unsigned verbosity = 500; /I Verbosity setting for miscompare
8 =
ovm_severity sev OVM_INFO; /I Severity setting for miscompare
9 string miscompares =""; 1/ Last set of miscompares
10 bit physical =1; /I compare physical fields
11 =
bit abstract 1; /I compare abstract fields
12 bit check_type = 1; /I verify that object types match
13 ovm_recursion_policLenum policy = OVM_DEFAULT_POLlCY;
14 end class
15
16 =
custom_com parer cc new;
17 =
Simple_packet pkt1 new;
18 =
Simple_packet pkt2 new;
19 initial begin
20 cc.show_max =0;
21 'message(OVM_LOW, (pkt2.compare(pkt1, cc»)
22 end
23 end module
Core Utilities 153

In this example, class custom_com parer is derived from oYm_comparer. For illustration
purposes, the definition of this derived class lists properties that are available in class
oym_comparer and their default settings. The setting for these properties can be changed
either during class definition (lines 5-14) or before the policy object is passed to the compare
operation (line 21). In this example, field show_max of the compare policy object is set to
zero so that no compare results are printed, and only the result of calling function compare()
(line 21) is shown.

6.5.3 Print
OVM defines the following predefined methods to support the print operation for classes
derived from oYm_object:
Print Methods defined for class oYm_object:
function yoid print (oYm_printer printer=null)
function string sprint (oym_printer printer=null)
Yirtual function yoid do_print (oYm_printer printer)

Function print() is used to print all automated fields of the calling object that are config-
ured to be included in the print operation. Function print() recursively calls the print() function
of all automated fields that are objects derived from base class oYm_object. Function sprint()
is the same as printO except that it returns a string instead of printing the result to the screen.
Function do_print() provides a callback mechanism for handling fields of the calling object
that require special handling or are not configured by field automation macros to be included
in the print operation. The use of print() and do_printO functions is similar to the use model for
the copy operation.
The print operation makes use of print policy object oYm_printer. This policy object con-
tains settings for controlling the print operation, as well as predefined methods that custom-
ize the way object data is printed. It is possible to define a new print policy object that
redefines the settings of the default printer and hence achieves a modified print behavior. The
following print policy objects are automatically created by loading the OVM class library:
• Global object oYm_default_printer (initialized to oym_defauIUable_printer)
• Global object oYm_defauIUine_printer of type oYm_line_printer
• Global object oYm_default_tree_printer of type oYm_tree_printer
• Global object oYm_defauIUable_printer of type oYm_table_printer
If no printer policy object is passed to print functions (i.e., prlnt() and sprintO), then the
global printer oYm_defaulCprinter is used by default. Each printer policy object contains field
knobs that can be used for customizing the printer output. Table 6.4 shows a summary of
knob properties that can be configured.
The implementation of the print utility allows the output of print functions to either fol-
low the global setting of the printer policy object or to be customized depending on the local
requirements. Local customizations are done by instantiating a local copy of a printer policy
object and then modifying its properties before passing it explicitly to the print function. The
printer policy object that is passed to callback function do_print() is the same printer policy
object that is provided as argument to the print functions.
154 OVM Infrastructure

Type Field Name Default Value Description


I Class ovm"'pnnter_knobs:
int column 0 Current column the printer is writing to.
int max_width 999 Do not print anything beyond this column number.
string truncation "+" Append this string to end of truncated fields.
bit header I Print a header if set to I.
bit footer I Print a footer if set to 1.
int global_indent 0 Indent size at the beginning of each new line.
bit full_name I xx
bit identifier I Print field identifiers if set to I.
int depth -1 How many levels of objects to print (-1 means all).
bit reference 1 Print object pointer value if set to 1.
bit type_name 1 Print field type name if set to 1.
bit size 1 Print field sizes if set to 1.
int begin_elements 5 Print this many elements from the beginning of array.
int end_elements 5 Print this many elements from the end of array.
bit show_radix I Show radix in printed values if set to I.
int mcd OVM_STDOUT File pointer where the print result is sent.
radix_enum defaultJadix OVM_HEX Formatting text when using OVM_NORADIX radix
string bin_radix ""b" Formatting text when using OVM_BIN radix
string oct_radix III'Oti
Formatting text when using OVM_OCT radix
string decJadix ""d" Formatting text when using OVM_DEC radix
string unsigned_radix ,uld ll Formatting text when using OVM_UNSIGNED radix
string hexJadix '"'hl! Formatting text when using OVM_BIN radix
I Class ovm_hier"'printer_knobs derived from ovm"'printer_knobs:
string indent_str " "(2 spaces) String to use for each indentation level.
bit show_root 0 Print the root object where printing is started.
I Class ovm_table"'printer_knobs derived from ovm_hier"'printer_knobs:
int name_width 25 Name column width (not printed if set to 0).
int type_width 20 Type column width (not printed if set to 0).
int size_width 5 Size column width (not printed if set to 0).
int value_width 20 Value column width (not printed if set to 0).
l Class ovm_tree"'printer_knobs derived from ovm_hier"'printer_knobs:
string separator "{}" Place each tree level string inside these two characters.

Table 6.4: Printer Knob Configuration Fields

The following program shows examples of changing the default printer object, as well
as creating local printer objects:
~~~------------------
Program 6.12: Using printer policy objects with printO method
1 module top;
2 'include "ovm.svh"
3 class Simple_packet extends ovm_sequencejtem;
4 rand byte addr;
5 rand byte data;
6
7 'ovm_object_utils_begin(simple packet)
8 'ovm_fieldjnt(addr,OVM':::ALL_ON)
9 'ovm_fieldjnt(data,OVM_ALL_ONIOVM_NOPRINT)
10 'ovm_object_utils_end
Core Utilities 155

11 endclass
12
13 ovm_line_printer 10caUine_printer = new;
14 ovm_tree_printer locaUree.J)rinter = new;
15 ovm_table_printer 10caUable_printer = new;
16
17 simple_packet pkt = new;
18 initial begin
19 lIovm_defaulCprinter = ovm_defauIUable_printer; IIdefault, so not needed.
20 pkt.printO;
21 ovm_default_prlnter = ovm_defauIUine_printer;
22 pkt.printO;
23 ovm_default_printer = ovm_defauIUree_printer;
24 pkt.printO;
25 10caUable_printer.knobs.type_wldth = 20;
26 10caLtable_printer.knobs.value_width = 20;
27 pkt.print(locaUable_printer);
28 end
29 endmodule

This example shows the definition of class simple_packet with fields data and addr,
where automation macros are used to include addr in (line 8) and exclude data from (line 9)
the print output. This example shows the creation of local printer objects local_line_printer,
local_tree_printer, and locaLtable_printer (lines 13-15). These objects can be modified locally
to change the default print behavior. By default, ovm_default_prlnter, is assigned to
ovm_defaulctable_prlnter (line 19). Calling printO without any arguments results in using
ovm_default_printer (line 20). The global default printer is assigned to the global default line
printer ovm_defauIUlne_printer (line 21). Therefore, calling function prlntO on line 22 results
in ovm_defauIUine_prlnter being used as the default printer. The customization of printer
knobs (table 6.4) for the local printer object local_table_printer is shown on lines 25 and 26.
When calling printO with this local printer object (line 27), this customized local printer
object is used instead of the global default printer object.

6.5.4 Packing and Unpacking


The OVM class library provides the following functions, defined for class ovm_object, for
packing and unpacking of a class object:
Methods defined for class ovm_object:
function int pack (ref bit bitstream[]. input ovm_packer packer=null);
function inl unpack (ref bit bitstream[], input ovm_packer packer=null);
Callback methods defined for class ovm_object:
virtual function void do_pack (ovm_packer packer);
virtual function void do_unpack (ovm_packer packer);

Calling function packO of a class object appends the packed contents of this object to bit
array bitstream. The contents of the class object are packed according to the settings of, and
by using the helper functions provided in, packer. Function packO first processes all auto-
mated fields of a class object, followed by executing virtual callback function do_packO to
perform any special handling required by the model being implemented.
Calling function unpackO of a class object unpacks the contents of bit array bitstream
into the class object. The unpacking is performed according to the settings of, and by using
the helper functions provided in, packer. As with function packO, function unpackO first pro-
156 OVM Infrastructure

cesses automated fields of the class object, followed by executing virtual callback function
do_unpackO to perform any special handling required by the model being implemented.

The packing and unpacking utility of the DVM class library is implemented using the
helper functions provided by the class ovm_packer. This class provides the following methods
for packing and unpacking data types:
Methods defined for class Dvm_packer:
virtual function void pack_field_int (loglc[63:0] value, int size);
virtual function void pack_field (bitstream_t value, int size):
virtual function void pack_string (string value):
virtual function void pack_time (time value):
virtual function void pack_real (real value):
virtual function void pack_object (ovm_void value):
virtual function logic[63:0] unpack_field_int (int size):
virtual function bitstream_t unpack_field (int size):
virtual function string unpack_string 0:
virtual function time unpack_time ():
virtual function real unpackJeal 0:
virtual function void unpack_object (Dvm_void value):
virtual function int get_packed_sizeO:
virtual function bit is_null 0:

In the remainder of this section, an example is shown where these helper functions are
used in the definition of callback functions do_packO and do_unpackO to pack and unpack a
class object. The example shown in this section does not show any automated fields, but any
field that is automated for packing and unpacking is processed using the same helper func-
tions shown explicitly in this example. Class fields are included in and excluded from pack-
ing and unpacking by using automation attributes OVM_PACK and OVM_NOPACK, respectively
(table 6.2). It should be noted that all automated fields are processed before a callback func-
tion is executed.
The following program shows the definition of two class objects that will be used to
describe the packing and unpacking mechanism of the DVM class library:

{program 6.13: Example classes to be packed and unpacked


1 class scalars extends ovm_object:
2 string addr:
3 time days:
4 real prob:
5 byte bits_0008:
6 bit [1023:01 bits_1024:
7
8 extern function void do_pack (ovm_packer packer):
9 extern function void do_unpack (Dvm_packer packer):
10 end class
11
12 class composites extends Dvm_object;
13 string string_arrayD:
14 scalars one_obj =new:
15 scalars obLarraylJ;
16
17 extern function void do_unpack (ovm_packer packer);
18 extern function void do_pack (ovm_packer packer);
19 end class
L

This program shmvs the implementation of classes scalars and composites, each con-
taining the different types of class fields that may need to be packed or unpacked. Callback
Core Utilities 157

functions do_packO and do_unpackO are defined as extern functions that must be defined as
part of this implementation (lines 8-9, 17-18).

The implementation of function do_packO for classes scalars and composites is shown in
the following program:

'Program 6.14: Implementation of do packO


1 function void scalars::do_pack (ovm_packer packer);
2 packer.pack_slring(addr);
3 packer.pack_time(days);
4 packer.pack_real(prob);
5 packer.pack_field_inl(blts_0008, $bits(bits_0008»;
6 packer.pack_field(blts_1024, $bits(bits_1024»;
7 endfunction
8
9 function void composites::do_pack (ovm_packer packer);
10 Int unsigned arr_size;
11
12 arr_size = slrlng_array.sizeO;
13 packer.pack_field-'nl(arr_size, $bits(arr_size»;
14 for (inl i=O; i<arr_size; i++)
15 packer.pack_string(string_array[i]);
16
17 packer.pack_object(one_obj);
18
19 arr_size = obLarray.sizeO;
20 packer.pack_field_int(arr_size. $bits(arr_size»;
21 for (inl i=O; i<arr_size; i++)
22 packer.pack_object(obLarray[i]);
23 endfunction

Class scalars contains only non-composite fields, and the packing of each of its fields
into the packer object packer passed into this function is shown above (lines 2-6). In packing
integral fields, function pack_fieldO is used to pack an integral field of any size up to 4096 bits,
and function pack_field_intO is used to pack fields of at most 64 bits. In this implementation,
SystemVerilog system function $bitsO is used to get the number of bits in variables bits_ODDS
and bits_1024 (lines 5, 6).
Packing composite fields is shown for class composites (lines 9-23). The convention in
packing an array is to first pack the size of this array as an integer and then pack each mem-
ber of this array. This process is shown for array string_array (lines 12-15). First the size of
array is packed using function pack_field_intO (line 13) and then each member of the array is
packed using function pack_string() (line IS). The packed size value will be used during the
unpacking process.
A single object is packed using function pack_objectO. Calling function pack_obJectO on
field one_obJ results in calling function do_packO of that object (lines 1-7). The packing of an
object array (lines 19-22) follows the same flow as that of string_array.
The implementation of function do_unpackO of classes scalars and composites is shown
in the following program:

'Program 6.15: Implemenlalion ofdo_unpackO


1 funclion void scalars::do_unpack (ovm_packer packer):
2 addr = packer.unpack_slringO:
3 days = packer.unpack_IimeO:
4 prob = packer.unpack_reaIO:
158 OVM Infrastructure

5 bits_Oooa = packer.unpack_fieldjnt($bits(bils_OOOa));
6 bits_1024 = packer.unpack_field($bits(bits_1 024));
7 endfunction
a
9 function void composites::do_unpack (ovm_packer packer);
10 int unsigned arr_slze;
11
12 arr_size = packer.unpack_fieldjnt($bils(arr_size));
13 for (int i=O; i<arr_size; i++)
14 string_array[i] = packer.unpack_stringO;
15
16 if (one_obj != nUll)
17 packer.unpack_object(one_obj);
18
19 arr_size = packer.unpack_fieldJnt($bils(arr_size));
20 for (int i=O; i<arr_size; i++)
21 obLarray[i] = newO;
22 packer.unpack_object(obLarray[i]);
23 end
24 endfunction

The implementation of do_unpack() for class scalars shows the unpacking of values from
packer object packer in the same order that these objects were packed (lines 1-7). Also, the
implementation of the unpacking process for composite objects in class composties follows
the same order that these objects were packed (lines 9-24). Array string_array is unpacked by
first unpacking the size of this array (line 12) and then using a loop to unpack each member
of this array. Calling function unpack_object() on field one_obj results in calling function
do_unpack() of class scalars (lines 1-7). The unpacking of an object array (lines 19-22) fol-
lows the same flow of unpacking arrays.
The use of packing and unpacking functions is shown in the following program:

'Program 6.16: Calling packO and unpackO functions


1 module top;
2 'include "ovm.svh"
3 'include "program_6.13" 1/ declarations of scalars and composites
4 'include "program_6.14" 1/ do-packO functions
5 'include "program_6.15" 1/ do_unpackO functions
6
7 composites cmpst1 new;=
8 composites cmpst2 new;=
9 bit stream[];
10 initial begin
11 /I populate fields of cmpst1 with arrays and values.
12 void'(cmpst1.pack(stream));
13 'message(OVM_LOW, (stre~m.size()))
14 void'(cmpst2.unpack(stream));
15 end
16 endmodule

This program shows that object cmpst1 is packed by calling its predefined function
pack() (line 12), The resulting stream is then unpacked into cmpst2 by calling its predefined
function unpack() (line 14). Calling functions pack() and unpackO in tum calls the hook meth-
ods do_packO and do_unpack() respectively.
In most applications. a data object is packed so that the packed bit stream can be applied
to a DUV interface. Similarly, unpacking is needed where a class object must be populated
by the bit stream collected from a DUV interface. In such cases, it is important that the
Core Utilities 159

unpacking flow mirrors the steps taken for collecting data from the environment, and that the
packing process satisfies the format needed for applying the packed stream into the DUV
interface.

6.5.4.1 Packing and Unpacking of Automated Fields


Automating class fields allows these fields to automatically be included in the packing and
unpacking processes. The internal implementation of field automation for packing and
unpacking mirrors the flow described in this section. During the packing process, first, auto-
mated fields of a class object are packed and then its callback function do_packO is called.
During the unpacking process, first automated fields of a class object are unpacked and then
its callback function do_unpackO is called.

6.5.4.2 Packing and Unpacking Metadata


An important assumption about the default behavior of packing and unpacking of automated
object fields is that an object is packed if it is not null, and a bit stream is unpacked into an
object if that object is not null. This means that before unpacking into a class object, all its
fields that are also class object must be initialized to non-null values.
The OVM packer class ovm_packer defines field use_metadata which is set to false by
default. When this field is set to true, it causes metadata to be added to a packed stream when
automated dynamic fields are packed. Metadata is collected for the following dynamic ele-
ments:
• Strings
• Class objects
• Arrays
When packing strings with an active use_metadata flag, a null byte is added after a string
is packed. This behavior is intended to duplicate the string handling behavior of the C lan-
guage where each string is terminated with a null byte.
When packing a class object with an active use_metadata flag, four bits are packed
before an object is packed, indicating whether the object has a null value. During unpacking,
function Is_nullO can be used to check the value of these bits and to decide ifan object should
be allocated and unpacked into. In contrast with default behavior of the unpack operation
(Le., when use_metadata is set to false. and where all objects that are unpacked into must be
allocated before unpacking is started), this feature allows object allocation to be decided dur-
ing the unpacking process.
When packing arrays with an active use_metadata flag, a 32 bits value indicating the size
of the array is packed before array contents are packed. The same field is used during the
unpacking process of automated fields to resize the array before is members are unpacked
into.
It is important that the same setting for field use_metadata be used for packing and
unpacking of the same object.
160 OVM Infrastructure
CHAPTER 7 OVM Component
Hierarchy

In a class-based verification environment, it is possible to build the environment hierarchy


during simulation runtime since class objects representing components can be created after
simulation has been started. This ability is in contrast with an RTL design or a module-based
verification environment where the hierarchy is modeled with module blocks leading to a
fixed structure before simulation runtime is started.
The ability to create the verification environment hierarchy during simulation runtime
leads to great flexibility in how the verification environment can be created. Taking advan-
tage of this flexibility, however, requires that a consistent approach be followed during hier-
archy creation, and that a powerful infrastructure for effective implementation of this
consistent approach is available.
This chapter describes the features provided by the OVM class library for building the
verification environment hierarchy and managing simulation phases of components in this
hierarchy. Section 7.1 provides an abstract view of hierarchical components and objects, and
principles that must be followed when building the environment hierarchy. Section 7.2
describes the constructs provided by the OVM class library for building the environment
hierarchy and shows a detailed example of hierarchy construction for an environment con-
taining the types of cases that can be handled by these constructs. Section 7.3 describes the
predefined class types provided by the OVM class library for building different components
in a verification environment. Section 7.4 introduces the predefined simulation phases of the
OVM class library and describes the constructs provided for activating and controlling the
execution of these phases.

Guidelines for creating the environment component hierarchy is in part related to the princi-
ples of object-oriented programming. These guidelines are:
• Self-containment
• Recursive construction
162 OVM Component Hierarchy

• Configurability
Self-containment ~uideline refers to the need that a component behavior or implementa-
tion be independent of any value not contained within its sub-hierarchy. This guideline is a
necessary requirement for achieving component reuse. The need for port-based connectivity
is a direct result of enforcing the self-containment guideline (see section 9.7 on transaction
interfaces). A clear example of where this guideline must be strictly enforced is in the imple-
mentation of a verification component.
Recursive construction refers to how the hierarchy is created. In this approach, each
parent component builds only its immediate children components, and the children compo-
nents in turn, build their own immediate children components. The recursive nature of this
approach leads to the creation of the full hierarchy where each parent component is created
before its child components are created. During this recursive construction process, the build
method of each component follows these steps:
• Local fields that can locally be decided are initialized (e.g., default values).
• Local hierarchy configuration fields (usually set to default values and initialized by
parent components as needed) are used to decide which children components must be
created.
• Each child component is created and then the child component's build mechanism is
activated to build its sub-hierarchy.
• Local fields that must be decided by the contents of children components and their
sub-hierarchies are initialized.
• Transaction interfaces that must link children components are connected.
Configurability refers to the ability for a component to configure the structure and
behavior of its component sub-hierarchy. The need for this ability is a direct consequence of
enforcing the self-containment guideline, since self-containment implies that a child compo-
nent is not aware of its parent component, and as such, configuration information should be
passed from the higher layers of the hierarchy to the lower layers of the hierarchy. When
using the recursive construction approach, the configurability challenge is that when a com-
ponent is starting to build its children components, the sub-hierarchy rooted at these children
components has not yet been created and, therefore, configuration fields within this sub-hier-
archy do not yet exist. This means that a dedicated mechanism is required for controlling
configuration fields deep within the sub-hierarchy when the sub-hierarchy is recursively con-
structed. Support for this feature is provided in the OVM class library.
Figure 7.1 shows a graphical view of verification environment elements from the per-
spective of hierarchy construction. A hierarchical component may contain any of the follow-
ing elements:
• Fields
• Regular
• Hierarchy configuration
• Behavior configuration
• Children components
• Fixed components
• Conditional components
• Reference components
Abstract View of the Component Hierarchy 163

• Children objects
• Source objects
• Cloned objects
• Reference objects
A re~ular field is used for the internal operation of a component. A hierarchv configu-
ration field is used to decide the hierarchical structure of a component (e.g., passive/active
flag in an interface verification component indicating whether or not an agent should contain
a driver and sequencer). A behavior configuration field is used to control the behavior of the
component (e.g., how many packets to generate in a sequencer). Hierarchy and behavior con-
figuration fields are set to default values and then initialized by parent components during
hierarchy construction.
A fIXed child component is always present in the component hierarchy (e.g., the monitor
in an interface verification component). A conditional child component is present if indi-
cated by a hierarchy configuration field (e.g., sequencer and driver components are present
only in an active interface verification component). A reference child comvonent is in fact a
pointer to a component that is physically located at a different level of the hierarchy (e.g.,
components at the lower level of the hierarchy need to access components that sit at higher
layers of the hierarchy).
A source child object is a container of a collection of data items (e.g., configuration set-
tings). A source child object is referred to as "source" since it is neither cloned nor a refer-
ence and hence contains the source of its content. A cloned child object is created by cloning
another object that is physically located at a higher level of the hierarchy (e.g., copying the
global configuration object to the local scope for local use and modification). A reference
child object is in fact a pointer to an object that is physically located at a different level of the
hierarchy (e.g., a reference to the global configuration object may be made available in the
lower layers of the hierarchy).
Figure 7.1 shows examples of these fields, objects, and component types. For example,
TOP_COMP is a fixed component that is physically located in the top level of the hierarchy,
and RefTOP_COMP components in SystemComp and ModuleComp components are both refer-
ence components that point at fixed component TOP_COMPo Also, source object GlobConf is a
configuration object that is physically located in the top level of the hierarchy, and RefGlob-
Conf objects in SystemComp and ModuleComp are both reference objects that point to source
object GlobConf in the top level of the hierarchy. Also, field HierFlag is a hierarchical configu-
ration field in component ModuleComp whose value should come form field GlobFlag in the
top level of the hierarchy.
The arrows that point from each layer of the hierarchy to the lower layers indicate the
required order of construction. For example, at the top level of the hierarchy, object Glob-
Conf, component TOP_COMP, and field GlobFlag control the content and structure of the hier-
archy rooted at component SystemComp, and must therefore be created before component
SystemComp is created. The relationships shown by arrows in figure 7.1 are specified using
the configuration construct of the OVM class library. The next section introduces the con-
structs provided by the OVM class library for creating and configuring a component hierar-
chy.
1640VM Component Hierarchy

F Source Object
I Fixed Component

C Conditionel Component Cloned Object


I
R Reference Component Reference Object
I I
CD Regular Field
@--t::>@] B is Reference to A
c::::L) Hierarchy Configuration Field
~ B is a clone or copy of A
CO Behavior Configuration Field
~ A defines conditions for creation of B

Figure 7.1 Hierarchical Components, Objects, and Fields

7.2 Hierarchy and Configuration Constructs


--------------------

In the OVM class library, class ovm_component provides the necessary facilities for building
a component hierarchy. Class ovm_threaded_component derived from ovm_component, in turn,
provides the necessary facilities for controlling the runtime behavior of these hierarchical
components (section 7.4). The predefined verification environment components of the OVM
class library (e.g., ovm_monitor, section 7.3) are derived from ovm_threaded_component. This
means that the features of classes ovm_component and ovm_threaded_component are available
to all predefined verification environment components of the OVM class library.

The OVM provides specific guidelines for using its predefined verification environment
components to build a verification environment hierarchy. A complete example showing this
flow is provided in section 13.6. This section, however. uses class ovm_component to illus-
trate the general steps for building and configuring a component hierarchy. The techniques
described in this section are then used with all classes derived from class ovm_component.
Hierarchy and Configuration Constructs 165

Hierarchical building and configuration of components is supported through the follow-


ing methods:
Methods Defined for class ovm_component:
function void buildO;
function void set_configjnt(string insCname, string field_name, ovm_bitstream_t value);
function void seCconfig_object(string insCname, string field_name, ovm_object value,bit clone=1);
function void set_config_slring(string inst_name, string field_name, string value);

Global Methods:
function void set_config_int(string inst_name, string field_name, ovm_bitstream_t value);
function void seCconfig_object(slring inst_name, string field_name, ovm_object value, bit clone=1);
function void set_config_string(string Inst_name, string field_name, string value);

The steps for building the contents of a component are implemented in function bulidO
of that component. Configuration functions seCconfig_*O provide a name-based approach for
. setting fields within sub-components that are not yet created. An instance name Inst_name
and a field name field_name are specified with each configuration function. As the hierarchv
is created, configuration settings are applied if the instance name and a field name in th~
newly created component match one of the configuration settings specified before the com-
ponent was created. Configuration setting functions allow fields of integral type, string type,
and ovm_object type to be configured. Function seCconfi9_objectO additionally provides argu-
ment clone that indicates whether a field should be set to either a reference or a copy of the
argument specified with argument value of function set_confl9_objectO. Global functions cor-
responding to configuration functions of class ovm_component are provided in order to allow
configurations to be set at the top-most level of hierarchy where no component of type
ovm_component is yet created. The following comments apply to component configuration
usage:
o Configuration functions can manage only automated fields (section 6.4).
o Instance name and field name arguments (i.e., inst_name, field_name) may optionally
include wild-card characters "*,, and "?" allowing the same configuration setting to
be applied to different fields within the same component and/or to different compo-
nents across the sub-hierarchy.
o Base class ovm_component is defined in a way that configuration settings for a com-
ponent instance are applied as part of calling function buildO of that instance. As such,
using function bulJd() is mandatory for having the configuration settings for an
instance to take effect. A consequence of this implementation is that all configuration
settings for a component instance have been applied by the time its function bulJdO is
called, and therefore these settings can be used during the building of the sub-hierar-
chy rooted at that instance.
o The argument inst_name specified with each configuration function is assumed to be
relative to the hierarchical path of the component from which it is called.
o Configuration settings in a higher scope take precedence to the ones in a lower scope
by default. The reason for this ordering is to allow configuration settings at the higher
layers of the hierarchy to override those specified at the lower layers.
o Within the same scope, the last configuration setting matching a field takes prece-
dence over previous settings for the same field.
The use of configuration functions is illustrated by showing the construction of the
component hierarchy shown in figure 7.1. Even though the component hierarchy is built
recursively from top to bottom. the implementation of the program that creates this hierarchy
166 OVM Component Hierarchy

is started from the lowest level of the hierarchy. These steps are shown in the remainder of
this section.
The first step is to create the object and components at the lowest level of the hierarchy.
The content of components at the lowest level of hierarchy do not affect the build process
and as such are not shown in this implementation:

I Program 7.1: Leaf component and object class declarations


1 class ex_config extends ovm_object;
2 'ovm_object_utils(ex_config)
3 end class
4
5 class ex_block extends ovm_component;
6 function new(string name, ovm_component parent);
7 super.new(name, parent);
8 endfunction
9 'ovm_componenCutils(ex_block)
10 endclass
11
12 class ex_top_comp extends ovm_component;
13 function new(string name, ovm_component parent):
14 super.new(name, parent);
15 endfunction
16 'ovm_component_utils(ex_top_comp)
17 endclass

This program shows the implementation of object ex_config, and components ex_block
and ex_top_comp. It is assumed that contents of ex_top_comp may need to be accessed by
components at lower layers of the hierarchy. In addition, note that as required, these class
declarations include the appropriate automation macros (lines 2, 9, and 16) to allow these
classes to be handled by the OVM factory (section 6.3). Also, function newO must be defined
for all classes derived from ovm30mponent (lines 6, 13).
The next step is to define the class for module component ModuleComp:
r-----
!'rogram 7.2: ex_module class declaration, showing function build(}
1 : class ex_module extends ovm_component;
2 int Mod Data, LocalFlag;
3
string HierFlag;
4
ex_block BlockComp, CondBlockComp;
5
ex_config RefGlobConf, ModConf;
6
7 ex_top_comp ReITOP_COMP;
8
9 function new(string name, ovm_component parent):
10 super.new(name, parent);
11 endfunction
12
function void buildO:
13
14 Super.buildO;
15 $cast(BlockComp, create_component{"ex_block", "BlockComp"));
Blo~kComp.buildO;
16
17 If (HlerFlag == "must have CondBlockComp") begin
18 $cast(CondBlockComp, create_component("ex_block", "CondBlockComp"));
19 endCondBlockComp. build()'
,
20 endfunction
21
22 . ovm component t'l
~ovm ,...u I S_begin(ex module)
23 -field_1nt(ModData,OvM_DEFAULT)

------------------------------------------
Hierarchy and Configuration Constructs 167

24 .ovm_fieldJnt(LocaIFlag, OVM_DEFAULT)
25 'ovm_field_string(HierFlag,OVM_DEFAULT)
26 'ovm_componencutils_end
27 endclass
L ______________________ __

The implementation of class ex_module is shown in the above program. Note that no
configuration is passed from this module to lower level components (no arrows from this
module to lower level components in figure 7.1). Therefore, the implementation of function
bulidO for this class requires only that the appropriate components be constructed, and no
configuration settings are needed. It is assumed that any configurations applied to this com-
ponent by higher layer components have already taken effect before entering function bulldO.
The first step in function bulldO is to call the buildO method of the super class (line 13). Com-
ponent BlockComp is then created and built (lines 14, 15). Conditional component CondBlock·
Comp is then created only if hierarchical configuration field HlerFlag is appropriately set
(lines 16-19). Note that RefGlobConf, ModConf, and RefTOP_COMP are all set to their appropri-
ate values (set through configuration from higher layer components) by the time function
build() is called. This configuration mechanism is shown in the implementation of ex_system,
shown in the following program:

'Program 7.3: ex module class declaration, showing function buildO


1 class ex_system extends ovm_component;
2 Int SysFlag;
3 ex_module ModuleComp;
4 ex_config RefGlobConf, SysConf;
5 ex_top_comp RefTOP_COMP;
6
7 function new(string name, ovm_component parent);
8 super.new(name, parent);
9 =
SysFlag 15;
10 endfunction
11
12 function void buildO;
13 super.buildO;
14 set_config_int("ModuleComp", "LocaIFlag", SysFlag);
15 set_config_object("ModuleComp", "ModConf', RefGlobConf, 1);
16 $cast(ModuleComp, create_component("ex_module", "ModuleComp"»;
17 ModuleComp.buildO;
18 endfunction
19
20 'ovm_component_utils_begin(ex_system)
21 'ovm_fieldJnt(SysFlag,OVM_DEFAULT)
22 'ovm_field_object(ModuleComp,OVM_DEFAULT)
23 'ovm_field_object(RefGlobConf,OVM_DEFAULT)
24 'ovm_field_object(SysConf,OVM_DEFAULT)
25 'ovm_field_object(RefTOP_COMP, OVM_DEFAULT)
26 'ovm_component_utils_end
27 endclass

Component SystemComp is an instance of class ex_system shown above. This compo-


nent contains field SysFlag and component RefGlobConf whose values should be passed to
component ModuleConf when building that component. Field SysFlag is initialized in the con-
structor for class ex_system (line 9) and component RefGlobConf is initialized through config-
uration from the higher layer and therefore, has a valid value when function build() is entered.
Configuration functions set_config_int() and set_config_objectO are used to specify that fields
LocalFlag and ModConf of instance ModuleComp should be set to SysFlag and RefGlobConf
168 OVM Component Hierarchy

respectively (lines 14, 15). In addition, argument clone of set_config_objectO is set to 1 in


order to indicate that a cloned copy of RefGlobConf should be assigned to ModConf of instance
ModuleComp.

The implementation of the environment top level is shown in the following program:

"Program 7.4: Top level program for creating the component hierarchy
1 module top;
2 'include "ovm.svh"
3 'include "program-7.1"
4 'include "program-7.2"
5 'include "program-7.3"
6
7 string GlobFlag;
8 ex_system SystemComp;
9 ex_tap_camp TOP_COMP;
10 ex_config GlobConf;
11
12 initial begin
13 GlobFlag = "must have CondBlockComp";
14 $cast(GlobConf, ovm_factory::create_object("ex_config", "", "GlobConf'));
15 $cast(TOP_COMP,
16 ovm_factory::create_component("ex_top_comp", "", "TOP_COMP", null));
17 TOP_COMP.build();
18
19 set_config_string("SystemComp.ModuleComp", "HierFlag", GlobFlag);
20 set_config_object("SystemComp", "SysConf', GlobConf, 1);
21 se,-confi9_objecW''', "RefGlobConf', GlobConf, 0);
22 set_config_object("''', "RefTOP _COMP", TOP_COMP, 0);
23
24 $cast(SystemComp,
25 ovm_factory::create_component("ex_system", "", "SystemComp", null));
26 SystemComp.buildO;
27 ovm_prinUopologyO;
28 end
29 end module
~

Field GlobFlag, component TOP_COMP, and object GlobConf are assigned and created
first (lines 13-17) since they affect the sub-hierarchy rooted at component SystemComp. Note
that this program is in the scope of a module block, and as such, the globally defined
ovm_factory is used to create the appropriate components and objects (lines 14, 16). For the
same reason, global configuration functions are used to specify configuration settings for the
sub-hierarchy rooted at component SystemComp (lines 19-22). Function set30nfi9_stringO is
first used to indicate that the value of field HierFlag in instance SystemComp.ModuleComp
should be set to the value offield GlobFlag (line 19). Next, global function set_config_objectO
with clone argument set to 1, is used to indicate that component SysConf of SystemComp
should be set to a cloned copy of object GlobConf (line 20). Global function
seCconfiQ_objectO with clone argument set to 0, is then used to indicate that object RefGlob-
Conf and component RefTOP_COMP in any instance (as indicated by "*") should point to
object GlobConf and component TOP_COMP in the top level of hierarchy respectively (lines
21, 22). With these configuration settings in place, instance SystemComp is then created and
built by using the factory and calling function buildO (lines 24-26).
Verification Environment Component Types 169

7.3 Verification Environment Component Types


..- - - - - - - - - - - - -.. - - - - - - -..
..-----.------ -- -,-----.
-------------.-.~- '--"---
A verification environment is composed of a set of hierarchical components that communi-
cate to produce the desired behavior. Given this general view, the implementation of a verifi-
cation environment can be described in terms of the following:
• Creating a component hierarchy
• Specifying the interconnection between environment components
• Implementing the specific requirements of each component for their intended Use
(e.g., a monitor has different implementation requirements than a driver)
Implementation of a component hierarchy is described in section 7.2. Transaction-based
connectivity between components is described in chapter 9. This section provides a general-
ized view of the verification environment in terms of the different kinds of components that
are present in the verification environment (subsection 7.3.1) and the constructs provided in
the OVM class library for implementing these specialized components (subsection 7.3.2).

"7.3.1 Verification Environment Components


Figure 7.2 shows the architectural view ofa verification environment described in chapter 3.
The main focus in this view is on how different components in the verification environment
are grouped to form the hierarchy of the verification environment. The following hierarchi-
cal components can be identified in this view:
• Verification environment top level
• Software verification components
• Interface verification components
• Module verification components
• System verification components
Verification environment top level is the main container for all components in the veri-
fication environment. An interface veritication component interacts directly with the DUV.
Each module verification environment interacts with mUltiple interface veritication environ-
ment to implement module level verification environments. Software verification compo-
nents provide the abstraction necessary for interacting with a DUV's software stack during
the simulation runtime. A system level verification component interacts with mUltiple mod-
ule, software, and interface veritication components to carry out the system level verification
scenarios.
Figure 7.2 shows only interface, module, and system verification components. The spe-
citic view of the veritication environment for a project, however, depends on its integration
schedule where a layer of module/cluster/system level verification components will be in
place for each integration of smaller components into larger components (e.g., blocks to
modules to clusters to chips to systems). Features of these components are described in chap-
ter 3.
The implementation ofa verification environment should proceed in a hierarchical fash-
ion. In the first step, interface verification components are implemented. In the next step.
170 OVM Component Hierarchy

Verification Environment DUV

Hardware

Figure 7.2 Complete Architecture of a Verification Environment

module verification components are implemented and used in verifying modules. In the next
step, system verification components are implemented to verify system level behavior.
The implementation view of an interface verification component is shown in figure 7.3.
This component contains the following:
• Driver
• Monitor
• Sequencer

Interface Verification Component

I
I
I Agent
Bus Monitor
I --
r
1
Agent r-
Agent 8- DUV

I Monitor
~ ~
I
I
Sequencer
Il Driver
~
V
I

Figure 7.3 Interface Verification Component Implementation Hierarchy


Verification Environment Component Types 171

• Verification component agent


• Verification component
An interface verification environment encapsulates the abstraction necessary for inter-
acting with a device port that follows a well defined protocol (e.g., USB, Pel-Express).
Given that a design may have multiple ports that communicate using the same protocol (e.g.,
a network switch with multiple Ethernet ports), one instance of an interface verification com-
ponent should be able to interact with multiple device ports of the same type. An agent pro-
vides the abstraction for the interaction with a single device port, and can be used to model
the varying types of devices supported by a single protocol. An interface verification envi-
ronment may contain mUltiple agent instances, each communicating with a different device
port. This configuration allows a single interface verification component to be used for all
device ports that follow the same protocol. Each agent contains a driver, a monitor/coverage
collector, and a sequencer.
A module verification component interacts with multiple interface verification compo-
nents to verify a module. Module verification components interact with the DUV through
interface verification components and therefore do not require a driver. They do, however,
contain a sequencer, and a monitor for properties relevant at the module level. A system ver-
ification component interacts with multiple module and interface verification components to
verify the functionality at the system level.
Figure 7.4 shows a graphical view of different types of components that are present in a
verification environment. This view also identifies two types of component connectivity:
physical interfaces and transaction interfaces. Physical interfaces are implemented using
SystemVerilog port and interface constructs (see section 12.7 for an example implementa-
tion). The implementation of transaction interfaces using the OVM class library is described
in chapter 9. The following component types, categorized based on their interface types, can
be identified in this figure:
• Environment
• Sequencer (layered and non-layered)
• Driver (master and slave)
• Monitor (DUV and transaction)
• Scoreboard
Based on this view, an interface verification component consists of sequencer, driver,
and monitor components placed in an environment component. Module and system verifica-
tion components consist of sequencer, monitor, and scoreboard components placed in an
environment component. All verification components are also placed in an environment
component modeling the top level of the verification environment hierarchy. It should be
noted that in a class-based implementation, the physical interface of a component is provided
as a virtual interface that is a field of the class modeling that component (section 13.3).
The implementation model of these components using the OVM class library is
described in the next section.
172 OVM Component Hierarchy

Environment
Component q MasterDriver ~ ~ Physical Interface

~ Slave Driver q. c:::::> Transaction Interface

Layered Scoreboard or
Sequencer Transaction Monitor

Figure 7.4 Component Interface Connectivity

7.3.2 OVM Models of Verification Environment Components


The hierarchical structure ofan OVM-based verification environment is shown in figure 7.5.
This structure is composed of the following OVM classes derived from class
oym~threaded_component:

• ~Ym_test
• oym_eny
• oYm_agent
• oYm_monitor
• oym_drlyer
• oYm_sequencer, oym_Yirtua'-sequencer
• oYm_scoreboard

Environment components are modeled using class oYm_enY. An environment compo-


nent is the container for either an interface or a module/system verification component, as
well as the container for the top level of the verification environment hierarchy. An environ-
ment modeling a verification component may contain multiple instances of an agent compo-
nent modeled using class oYm_agent, with each agent component corresponding to an
instance of the core functionality ofa verification component. A verification component may
optionally include a bus monitor (section 3.2). An agent may be passive or active. An active
agent contains a monitor modeled using class oYm_monltor, a sequencer modeled using class
ovm_sequencer and a monitor modeled using class oym_monitor. A passive agent contains a
monitor. A module VC contains the definition of a virtual sequencer modeled using class
oym_Ylrtual_sequencer and a set of virtual sequences placed in the sequence library of this vir-
tual sequencer. The actual instance of the virtual sequencer can, however, be placed in the
top level component of the verification environment.
A driver is modeled using class oYm_driver and contains all functionality that is neces-
sary for an agent to interact with the DUV such that either information is driven into the
DUV port or the necessary handshaking for collecting DUV outputs is applied at the DUV
ports. A monitor is modeled using class oym_monitor and contains all functionality that is
necessary to passively observe DUV port activity or the monitors in other verification envi-
ronment components. A monitor component contains the necessary checking and coverage
collection functionality. A sequencer is modeled using class oYm_sequencer and contains the
functionality to create scenarios relevant to the next downstream component it interacts with.
The next downstream component may be the DUV port, or a verification component. A
Verification Environment Component Types 173

ovm_test (e.g .. xbar_defauIUest)

ovm_vlrtuaLsequencer

Figure 7.5 Verification Environment Component Types Provided by OVM

sequencer component may act autonomously or be controlled by the sequencer component in


another verification component.
The system level container of all verification components is also modeled using an
obj ect of type ovm_env. All interface and module/system verification components are instan-
tiated inside this top level environment component. This component is the top-most struc-
tural block in the verification environment, in that it represents the stmctural make-up of the
verification environment. An implementation of a verification environment represented by
an ovm_env component may be driven using different initial conditions, generation con-
straints, and test-specific configurations. All such test-specific settings are captured in a test
component modeled with class ovm_test, which also acts as the container of the top-most
environment component. Given this configuration, creating a new test requires the creation
of a new test component that includes an instance of the top-most environment component
along with its related test-specific settings. A unit testcase requiring a customized verifica-
tion environment can be created by instantiating a customized top level environment compo-
nent. This organization of the verification environment hierarchy allows all testcases to be
uniformly maintained as top level test components where customizations of the verification
environment for each testcase are managed through instantiation of the appropriate top level
environment component in that testcase.
Component-specific classes provided by the OVM class library are mainly lIsed to
clearly mark the intent in implementing each environment component. Providing a dedicated
class name for each component type allows implementation intent to be clarified by using the
appropriate class type. In the current release of the OVM class library, class ovm_sequencer
provides special methods for generating and managing sequences (section 8.2.2). In addition,
classes ovm_env and ovm_test provide special features for starting and stopping the simula-
174 OVM Component Hierarchy

tion and for handling simulation phases (section 7.4). The future release of the GVM class
library may add additional features to each component definition as the need for such fea-
tures are identified.

7.4 Simulation Phase Control


Simulation of a verification environment can naturally be divided into multiple phases.
These phases correspond to steps for building the environment, simulating the environment,
and collecting and reporting results.

In the OVM class library, the concept of simulation phases is implemented as part ofthe
following classes:
• oYm_component
• oYm_threaded_component
• oym_eny
• oYm_test

An oym_component implements the notion of hierarchy and is used for building a hierar-
chical environment. Additionally, this class implements simulation phases that do not con-
sume simulation time (i.e., occur in zero time). An oYm_threaded_component is derived from
an OYm_component and adds a time consuming phase to the set of predefined phases. An
ovm_env is derived from oYm_threaded_component, and adds special utilities for controlling
the execution of individual simulation phases. All components in a verification environment
should be derived from ovm_env.
An oYm_test is derived from oYm_env, and models the top level component in the hierar-
chy. This top level component contains an instance of the verification environment hierarchy
customized to the requirements ofthe specific test to be run by this top level test component.
Using this approach, different tests running on the same verification environment hierarchy
are created as follows:
• Class test1 is derived from oYm_test, and contains an instance of the verification envi-
ronment hierarchy (an object of type oym_eny) initialized to the requirements of the
specific test to be carried out by test1.
• Class test2 is derived from ovm_test, and contains an instance ofthe verification envi-
ronment hierarchy (an object of type oym_eny) initialized to the requirements of the
specific test to be carried out by test2.
• Verification is started by calling global function run_test() with the name of the test
class that should be executed in the current simulation run (e.g., run_test(Utest1"1). A
test name specified using plus-argument OVM_TESTNAME overrides any name passed
to function ru"_testO (section 7.4.1).
Table 7.1 shows the predefined phases defined by the GVM class library. It is important
to note that functions build{) and resolye_all_bindings() are called implicitly as part of execut-
ing this flow.
Simulation Phase Control 175

Phase Callback Type Defined in Class Description


-First phase called.
post_new function ovm_component -Function buildO of component is called before
entering this function.
-Binding of transaction ports is automatically per-
elaborate function ovm_component formed at the end of this phase by calling function
resolve_all_bindingsO of each component
-It is assumed that by the time this phase is started,
pre_run function ovm_threaded_component the hierarchy is fully created and all component
fields are initialized.
-The only time-consuming phase.
run- task ovm _threaded_component -May be suspended, stopped, or resumed through
methods defmed in ovm_env.
-Used for extracting information from the simula-
extract function ovm_component
tion run.
-Used for checking results extracted from the simu-
check function ovm _component
lation run.
-Used for reporting the result of checking simula-
report function ovm _component
tion run results.

Table 7.1: Predefined Simulation Phases

Table 7.2 lists the predefined callback methods used for customizing the execution of
these phases to specific requirements of a verification environment components. Table 7.3
lists function and methods calls used for controlling the execution and termination of these
simulation phases.

Phase Type Returns Name Defined in Class Called After


post_new virtual lunchon void post_newO ovm_componenl run_testO
elaborate virtual function void end_ oCelaborationO ovm_component run_testO
pre_run v irtual function void pre_TUnO ovm_threaded_component run_testO
nlO virtual task runO ovm_threaded_component run_testO
run virtual task stop(string ph_name) ovm_component global_stop _requestO
extract virtual function void extractO ovm_component globaUtop _requestO
check virtual function void checkO ovm_component global_stopJequestO
report virtual function void reportO ovm _component global_stop_requestO

Table 7.2: Phase Callback Methods

7.4.1 Starting the Simulation Phases


The OYM class library provides global function run_testO for starting the execution of
phases listed in table 7.2. The execution of phases for the verification environment is orga-
nized in terms of top level components.
A top level component is the top-most component for a verification environment hierar-
chy. A top level component is assumed to be of type Dvm_test (figure 7.5). It is possible for a
simulation runtime to contain more than one top level component, as described below. The
top level components that participate in a simulation run are decided as follows:
• The name of a class derived from Dvm_test can optionally be passed to function
176 OVM Component Hierarchy

Type Returns Name Defined in class


function string get_current_global_philse() ovm_component
task global_stopJequestO ovm_component
function void setJllobal_timeout(time t) ovm_component
function void setJllobal_stop_timeout(time t) ovm_component
stopJequest(stop_enum s=OVM_STOP_ALL)
task (one ofOVM_STOP_CHILDREN, ovm_component
OVM_STOP_SELF, OVM_STOP_ALL)
virtual I
void killO ovm_threaded_component
function
task run_test(string test_name) ovm_env
task run_test(string test_name) Global Function

Table 7.3: Phase Start and Control Methods

run_testO. In this case, an instance of this class is created and this instance is used as
the top level component.
• Ifno argument is passed to function run_testO, and plus-argument OVM_TESTNAME is
not given, then each object of type ovm_test that is instantiated in the SystemVerilog
code is assumed to be a top level component.
• Simulation plus-argument +OVM_TESTNAME=<test_name> can be used to specify the
name of the test that is to be started for a given simulation run. Test name tesCname
specifies a class type derived from class ovm_test. This form of specifying a test name
has precedence over a test name that is passed as argument to function run_testO. This
behavior allows tests to be selected without having to recompile the SystemVeri log
code.
All top level components are processed concurrently. Given a top level component,
methods for each phase are executed using a depth-first postorder traversal, where the phase
method of a component is called after the phase method for its child components (e.g., func-
tion preJun() of a component is called after function pre_runO of its child component). The
exception to this behavior is that for the run phase, task runO of each component is started
using a depth-first preorder traversal where task runO of a component is started before the
task runO of its child components.

7.4.2 Stopping the Simulation Run Phase


Except for the run phase, all simulation phases are modeled using functions, and therefore,
occur in zero simulation time. The run phase is the only phase that consumes simulation
time. At the start of the run phase, task run() of all components in the hierarchy are started
using a depth-first preorder traversal.
The end of run phase is decided by one of the following mechanisms:
• By default, the run phase of the hierarchy rooted at a top level component ends when
task runO of all components in this hierarchy have completed.
• Function seCglobal_timeout() of class ovm_component can be used to specify a time
value at which the run phase of a component should end. The default value for this
timeout is 0 which indicates that no such timeout exists.
• Task stop-requestO of class ovm_component can be used to selectively end the run
Simulation Phase Control 177

phase of a single component, the hierarchy rooted below a component, or both (see
argument options in table 7.3). The default behavior is that task run() of all compo-
nents affected by calling this task are terminated immediately.
• Field enable_stop_lnterrupt and virtual task stop() of class ovm_component allow the
default stop behavior to be modified. This modification mechanism, described later
in this section using an example, allows any component to raise an objection to, and
therefore prevent, the ending of the run phase for this component, and therefore, the
hierarchy in which it exists.
• Function set_global_stop_timeout() of class ovm_component can be used to specify a
timeout value for the maximum length oftime a component may hold an objection to
ending the run phase. The default value for this field is 10,000 time units.
• Task global_stop_request() of class ovm_component can be used to call stop_request()
for all active top level components. .
• The global run phase completes when the run phase of all top level components have
completed,
The mechanisms described in this section can be applied to any component types
derived from ovm_component (e.g., ovm_monitor, ovm_env, ovm_test, etc.)
The following program shows an example of redefining simulation phase callback
methods to control the completion of the run phase:

~Program 7.5: Redefining simUlation phase callback methods


1 class top_sve extends ovm_env;
2 'ovm_componencutils(top_sve)
3
4 function new (string name='''', ovm_component parent=null);
5 super,new(name, parent);
6 endfunction: new
7
8 function void raise_objection(); enable_stopjnterrupt ++; endfunction
9 function void lower_objectionO; enable_stop_interrupt --; endfunction
10
11 function void pre_runO:
12 raise_objectionO;
13 endfunction
14
15 task stop(string ph_name="run");
16 while (enable_stopjnterrupt) @(enable_stopjnterrupt);
17 endtask
18
19 function void extractO;
20 'message(OVM_INFO, ("Now in phase:", geCcurrent-lJlobal_phase()))
21 'message(OVM_INFO, ("phase extract entered at time",$time))
22 endfunction
23
24 task run();
25 'message(OVM_INFO, ("Now in phase:", get_current-lJlobal_phase()))
26 #1000; /I represents simulation time consumed by this component run phase
27 lower_ objection();
28 endtask
29 endclass
L

This program shows the implementation of class top_sve, the top level component con-
taining the verification environment hierarchy. The hierarchy rooted at this component does
not relate to the focus of this example and is not shown. The implementation of this class
178 OVM Component Hierarchy

shows the use of predefined field enable_stop_interrupt of class ovm_component for prevent-
ing the run phase of this class from being stopped by a parent component. This approach is
based on the behavior that if field enable_stop_interrupt is larger than zero, then a call to task
stop() of this class must be completed before the run phase of this class can be terminated. In
this implementation user defined functions raise_objectionO and lower_objectionO are defined
to raise and lower the value offield enable_stop_interrupt (lines 8, 9). Before the run phase is
started, function raise_objection() is called in function preJun() (line 12). This means that as
long as this objection remains raised and the stop timeout value, if provided, has not expired,
then the run phase of this component will not end. Function stopO is also defined so that this
function completes only when the value of field enable_stop_interrupt is set to 0 (line 16).
Function extract() of this class is also defined to print the time at which this phase starts,
thereby giving the simulation time at which the run phase completes (lines 19-22). Finally,
task runO is defined to call function lower_objection() after some simulation time has passed.
The use of this class in a test is shown in the following example:

:Program 7.6: Defining and running a new lestcase


1 module top;
2 'include "ovm.svh"
3 'include "program-7.5" /I class lop_sve
4
5 class my-test extends ovm_test;
6 'ovm_component_ulils(my_lesl)
7 top_sve sve;
8
9 function new (sIring name="", ovm_component parent=null);
10 super.new(name, parent);
11 endfunction: new
12
13 virtual function void build(};
14 super.build();
15 $cast(sve, create_component("top_sve", "sve_15"));
16 sve.buildO;
17 endfunction
18
19 task runO;
20 global_stop_request();
21 /I Alternatively call function below to just stop hierarchy rooted here
22 I/stop_request(OVM_STOP_ALL);
23 endlask
24 end class
25
26 initial run_test(nmy_test");
27 endmoduJe
~-----

The above example shows the implementation oftestcase my-test which is derived from
class ovm_test (line 5). This class instantiates class top_sve (line 7) which is configured and
built in function buildO (line 13-17). Task runO of this class is defined to stop all running
components anywhere in the environment by immediately calling global_stop_requestO (line
20). The use of this function is in contrast with function stopJequestO which only controls
the tenninatiol1 of the run phase for components in the hierarchy of its calling component
(line 22). The use of global_stop_requestO is recommended over stopJequest() unless there is
specific requirement for stopping the run phase for only a portion of the verification environ-
ment hierarchy. Global function run_testO is used to run testcase my-test (line 26).
Simulation Phase Control 179

In this implementation, even though task global_stoPJequestO is called at simulation


time 0 immediately after entering task runO of my_test (line 20), the run phase continues until
time 1,000 when task runO of top_sve completes and lowers its objection to ending the run
phase, thereby allowing task stop() to complete.
The mechanism for raising objections to ending the simulation run phase is useful in
providing distributed control for when the run phase can end. This approach is also useful for
preventing the run phase from tenninating during activities that must be completed once
started. For example, a transaction that has already been generated by a sequencer must be
processed before the run phase can end. In this case, an objection can be raised by the
sequencer before a new transaction is generated and then lowered after the processing of this
transaction is completed.
180 OVM Component Hierarchy
CHAPTER 8 OVM Transaction
Sequences

Effective creation of verification scenarios is perhaps the most important part of carrying out
a verification project. After all, everything in a verification environment is put in place to
support the ultimate goal of creating and monitoring all of the scenarios that must be veri-
fied.
In modem verification environments, verification scenarios are modeled as a sequence
of items where both the sequence items and the sequence itself can be randomly or determin-
istically defined and created at simulation runtime. This approach brings the power of ran-
domization to scenario generation, and allows for fine-grain runtime control of how
sequences are built. The ability to define a sequence item at any level of abstraction (i.e.,
from low-level driving of signals to high level issuing a command to a verification compo-
nent) allows this approach to be applicable at any level of abstraction. This approach also
facilitates the creation of hierarchies of sequences where complex sequences are created by
combining lower level sequences.
Effective construction of verification scenarios as sequences of items requires a mix of
language constructs and an infrastructure for sequence generation and driving. A sequence
generation infrastructure should provide the ability to define:
• Flat sequences (series of sequence items driven by one driver)
• Hierarchical sequences (sequence of sequences driven by one driver)
• Virtual sequences (sequence of sequences driven by multiple drivers)
• Layered sequences (sequences driving other sequences)
• Reactive sequences (ability for a sequence to react to its environment)
This chapter provides a detailed description of sequence generation capabilities pro-
vided by the OVM class library. This chapter also provides small implementation examples
to better illustrate the concepts discussed in this chapter. A comprehensive and detailed
example of sequence generation is provided in chapter 14.
182 OVM Transaction Sequences

Verification scenarios can be modeled as a sequence of transactions, where each transaction


models an atomic interaction with a module in the verification environment. For example, in
verifying a memory model, a verification scenario may consist of writing to a memory loca-
tion and then reading back the value at that memory location in order to make sure that the
read value is in fact the same as the value that was initially written to that memory location.
Verification scenarios can be categorized as single-sided or multi-sided. A single-sided
scenario interacts with only a single DUV interface (e.g., writing to a memory location and
then reading back that memory location). A multi-sided scenario, however, requires interac-
tion with multiple DUV interfaces (e.g., injecting a packet at DUV interface 1 and then
injecting a packet at DUV interface 2 to verify that DUV transmits these packets on interface
3 with the correct priority).

In its simplest form, a sequence-based model of a verification scenario consists of a sin-


gle stream of transactions that is to be consumed by a single driver. This simple configura-
tion consists of the following elements:'
• Sequence item
• Sequencer
• Sequence item interface
• Driver
Figure 8.1 shows a general view of this architecture. A sequence item refers to a trans-
action describing an atomic interaction with the environment. In the memory verification
example, writing to memory can be considered an atomic step and hence defined as a
sequence item. Sequence items may describe complex activities such as initializing a system,
or an activity as simple as applying a value to a signal. In describing a memory write opera-
tion as an atomic operation, all details related to how the memory is written and the fact that
this operation may take multiple cycles are considered to be a part of the sequence item
description, which is to be handled by the driver. This abstraction allows the sequence gener-
ation mechanism to focus on generating the sequence without considering the low-level
details of how each item is handled by the environment. In most cases, sequence items con-
tain randomly generated properties. For example, in a memory write operation, data and the
address values may be randomly generated. A sequence item declaration defines a new
sequence item type and refers to the description of a sequence item. A sequence item
instance refers to the actual item created while applying the relevant random generation con-
straints.

Sequencer Driver DUV

0000
Sequence Item Interface

Figure 8.1 Single-Sided Sequence Generation Architecture


Verification Scenarios and Sequences 183

A sequencer produces a single stream of sequence items where all of the sequence items
have the same type. The ability of a sequencer to produce only a single stream of items limits
this configuration to single-sided scenarios. As will be shown later in this chapter, a virtual
sequencer is used to remove this limitation and allow multi-sided scenarios to be handled by
the sequence generation facilities of the OVM class library.
A driver is the consumer of sequence items produced by a sequencer. A driver may
drive the sequence items directly into a DUV interface (figure 8.1). A driver may also repre-
sent the sequencer in a lower layer of the verification scenario generation hierarchy. For
example, a TCP/IP layer sequencer, producing items representing TCP/IP layer traffic, may
pass its items to an Ethernet sequence that uses a single TCP/IP layer item to generate a
sequence of Ethernet layer items.
A sequence item interface is used to connect a sequencer to a driver. A sequence item
interface is a transaction interface (chapter 9) that is customized to the requirements of
sequence generation architecture (i.e., it supports a customized set of methods instead of the
standard transaction interface methods listed in section 9.3).
The interaction between a sequencer and a driver can be one of:
• Push mode
• Pull mode
In push mode, a sequencer drives a produced item into a driver when that item is gener-
ated, and waits until the driver consumes this item. In pull mode, a driver requests the
sequencer to provide it with a sequence item. The pull mode of interaction is superior to push
mode. The reason is that first, in pull mode, a sequence item is consumed immediately after
it leaves the sequencer. This means that the sequencer can customize the contents of the
sequence item to the timing of sequence item consumption. Second, the single stream of
sequence items leaving a sequencer may represent multiple concurrently running scenarios,
and pull mode allows the sequencer to arbitrate between items generated by these concur-
rently running scenarios based on the item that is best suited for consumption at the time tlle
driver requests the next item (see is_relevant() in section 8.8.2). In addition, a pull mode
implementation can easily be turned into a push mode implementation by adding an active
transaction channel between the sequencer and the driver, containing a thread of execution
that reads the next available sequence item from the sequencer, passes it to the driver, and
blocks until this sequence item is consumed by the driver. The OVM class library fully sup-
ports the pull mode of operation, with support of push mode planned for future versions.
The configuration in figure 8.1 shows a pull mode interaction between the sequencer
and the driver. In this mode, the driver is the initiator component and the transaction con-
sumer, and the sequencer is the target component and the transaction producer. The sequence
item interface defined in OYM supports a bidirectional transaction transfer across this inter-
face where the driver requests the next sequence item from the sequencer and then returns
the result of executing that item through the interface.
The remainder of this chapter describes the sequence generation utilities of the OYM
class library.
184 OVM Transaction Sequences

8.2 Sequencers
The architecture of a sequencer component is shown in figure 8.2. This architecture is identi-
fied by:
• A default sequence item type
• A library of predefined and user defined sequences
• A set of running sequences
• An arbiter
• A sequence item interface
A sequencer produces a single stream of sequence items whose base type is given by the
sequencer's default sequence item type. A sequencer contains a sequence library which is a
container of predefined and user defined sequences that can either be started as running
sequences each having an independent thread of execution, or used as subsequences in hier-
archical sequences. Each running sequence generates a stream of sequence items, feeding
these items into an arbiter that uses a first-in-first-out (FIFO) policy to select among relevant
items from the incoming streams. The OVM class library provides a mechanism for allowing
each sequence to define whether or not the items it generates are relevant to the current veri-
ficationcontext (see is_relevant() in section 8.8.2). The driver connects with the sequencer
through a sequence item interface that forwards driver requests for a new item to the arbiter.

Sequencer Driver

G Sequence Item Type 1m] Sequence Type


@ Sequence item Instance ® Sequence Instance (Running Sequence)

~ Starting a sequence

Figure 8.2 Sequencer Architecture

The implementation of a sequencer using the OYM class library is described through
the following steps:
• Sequence item definition
• Sequencer and the default sequence item declaration
Sequencers
-
185
-----------------------------------------------------------------------
• Defining flat and hierarchical sequences
• Sequence library and the predefined sequences it contains by default
• Sequence activation
• Arbitration mechanisms
• Sequence item interface
These topics are discussed in the following subsections.

8.2.1 Sequence Items


A sequence item is the atomic object that is produced by a sequencer. The lifetime of a
sequence item consists of the following major stages:
• Creation
• Randomization
• Transfer to driver
• Execution
All sequence items are generated by sequences, which then pass these sequence items to
the sequencer for transfer to a driver. A sequence item is created in a sequence. The timing
for the transfer of a sequence item to a driver is decided by the interaction between the
sequence, sequencer, and the driver. This means that a sequence item may be passed to a
driver some time after it is created. A sequence item can be randomized immediately after it
is created (i.e., early randomization) or right before it is transferred to the driver (i.e., late
randomization). The mechanism provided in the OVM class library for controlling this ran-
domization behavior is described in section 8.6.2.

The implementation of a sequence item using the OVM class library is shown in the fol-
lowing example (see section 12.2 for XBar design specification):

I Program 8.1: Sequence item Implementation


1 class xbar_xmt_packet extends ovm_sequencejtem;
2 rand byte payload;
3 .ovm _ object_ utils_begin(xbar_xmt_packet)
4 'ovm_fieldjnt{payload,OVM_ALL_ON)
5 'ovm_object_utils_end
6 function new (string name = "xbar_xmt_packet",
7 ovm_sequencer_base sequencer = nUll, ovm_sequence parent_seq = null);
8 super.new(name, sequencer, parent_seq);
9 end/unction: new
10 endclass: xbar_xmt_packet

The above example highlights the following general guidelines that must be followed
when implementing these elements of a sequence generation facility:
• Sequence item classes must be derived from class ovrn_sequence_item (line 1).
• The declaration of a new sequence item must include the declaration of its construc-
tor (lines 6-9).
• Macro ovm_object_utilsO must be used in order to allow a newly defined sequence
item to be managed by the OVM factory. Field automation macros can also be speci-
fied by using the begin/end variation of this macro (lines 3-5).
] 86 OVM Transaction Sequences

8.2.2 Sequencer Declaration


Sequencer implementation using the OYM class library is shown in the following example:

~ 8.2: Sequencer implementation


1 class xbar_xmt_sequencer extends ovm_sequencer;
2 int unsigned port_num;
3 'ovm_sequencer_utils_begin(xbar_xmt_sequencer}
4 'ovm_fieldJnt(port_num.OVM_ALL_ON)
5 'ovm_sequencer_utils_end
6
7 function new (string name="", ovm_component parent=null);
8 super.new(name, parent);
9 'ovm_update_sequence_lib_andjtem(xbar_xmCpacket}
10 endfunction: new
11 endclass: xbar_xmt_sequencer

The example shown the implementation of sequencer xbar_xmt_sequencer derived from


class ovm_sequencer, Macro ovm_update_sequence_lib_and_item() is used in the constructor to
mark class xbar_xmCpacket (defined in program 8.1) as the default sequence item (line 9)-
This means that sequencer xbar_xmCsequencer will be passing transactions of type
xbar_xmCpacket through its sequence item interface.

The above example highlights the following general guidelines that must be followed
when implementing a sequencer:
• A sequencer class must be derived from ovm_sequencer (line 1).
• Macro ovm_sequencer_utilsO or its begin/end variation must be used with a new
sequencer declaration (lines 3-5) to initialize its sequencer utilities. The begin/end
variation of this macro is used when the newly defined class contains fields that must
be automated. The argument to this macro is the name of the newly declared class in
which the macro is used.
• The declaration of a new sequencer must include the declaration of its constructor
(lines 7-10). Additionally, macro ovm_update_sequence_lib_and_item() must be used
in this constructor to define the default sequence item handled by this sequencer (line
9). The argument to this macro is the name of the default sequence item to be handled
by this newly declared sequencer. This default sequence item also gives the sequence
item type generated by sequence ovrn_simple_sequence (section 8.5.1).

The above example creates a minimal sequencer that produces a stream of sequence
items of type xbar_xmt_packet, and contains a sequence library with a set of predefined
sequences. The addition of user defined sequences to this sequence library is shown in the
next section. A predefined sequence of this sequencer is started by default, producing a
stream of randomized items of base type xbar_xmt_packet (section 8.5.1). This means that this
sequencer, as implemented, can be used for initial verification steps. Creating more interest-
ing scenarios, however, requires that new sequences be added to the sequence library and
started. This step is described in the next section.
Sequences 187

8.3 Sequences
A sequence contains a recipe for generating an ordered list of sequence item instances. A
sequence declaration describes these instructions and may contain randomizable fields and
fields that get customized to the specific conditions when the sequence is created. A
sequence declaration defines a new sequence we. A sequence instance refers to a specific
instance of sequence type that generates a sequence of item instances during the simulation
runtime. Multiple sequence instances may be created from a single sequence type, each
behaving differently because of their randomizable fields and other fields initialized to the
specific conditions when that sequence instance was created.
A flat sequence is defined only in terms of sequence items. A flat sequence contains the
following types of information:
• Sequence item(s) to generate
• Dynamic constraints 1 that should be applied to each item during generation
• Randomization control (e.g., use of functions constrainCmode() and rand_mode() to
modify sequence item static constraints)
• Flow control information (e.g., timing)
• How environment conditions affect item generation (reactive sequences)
• Relationship between consecutively generated items
A sequence declaration is identified by:
• Sequence items and/or subsequences
• Action block
Sequence action block describes how and in what order items and subsequences are
generated. A graphical view of this process is shown in figure 8.3. In this example, sequence
51 contains three sequence items A, B, and c. The action block for this sequence indicates
that item A should be generated, followed by item B, followed by item A, and followed by
item c. The generation of sequence 51 produces a sequence of items A, B, A, and c
which are passed to the sequencer in the order of their generation.
The implementation of a flat sequence using the OVM class library is shown in the fol-
lowing example:

'Program 8.3: Flat sequence implementation


1 class xbar_xmt_seq_flat extends ovm_sequence:
2 rand byte default_payload:
3 rand byte count:
4 constraint c {default_payload < 10:}
5
6 . ovm _sequence _ utils_begin(xbar_xmt_seq_flat, xbar_ xmt_sequencer)
7 'ovm_field_int(defauICpayload,OVM_ALL_ON)
8 'ovm_sequence_utils_end
9
10 function new(string name="", ovm._component parent=null):
11 super.new(name):
12 endfunction

I. Static constraints are constraints that are SPecified as part of an item declaration while dynamic con-
straints are in-line constraints proyided when an item instance is heing randomized.
188 OVM Transaction Sequences

Sequence Declaration: Defining sequence S1

do Actions:
Sequence Iten1S: --_.JI!!!!iIIIIIIIlll'illlllj1llil1I
what to generate how to generate each sequence item

Sequence S1
jgj]-- '---Action block:
order of generating sequence items

Sequence Instance: Creating sequence S1

S1 'l E!>-9 A- - - - - - - -
'~ElB""""""
c A B A
000 0
Arbiter
.----- - 9 A --------
--------- 9 8 -------- ..
Time.

Figure 8.3 Flat Sequence Declaration and Generation

13
14 virtual task bod yO:
15 xbar_xmt_packet this_seCLitem;
16 'ovm_do_with(this_seqJtem, (payload==default_payload;})
17 repeat (count)
18 'ovm_do(this_seqJtem)
19 endtask
20 endclass: xbar_xmt_seq_flat

Flat sequence xbar_xmt_seq_flat is declared in the above example. This class is derived
from the predefined base class ovm_sequence (line I). Macro ovm_sequence_utils() is used to
register this class with the factory and to add it to the sequence library for sequencer
xbar_xmt_sequencer implemented in program 8.2 (line 6). This sequencer is defined to con-
tain a random field defaulCpayload, and a random field count for controlling the number of
generated items (lines 2, 3). A constraint defining the desired range for this payload is also
included in this description (line 4). As will be shown in program 8.4, fields defaulCpayload
and count can be used to customize this sequence when it is used as a subsequence in a hier-
archical sequence.

The action block for sequence xbar_xmCseq_flat is specified by defining virtual task
body() and using execution macros ovm_do_with() and ovm_do() to execute sequence item
thls_seq_item (lines 14-19). Macro ovm_do_withO is used to execute sequence item
this_seq_ltem with dynamic constraints, constraining field payload to field default_payload of
the sequence (line 16). Macro ovm_do() is used to execute sequence item thls_seq_item with
its static randomization constraints (line 18). Every execution of this sequence will produce
count+1 sequence items. The OYM class library detines a set of execution macros that are
used for executing sequences and sequence items (section 8.6 ..2).
Hierarchical Sequences 189

It should be noted that depending on the execution macro, a new instance of sequence
item this_seq_item may be created and randomized for each execution of this sequence item
(lines 16, 18). Table 8.1 provides a summary of actions perfonned by each execution macro.
As such, the definition of a sequence should not depend on the content of a sequence item
that is created or randomized by an execution macro. Given that both execution macros
ovm_do() and ovm_do_with() create and randomize the sequence item, the implementation
shown above follows this guideline by defining a new field holding the default payload value
and constraining sequence item this_seq_item when it is passed to an execution macro.
The above example highlights the following general guidelines that must be followed
when implementing a flat sequence:
• A new sequence must be derived from ovm_sequence (line 1).
• The declaration of a new sequence must include the declaration of its constructor
(lines 10-12).
• Macro ovm_sequence_utils(), or its begin/end variations must be used to register a new
sequence type with the sequencer whose sequence library should contain this newly
declared sequence (lines 6-8). This macro also registers this new sequence type with
the OVM factory so that override operations can be supported. The begin/end varia-
tion of this macro is used when the newly defined class contains fields that must be
automated. The arguments to this macro are the name of the class in which this macro
is used, and the sequencer class whose sequence library should contain this newly
declared sequence.
• A sequence action block is specified by defining the contents of virtual task bodyO
(lines 14-19).
• In some cases, a new sequence is only defined to be used later as a base class for
other sequences, and hence, should not be placed in the sequence library of its
sequencer. For such sequences, macro ovm_object_utilsO or its begin/end variations
can be used instead of macro ovm_sequence_utiisO. In this case, the new sequence is
registered with the OVM factory without being placed into a sequence library.
The flat sequence created in the above example is automatically added to the sequence
library for sequencer xbar_xmCsequencer and can be used in hierarchical sequences belong-
ing to that sequencer, or started as the root sequence of a new running sequence, or set as the
default sequence for this sequencer. The use of this sequence in a hierarchical sequence is
shown in the next section. Starting this sequence as a root sequence is shown in section 8.6.

8.4 Hierarchical Sequences --_.... _----------_._------- - - - -

A hierarchical sequence is defined in terms of both subsequences and sequence items. Hier-
archical sequences allow previously defined sequences to be reused, and also provide a
means of organizing a long sequence into smaller interelated sequences. Consider a verifica-
tion sequence generating 20-30 SIZED Ethernet packets followed by a PAUSE packet fol-
lowed by 40-50 QTAGGED packets. It may be helpful to model this sequence as a hierarchical
sequence where the generation of SIZED and QTAGGED packets are defined as flat sequences
that are then used in the final hierarchical sequence.
1'90 OVM Transaction Sequences

Figure 8.4 shows an example of a hierarchical sequence. In this example sequence 52


makes use of sequence S1 defined in figure 8.3, and sequence S3 makes use of sequence S2. It
is important to note that the internal organization of a sequence (i.e., hierarchical versus flat)
is not visible to the sequencer, and the sequencer is unaware of the fact that the sequence of
items generated by sequence 53 are generated using a hierarchical sequence.

Sequence Declaration: Defining hierarchical sequences 8 2 and S3

Sequence S3

Sequence Instance: Creating hierarchical sequence 8 3

F
S3 CGn----l> 0 F- - - - - - - - - - - - - - - - - - - - - - - - -
~@-S:2---t>@-E- - - - - - - - - - - - - - - -- E
A
~ @£,---I3>-0 A - - - - - - -- ....
E
- - - - - -~~0B- - -- -- -- B
~
- -- -- - \-.~ 0 A - - - - - - - - A
ts
u
C-
---------- 08-------- ~
<l)
::J
- - - - - - - - - @D - - - - - - - - - - - - - - - - D cr'
<l)
[/J

------------ -- 0£- --- ---.. -.......... -.... E

- -.... - . 9 A- ...... - ........ - .... - - .... - .. - - - - - .. - A

Time

Figure 8.4 Hierarchical Sequence Declaration and Generation

Two types of sequences can be identified during the execution of a hierarchical


sequence:
• Root sequences
• Subsequences
A root sequence is the start point of the execution of a new independent sequence tree.
The difference between a root sequence and a subsequence is that a subsequence has a parent
sequence but a root sequence does not have a parent sequence. Having a parent sequence
impacts the way a running sequence is treated by the sequencer. For example, a grabbed
sequencer accepts items from a subsequence if the sequence that is currently grabbing the
sequencer is an immediate or distant parent of that subsequence (section 8.8.1). The default
sequence of a sequencer is started as a root sequence (section 8.5.1).
A root sequence is started in its own thread of execution and a subsequence executes in
the execution thread of its parent sequence. This behavior is not, however, mandatory. For
Hierarchical Sequences 191

example a sequence 51 can execute a sequence 52 in its execution thread as a root sequence
thereby denying 52 any privileges granted to 51 (e.g., interaction with a sequencer currently
grabbed by 51), and wait for 52 to complete before continuing. Alternatively, a sequence 51
can execute sequences 52 and 53 as parallel subsequences (i.e., by using a fork statement), so
that these subsequences inherit the privileges currently granted to sequence 51'
Section 8.6 describes how root sequences and subsequences are started. The remainder
ofthis section shows the implementation of hierarchical sequences using the predefined exe-
cution macros ofthe DVM class library.
The implementation of a hierarchical sequence using the DVM class library is shown in
the following example:

Iprogram 8.4: Hierarchical sequence implementation


1 class xbar-><mCseq_hier extends ovm_sequence;
2 ·ovm_sequence_utils(xbar_xmCseq_hier. xbar_xmCsequencer)
3
4 function new(string name=..... ovm_component parent=null);
5 super.new(name);
6 endfunction
7
8 virtual task body();
9 xbar_xmCse<Lflat this_flat_seq;
10 xbar_xmt_packet this_seqjtem;
11
12 'ovm_do(this_se<Litem)
13 'ovm_do(this_flaCseq)
14 ·ovm_do_witl1(this_flat_seq. (defaulCpayload == 4 && count == 4;})
15 endtask
16 endclass: xbar_xmCseq_hier

This example shows how flat sequence xbar_xmCseq_flat (defined in program 8.3) is
used in the declaration of hierarchical sequence xbar_xmt_seq_hier. This new sequence is
derived from the predefined base class ovm_sequence (line I), and is added to the sequence
library for sequencer xbar_xmCsequencer by using macro ovm_sequence_utils() (line 2). The
action block for this sequence is specified by defining virtual task body() and using macros
ovm_do() and ovm_do_withO to start the execution of sequence item thls_seq_item (line 12) fol-
lowed by execution of subsequence this_flat_seq once without any constraints (line 13) and
once with constraints applied to randomized fields of sequence this_flat_seq (line 14). The
use of macro ovm_do_withO with a sequence argument (line 14) shows how each execution of
a subsequence in a hierarchical sequence can be customized to its execution context.
The guidelines for creating a hierarchical sequence are similar to those for a flat
sequence, except that a hierarchical sequence contains instances of previously defined
sequences (line 9) and can optionally execute sequence items. A hierarchical sequence may
contain instances of both flat and other hierarchical sequences. It should be noted that all
sequence instances used in a hierarchical sequence must belong to the same sequence library,
and hence the same sequencer.
1'2 OVM Transaction Sequences

Every sequence that is to be executed by a sequencer should be placed in the sequence


library of its sequencer by using macro ovm_sequence_utilO or its being/end variations.
Sequence declarations that provide a base class for deriving other sequences, and hence are
never executed, need not be placed in any sequence library. Every sequence in a sequence
library is identified by its type name and kind.
The OVM class library provides the following fields and methods for locating and cre-
ating a sequence from the sequence library.
Methods and fields for class ovm_sequencer_base:
string sequences[$];
function in! unsigned get_seq_kind(string seq_type_name);
function ovm_sequence get_sequence(int unsigned seq_kind);
Methods and fields for class ovm_sequence:
function int unsigned get_seq_kind(string seq_type_name);
function ovm_sequence ge,-sequence(in! unsigned seq_kind);

Array sequences in class ovm_sequencer holds all sequences in the sequence library of
this sequencer. The total number of sequences in the sequence library of a sequencer is given
by the size of this field. Function geCseq_kind() returns the kind for sequence type name
seq_type~name. Function geCsequence() creates a new instance of a sequence of kind
se~klnd. The resulting sequence instance is not randomized. These functions are defined for
both a sequencer and a sequence. Calling these functions from inside a sequence has the
effect of calling the same function in the sequencer that contains that sequence.
The fields and methods described above allow access to any sequence from inside a
sequencer or a sequence. The following sections show how a sequence created through this
interface can be started.

8.5.1 Predefined and User Defined Sequences


The OVM class library provides the following predefined sequences for every sequence
library:
• Sequence ovm_random_sequence
• Sequence ovm_exhaustive_sequence
• Sequence ovm_slmple_sequence
In addition, the following fields defined for a sequencer class are used to control the
execution of these predefined sequences:
Fields defined fro class ovm_sequencer_base:
=
protected string default_sequence "ovm_random_sequence";
int count = OVM_UNDEF;
in! unsigned max_random_count = 10;

Sequence ovm_simple_sequence executes the default sequence item of its containing


sequencer once. This means that running this sequence once produces a single sequence item
sent to the sequencer.
Sequence ovm_exhaustlve_sequence executes e\'ery user defined sequence and sequence
ovm_slmple_sequence once and in a random order.
Executing Sequences and Sequence Items 193

The behavior of sequence ovm_random_sequence depends on fields count and


max_random_count. If count is not negative, then this sequence executes count number of
sequences selected randomly from ovm_simple_sequence and user defined sequences regis-
tered with the sequence library. If count is less than zero, then the sequencer generates a ran-
dom value between 1 and max_random_count, and executes that many riumber of sequences
selected randomly from ovm_simple_sequence and user defined sequences registered with the
sequence library.
Field defaulCsequence of a sequencer determines which sequence from its sequence
library is automatically started when the run phase of simulation is entered. This field can be
changed in order to allow a user defined sequence to be started instead of sequence
ovm_random_sequence.

The fields shown above can be changed through field configuration constructs (section
7.2) to customize a sequencer to behave according to the requirements of its environment.

Sequences can be executed using either methods calls or through predefined macros.
Sequence items are executed by calling predefined macros. Executing root sequences and
subsequences using methods is described in section 8.6.1. Executing subsequences and
sequence items using the predefined macros of the OYM class library is described in section
8.6.2.

8.6.1 Executing Root Sequences and Subsequences using Methods


The OYM class library provides the following methods for executing sequences:
Method for class ovm_sequencer_base:
task start_sequence(ovm_sequence this_seq, ovm_sequencer_base this_sqnsr=null);
Methods for class ovm_sequence:
task start(ovm_sequencer_base parent_sqnsr, ovm_sequence parent_seq = null);
task do_sequence_kind(int unsigned seq_kind);

Task start_sequenceO of class ovm_sequencer_base is used to start a new root sequence


in sequencer this_sqnsr. If argument this_sqnsr is not provided, then the sequence is started
on the calling sequencer. Argument this_seq passed to this task must be a sequence that is
already created, and if necessary, randomized.
Task startO of class ovm_sequence is used to start a new sequence on sequencer
parent_sqnsr. If argument parent_seq is set to null, then this new sequence is started as a root
sequence. If argument parent_seq is provided, then the new sequence is executed as a subse-
quence of sequence parent_seq.
Task do_sequence_kindO of class ovm_sequence is used to start a new subsequence of
kind seq_kind as a subsequence of the calling sequence. This task always results in a subse-
quence. Task do_sequence_kind() is useful for executing sequences through distribution or
exclusion constraints, where each sequence kind is selected randomly and then executed
194 OVM Transaction Sequences

using this task. Other than this special use model, the use of execution macros (section 8.6.2)
is recommended over this task.
The following example program highlights two aspects of sequencer implementation:
Overriding the default behavior of a sequencer, and starting root sequences and subse-
quences. This implementation uses sequence library methods (section 8.5) and sequence start
methods shown above.

iprogram 8.5: Overriding sequencer behavior and starting a root sequence


1 module test;
2 'include "ovm.svh"
3 'include "program-B.2"
4 'Include "program-8.3"
5 'include "program-B,4"
6
7 class xbar_xmt_maln extends ovm_sequence;
8 'ovm_sequence_utils(xbar_xmt_main, xbar_xmt_sequencer)
9
10 function new(string name="", ovm_component parent=null);
11 super.new(name);
12 endfunction
13
14 virtual task bod yO;
15 int unsigned key;
16 xbar_xmt_seq_flat fseq;
17 xbar_xmt_seQ..hier hseq;
18
19 fork
20 begin
21 key = get_seq_kind("xbar_xmCseq_flat");
22 $cast(fseq, get_sequence(key));
23 assert(fseq.randomizeO with {count == 5;});
24 p_sequencer.start_sequence(fseq);
25 end
26 begin
27 key = get_seq_kind("xbar_xmt_seq_hier");
28 $cast(hseq, get_sequence(key));
29 hseq.start(p_sequencer, null); IIstart as a root sequence
30 end
31 join_none
32
33 do_sequence_kind(get_seq_kind("xbar_xmt_seq_hier"));
34 endtask
35 endclass: xbar_xmt_main
36
37 initial begin
38 xbar_xmt_sequencer sqnsr;
39 set_config_string("my-sqnsr", "default_sequence", "xbar_xmt_main");
40 $cast(sqnsr,ovm_factory::create_component(
41 "xbar_xmCsequencer", "", "my_sqnsr", null));
42 sqnsr.buildO;
43 end
44 endmodule

The first step in modifying the default behavior of a sequencer is to define a new
sequence that will replace the default sequence started by the sequencer and registering it
with the sequencer, Sequence xbar_xmt_main is defined in the abo\'e example (lines 7-35),
Configuration method set_config_strlngO is used to change the value of the field
defaulCsequence in sequencer sqnsr to the name of this newly defined sequence (line 39).
The OVM factory is then used to create an instance of the sequencer (line 40), and the buildO
Executing Sequences and Sequence Items 195

method of this new instance is used to create its content (line 42). Sequencer sqnsr will now
start sequence xbar_xmCmain when it enters the run phase of simulation.

The action block for sequencer xbar_xmt_maln (lines 14-34) shows the start of two new
root sequences (lines 19-31) and executing of a subsequence (line 33). An instance of
sequence xbar_xmt_seq_flat is started as a root sequence by first getting the key index for the
sequence type (line 21), creating a new instance of this key (line 22), randomizing the con-
tents of this sequence according to the local requirements (line 23), and then starting this
sequence instance by calling function start_sequence() of its parent sequencer accessed
through predefined field p_sequencer (line 26). The alternative approach of using task startO
of the sequence is used to start an instance of sequence xbar_xmt_seq_hler (lines 27-29) with-
out the randomization step, since this sequence does not contain any field requiring random-
ization. Task do_sequence_klnd() of class ovm_sequence is then used to start a subsequence
(line 33).
The use of macros to execute subsequences and sequence items is described in the next
section.

8.6.2 Executing Subsequences and Sequence Items using Macros


The execution of a sequence item or a subsequence in the action block of a sequence results
in the execution of a number of steps. These execution steps are described in this section.
The execution flow of a sequence item (e.g., this_seq_ltem on lines 12 in program 8.4)
consists of the following steps:
• Create and initialize the sequence item and let the sequencer know that it is available
• Sync: wait for sequencer to indicate that it is ready to accept an item
• Execute pre_do() method of the calling sequence
• Randomize the sequence item
• Call mid_doO method of the calling sequence
• Post-Sync: let sequencer know that randomization is complete and item can be sent,
and wait until sequencer indicates that the item has been consumed
• Call method post_do() of the calling sequence
In the first step, the sequence item is created and initialized with pointers to its
sequencer and parent sequence. Next, the execution waits for the sequencer to announce that
it is ready to accept a new item. The sequencer selects this sequence only if it is relevant to
the current verification context (see sequence selection in section 8.8.2). The sequence
informs the sequencer that it has an item available and then task pre_do() of the calling
sequence is called. In the next step, the contents of the sequence item are randomized accord-
ing to the static (embedded) and dynamic (in-line) randomization constraints of the sequence
item. Additional constraints that may have been provided during the activation of this
sequence item (e.g., by using macro ovm_do_withO) are also used as dynamic constraints dur-
ing this randomization. In the next step, task mid_do() of the calling sequence is called, allow-
ing user to modify or observe the randomized contents of the sequence item. In the next step,
the sequence informs the sequencer that the item is randomized, and ready to be sent. and
waits for the sequencer to indicate that the item was consumed. In the last step. task posCdoO
is called.
196 OVM Transaction Sequences

The execution flow ofa subsequence (e.g., thls_flaCseq on lines 13 in program 8.4) con-
sists of the following steps:
• Create and initialize the subsequence
• Execute pre_do() method of the calling sequence
• Randomize the subsequence
• Call mid_do() method of the calling sequence
• Call method body() of the subsequence
• Call method post_do() ofthe calling sequence
Execution steps for a subsequence are similar to those for a sequence item. The main
difference is that executing a subsequence does not involve any synchronization with the
driver. Also a subsequence has an action block defined by the contents of task body() which is
called after calling task mld_do().

&:
.-==
"
.!:i ~
it Ii .
e= 0
"1:1

Macros .f. .
:l
&:
~
i"CI
e "CI=
l
.t:I
Ii
~&:
.. 0
= ! .. =
"CI
&:

:51 ., it)= -;;


1
.lOI "1:1 1

1:1. &:
f
i!&: =
Ii 1:1. =
1:1.
&:
.~
.,
=
-;:

.=
&: <

Table 8.1: Item and Subsequence Execution Macros

Table 8.1 lists the predefined macros provided by the OVM class library for executing
sequence items and Subsequences. Each of the macros listed in table 8.1 cover different steps
of execution, therefore allowing these macros to be combined to achieve a number of differ-
ent randomization behaviors. Some possible scenarios include:
• Execute an item with late randomization (section 8.2.1) and with item's default con-
straints:
• Use macro ovm_do() to execute the item
• Execute an item with early randomization (section 8.2.1) and with item's default con-
Executing Sequences and Sequence Items 197

straints:
• Use macro ovm_create() to create the item. Then explicitly randomize the item.
Then use ovm_sendO to send the item. This macro does not do any further ran-
domization and, therefore, the item is sent with the early randomization that is
explicitly performed.
• Execute an item with late randomization and with constraints in addition to the item's
default constraints:
• Use macro ovm_do_withO to execute the item
• Execute an item with late randomization and with modification of its default random-
ization constraints:
• Use ovm_createO to create the item. Explicitly tum off rand statements or con-
straint blocks in the item using randomization methods. Use ovm_rand_sendO or
ovm_rand_send_with() to send the item.

The OVM class library defines a set of callback methods for a sequence object. These
methods can be used to customize the behavior of sequence generation steps. For example,
task mid_do() can be redefined to over-write the result of randomization for some fields of a
subsequence before the action block for that subsequence is executed. Table 8.2 summarizes
the list of callback methods for a sequence object.

Method
Return Name Arguments Notes
Type
task pre_body 0 Called only for root sequences
Called for both root sequences
task body 0 and subsequences
task post_body () Called only for root sequences
Called when executing both
task pre_do (bit is_item)
items and subsequences
Called when executing both
function void mid_do (ovm_sequencejtem this_item)
items and subsequences
Called when executing both
function void post_do (ovm_sequencejtem this_item)
items and subsequences

Table 8.2: Sequence Callback Methods

It is important to note that if a sequence executes multiple sequence items and subse-
quences, then the same hook methods are called when executing any of these items or
sequences (e.g., the same mid_do!) method is called for every execution of sequence item
and/or subsequence). As such, use of hook methods in sequences that execute multiple sub-
sequences and/or sequence items requires special facilities for making clear which sequence
item or subsequence is being executed when that hook method is called.
Figure 8.5 shows an example of the order in which these callback methods are called
during the execution of a root sequence, subsequences, and sequence items. Note that the
flow in this figure shows only the order of calling these callback methods and does not show
the full details of the steps that are carried out during the execution of sequence items and
sequences. Method names shown in this example directly correspond to steps shown in table
8.1.
198 OVM Transaction Sequences

Start root sequence rs executing:


-item iA
-subsequence ssA executing item iB in rs.bodyO
-subsequence ssB executing item iC J lIexec iA
-rs.pre_do(1 )
-rs.mid_do(iA)
-rs.post_do(iA)

j
in start sequence()
rs.pre_bodyO
I/exec ssA
-rs.pre_do(O)
-rs.mid_do(ssA)
J in ssA.body()
I/exec is
-ssA.pre_do(1)
'-----< rs.bodYO-k
t -ssA.bodyO --+-<
t -ssA.mid_do(iB)

h
rs.post_bodyO -rs.posCdo(ssA)
-ssA.post_do(iB)
1_

I/exec ssB in ssB.body()


-rs.pr~_do(O) lIexec iC
-rs.mld_do(ssB) -ssB.pre_do(1)
-ssB.bodyO- , -ssB.mid_do(iC)
\. -rs.posCdo(ssB) -ssB.post_do(iC)

Figure 8.5 Callback Method Calling Sequence

8. 7 Sequence Item Interfaces


The OYM class library provides a sequence item interface for connecting a sequencer with a
driver. A sequence item interface is a specialized transaction interface that supports methods
customized to the requirements of sequence item exchange (figure 8.6).

Sequencer (target component) Driver (initiator component)

0000
~---------+-. ovm_seqJtem_prodJf
Sequence Item Interface

Figure 8.6 Sequence Item Interface Architecture

In using a sequence item interface, it is assumed that the driver is an initiator component
that is a transaction consumer, and the sequencer is a target component that is a transaction
producer. The sequence item interface consists of two connector object kinds:
• Imp object kind ovm_seq_item_cons_if
• Port object kind ovm_seq_item_prod_if
Connector object kind ovm_seq_item_cons_if is instantiated inside the producer compo-
nent (i.e., ovm_sequencer) and connector object kind ovm_seq_item_prod_if is instantiated
inside the consumer component (i.e., ovm_driver). Connector object ovm_seq_item_consJf
Sequencer Arbitration Mechanism 199

provides the implementation for the predefined methods supported by this interface. Once
these connector objects are connected, these supported methods can be called from inside the
driver to exchange sequence items with the sequencer. Table 8.3 lists the methods supported
by a sequence item interface.

Type Returns Name Arguments Description


Returns the next available sequence
task get_next_item (output ovm_sequence_item item) item from the sequencer. Blocks if
no item is immediately available.
Returns immediately the next avail-
able sequence item from the
task trLnexUtem (output ovm_sequence_item item)
sequencer. Returns null ifno item is
available.
Is called by the driver when a
received sequence item is pro-
cessed. Can optionally return a
function void item_done (ovm_sequence_item item = null)
sequence item in reply to the
sequence item that was received
from the sequencer.
Returns I if a sequence item is
function bit has_do_available 0 available immediately from the
sequencer.
Connects this connector object with
function void connect_if (ovm_se<Litem_cons_if sqnsr_if) a connector object of kind
ovm_se<Litem_cons_if.

Table 8.3: Methods Supported by Connector Object ovrn_seq_itern_prod_if

The implementation of a sequencer, as provided by the OVM class library, contains a


connector object of kind ovrn_seq_itern_cons_if. Therefore, connecting a driver to a sequencer
requires that a connector object of type ovrn_seq_item_prod_if be instantiated inside a driver
and then connected with the connector object inside the sequencer when the environment
hierarchy is being constructed. The implementation of the Xbar verification environment
shows a complete example of instantiating and connecting a sequence item interface (section
13.6.1.4).

8.8 Sequencer Arbitration Mechanism


The arbiter component inside a sequencer interacts with multiple streams of sequence items
and selects one sequence item at a time to be passed to the driver (figure 8.2). This arbiter
then provides the next available sequence item to the driver through the sequence item inter-
face described in section 8.7.
The operation of the arbiter is described in terms of:
• Interaction with a single sequence producing a stream of sequence items
• Interaction with multiple sequences, deciding which sequence should provide the
next sequence item
These topics are described in the following subsections.
200 OVM Transaction Sequences

8.8.1 Sequence Interaction


The interaction between a sequencer arbiter and a single sequence is shown in figure 8.7. A
sequence producing a stream of sequence items, and a sequencer arbiter each have their own
execution threads. Initially, the arbiter loop waits for the driver to request a new sequence
item by calling task geCnext_ltemO of the sequence item interface. At this point, the arbiter
waits for a new sequence item to become available in its queue. It then emits event dO.-gen of
the sequence that placed the sequence item on its queue. Upon detecting event do_gen, the
sequence thread randomizes that sequence item and calls task mid_dol) and emits event
gen_done. Upon detecting event gen_done, the arbiter passes the sequence item to the driver
and waits for the driver to emit event item_done which is triggered when the driver calls
method Item_done(). Upon detecting event Item_done, the sequence thread continues with the
remainder of its execution steps, and the arbiter thread returns to the beginning of its loop.
The description above assumes that when the driver requests a new sequence item, no
item is available in the sequencer queue. This assumption, however, is not necessary since it
is possible for a sequence to place a sequence item in the sequencer queue before the driver
makes a request for a sequence item. In this case, when the driver request the next sequence
item, the arbiter realizes that a sequence item is already present in the queue and immediately
proceeds by emitting event do_gen and the steps that follow.
It should be noted that figure 8.7 shows only a logical view of the interaction between a
sequencer and a sequence thread, and does not reflect the actual implementation of this inter-
action. The actual interaction may also vary from this description. For example, depending
on the execution macro that is being used, no randomization may take place before calling
task mld_doO.
Figure 8.7 shows the sequencer interaction with a single sequence thread. This interac-
tion can, however, support mUltiple sequence threads feeding items to the sequencer arbiter.
The reason is that each sequence item passed to the arbiter has a pointer to the sequence that
produced it, and as such, the arbiter can target this interaction to the sequence that produced
the sequence item being handled at the time of this interaction.

8.8.2 Sequence Selection


The sequencer arbiter uses a first-in-first-out (FIFO) mechanism for deciding which
sequence that is relevant to the current verification context should provide the next sequence
item. By default, all sequences are assumed to be relevant to the current verification context.
Two mechanisms are provided by the OVM class library for allowing better control
over arbiter sequence selection:
• Grabbing
• Relevance

Grabbing refers to the ability of a sequence to grab a sequencer, thereby gaining exclu-
siye access to the arbitration mechanism. A grabbed sequencer selects sequence items from
only the sequence that has currently grabbed the sequencer or any of its subsequences. Since
the sequencer does not select sequence items from any other sequence, all other sequences
Sequencer Arbitration Mechanism 201

Execution Flow

Sequencer Sequence

Arbiter Loop
-wait for driver to request an item
-if no item available
wait for event new_item_added
c= Execute-item

:~
-place new item in queue
-emit event sqnsr.new_item_added
-pre_doO
-wait for event do_gen

-get item at head of queue


-emit event item.seq.do_gen
-wait for event item.seq.gen_done c:
. -Randomize item in queue
-mid_doO
-emit event gen_done
-wait for event sqnsr.item_done
-return item to the driver
-wait for driver to emit item_done [-
-
-driver emits item_done
-back to loop L-
:b return

~ ,.
Figure 8.7 Sequence and Sequencer Interaction

appear to block until the sequencer is ungrabbed. The OYM class library provides the fol-
lowing methods for allowing sequences to grab the sequencer:
Methods defined fro class ovm_sequencer:
task grab(ovm_sequence_seq seq);
function void ungrab(ovm_sequence_seq seq);
function ovm_sequence current_grabber();
function bit is_grabbedO;
Methods defined fro class ovm_sequence:
function bit is_blocked();

Task grabO is used to grab a sequencer. If the sequencer is already grabbed, then this
task blocks until the sequence that has already grabbed this sequencer calls function ungrabO.
Function currenCgrabberO returns a pointer to the sequence that is currently grabbing the
sequencer. Function is_grabbedO returns 1 if the sequencer is currently grabbed. A call to
grabO in a subsequence of a sequence that has already grabbed the sequencer does not block
202 OVM Transaction Sequences

(i.e., succeeds immediately). Method is_blockedO of a sequence returns 1 if that sequence is


currently blocked on a call to grabO.
Relevance calculation allows the sequencer arbiter to decide if items produced by a
sequence are relevant to the current verification context. For example, a sequence that should
only generate items during the reset phase, is not relevant to verification when the reset
phase is completed. The OVM class library provides the following methods for allowing
sequences to identify their relevance to the sequencer:
Methods defined fro class ovm_sequence:
virtual function bit is_relevantO;
virtual task wait_forJelevantO;

Method Is_relevantO is a virtual method of a sequence that returns a value of 1 by


default. The user can modify the definition of this task to return a 1 only if a sequence item
generated by this sequence is relevant to the current environment conditions. The relevance
of a sequence to the current conditions may change at any time during the simulation run-
time. As such, a special mechanism is needed to notify the arbiter, which may be idle due to
no additional items being generated after its last cycle, that items produced by this sequence
Should now be processed. Task walUorJelevantO is a virtual task that should block and com-
plete only when sequence items produced by this sequence become relevant to the current
environment conditions. The completion of task walt_fouelevant() forces the sequencer arbi-
ter to reprocess its sequence item queue. Task walUouelevantO should be redefined if task
Is_relevant() is redefined.

Examples of grabbing sequences and relevance calculation are given in section 14.10.

8.9
.....
Virtual
~"- ...
Sequencers
- -....... ... --".-.-
"".", ..' - ' " .... .

The OVM class library provides the virtual sequencer construct for creating multi-sided ver-
ification scenarios. Figure 8.8 shows a graphical view of a virtual sequencer and its interac-
tion with downstream sequencers. A virtual sequencer uses seguence interfaces to interact
with downstream sequencers. A sequence interface allows a virtual sequence to execute sub-
sequences belonging to the sequence library of downstream sequencers. A sequence inter-
face also allows a virtual sequence to interact with (e.g., grab) downstream sequencers.
Virtual sequences can only execute subsequences. As such, a virtual sequencer does not have
a default sequence item type. A subsequence executed by a virtual sequence may belong to
the sequence library of the local virtual sequencer or any downstream sequencer connected
to the local sequencer through a sequence interface.
The implementation of a virtual sequencer is shown in the following program:
I
Program 8.6: Virtual sequencer implementation
1 class xbar_xmt_virtual_sequencer extends ovm_virtual_sequencer;
2 int unsigned port_num;
3 'ovm_sequencer_utils_begin(xbar_xmCsequencer)
4 'ovm_fieldjnt(port_num,OVM_ALL_ON)
5 'ovm_sequencer_utils_end
6
7 function new (string name='''', ovm_component parent=null);
Sequence Interfaces 203

8 super.new(name, parent):
9 'ovm_update_sequence_lib
10 endfunction: new
11 endclass: xbarjmt_virtual_sequencer

The core implementation of a virtual sequencer is similar to that of a regular sequencer


except that a virtual sequencer is derived from base class ovm_virtual_sequencer (line 1), and
also, predefined macro ovm_update_sequence_lib should be used to initialize the sequence
library of a virtual sequencer (line 9).

Sequencer

downstream sequencers
can be either a real or a
virtual sequencer

Virtual sequences use the


sequence interface to execute
a sequence in a downstream
sequencer or interact with (e.g., grab)
a downstream sequencer

Virtual Sequences can


only execute subsequences
on local sequencer or on
downstream sequencers

Figure 8.8 Virtual Sequencers and Multi-Sided Sequence Generation Architecture

The use of virtual sequencers for generating multi-sided scenarios is shown using an
example in section 14.7.

8.10 Sequence Interfaces


---------------------------

The OVM class library provides a sequence interface construct for connecting a virtual
sequencer with a downstream sequencer. A sequence interface is a specialized interface that
supports methods customized to the requirements of sequence exchange (figure 8.9).
In using a sequence interface, it is assumed that the virtual sequencer is the initiator
component, and the downstream sequencer is a target component. The interface consists of
two connector object kinds:
• Port object kind ovm_se<Lcons_if
• Imp object kind ovm_seq_prod_if
204 OVM Transaction Sequences

Virtual Sequencer (initiator component) Downstream sequencer (target component)

ovm seq cons


- - - if~ .±l
~
ovm_se<Lprodj

Sequence Interface
~----------------------------

Figure 8.9 Sequence Item Interface Architecture

Port connector object kind ovm_seq_cons_if is instantiated inside the producer compo-
nent (i.e., ovm_virtual_sequencer) and imp connector object kind ovm_seq_prod_if is instanti-
ated inside the consumer component (i.e., downstream ovm_sequencer or
ovm_virtual_sequencer). Once these connector objects are connected, these supported meth-
ods can be called from inside the virtual sequencer to execute it on the downstream
sequencer. Table 8.3 lists the methods supported by a sequence interface.

Type Returns Name Arguments Description


task start sequence (ovm sequence this_seq) pass toaownstream sqnsr
task grab (ovm sequence this_seq) pass to downstream sqnsr
I function void ungrab (ovm _sequence this_seq) pass to downstream sqnsr
function ovm sequence current grabber 0 pass to downstream sqnsr
function bit is grabbed 0 pass to downstream sqnsr
function bit is connected () returns 1 if connected

function returns I if downstream


bit is_virtual_sequencer ()
sequencer is virtual I
name of downstream
I function string get_sequencer_type_name 0 sequencer type I

connect this connector


function void connecUf I (ovm _ seq.yrod_ if sqnsUf) object with the one in the
downstream sequencer
i
Table 8.4: Methods Supported by Connector Object ovm_seq_cons_if

The implementation of a virtual sequencer, as provided by the OYM class library, con-
tains an associative array of connector objects of kind ovm_seq_cons_if. The following field
and method are provided for class ovm_virtual_sequencer:
Method and field defined fro class ovm virtual sequencer:
ovm_seq_cons_if seq_cons_if[stiingl: -
virtual function void add_seq_consjf{string iCname):

Method add_seq_cons_ifO is used to add a new connector object to a virtual sequencer.


Once a connector object is added to the virtual sequencer, its name can be used in virtual
sequence execution macros to execute a given sequence on the downstream sequencer to
which this named connector object is connected. This newly added connector object can
directly be accessed through field seq_cons_if which is an associative array. A complete
implementation of this approach is shown in section 14.7.
The execution macros available for use in the action block of a sequence defined in a
yirtual sequencer are shown in table 8.5.
Sequence Interfaces 205

Execution Steps
c
~. ~

~
0 'il!"c "ec
.~
Macros .t
~
0
c
"1t E
c
~ .:.c ~
"cf
;1 ...c "...1
... c:o. "f ].9 E isc I
~., .£
c <
...c
ovm_do_seq(subseq, se'Lconsjf) IlQ IlQ IlQ IlQ IlQ Ii:]

ovm_do_se'Lwith(subseq, se'Lconsjf) ~ ~ ~ 0 ~ ~
ovm_create_seq(subseq, seCLcons_if) ~
ovm_send( subseq) ~ ~ ~ ~
ovm_rand_send(subseq) ~ ~ ~ ~ ~
ovm_rand_send_with(subseq, {constraints}) ~ 0 ~ 0 0
Table 8.5: Subsequence Execution Macros for Virtual Sequencers

Macros ovm_do_seq() and ovm_do_seq_withO can be used to directly execute a subse-


quence on the downstream sequencer that seq_cons_if connector object is connected with.
Alternatively, macro ovm_create_seq() can be used to create a new subsequence object, and
then macros ovm_sendO, ovm_rand_sendO, and ovmJand_send_wlthO can be used without
referring to the actual connector object to execute this subsequence on the downstream
sequencer connected with seq_cons_if object used when creating this subsequence instance.
A new root sequence can be created on a downstream sequencer by calling task
start_sequenceO of a sequence interface.

Full examples of creating, connecting, and driving sequences through virtual sequenc-
ers are given in section 14.7.
206 OVM Transaction Sequences
CHAPTER 9 oVM Transaction
Interfaces

In a modular verification environment, communication between verification environment


components are carried out through exchange of transactions. Transactions are containers of
information that one component must send to another. This information may carry data, sta-
tus, or commands. In essence, a transaction can model any content once the participating
components agree on how that content will be handled. Transactions operate at an abstrac-
tion level which can vary from physical wires all the way to abstractions that exist at the
product level description of the design. This means that transaction interfaces can be used at
any level of abstraction.
Transaction-based communication involves two components that interact over a trans-
action interface. A transaction interface is created by binding two transaction connector
objects in each of the two components. A transaction connector object serves the same pur-
pose as port objects in connecting SystemVerilog module blocks. During a transaction-based
interaction, transactions are moved from a transaction producer to a transaction consumer. A
transaction producer creates a transaction while a transaction consumer uses that transaction
to perform an activity. Transactions can optionally move in both directions on a transaction
interface. This bidirectional transfer of transactions is useful in cases where one component
sends a request transaction to another component and expects to receive a response transac-
tion in return. In this case, both components act as a producer and a consumer of transac-
tions, where the master, the initiator of the transfer, produces a transaction request and
consumes the reply transaction, and the slave consumes the request transaction and produces
a reply transaction. Figure 9.1. shows the architectural view of transaction-based connectiv-
ity and the movement of transactions from producers to consumers.
A transaction-based communication model has a data flow and a control flow property.
Transaction data flow is always from the producer of a transaction to the consumer of that
transaction. A transaction transfer request marks the beginning of an exchange between a
producer and a consumer. A transaction transfer request is made by the transaction initiator.
Either the producer or the consumer can be the transaction initiator (i.e., a transaction initia-
tor sends a transaction if it is a producer, and requests a transaction if it is a consumer). A
transaction target is the recipient of, and responds to, a transaction transfer request (a trans-
action target accepts a transaction if it is a consumer, and returns a transaction if it is a pro-
ducer). Transaction control flow is always from the transaction initiator to the transaction
208 OVM Transaction Interfaces

Transaction Initiator Transaction Target

Producer [} ~ Consumer

D
LJ
Initiator connector object

Target connector object

---. Transaction movement


Consumer ~ € Producer

P\W:tqdU~~r;'iJ
!:CQh.sum"Sj;!
._ .. ,..;,
.;~: ~J~v,~,'~·::l ~

Figure 9.1 Transaction-Based Communication Model

target. Transaction control and data flows do not have to be in the same direction. The trans-
action-based communication model shown in figure 9.1 allows either the producer or the
consumer to be the transaction initiator. In this case, if the producer is the initiator, then the
consumer is the target, and the producer puts a transaction into the consumer. If the consumer
is the initiator, then the producer is the target, and the consumer requests that the producer
give it a new transaction.
Concepts of push mode and pull mode (section 8.1) are related to data and control flow
directions. In a push mode interaction, the transaction initiator is also the transaction pro-
ducer. In a pull mode interaction, the transaction initiator is the transaction consumer.
Transaction data flow can be bidirectional. In this type of connection, the transaction
initiator can be both the producer and consumer of transactions. For example, a transaction
initiator may produce a transaction, pass this transaction to the target for consumption, and
then expect the target to reply back with a reply transaction. In a unidirectional transaction
interface, transactions move in only one direction. In a bidirectional transaction interface,
transactions move in both directions over the same transaction interface.
Transaction movement in a class-based environment is achieved through transaction
connector objects and method calls. In this approach, the initiator component contains a con-
nector object that provides it with a set of predefined methods. While developing the initiator
component, these methods are used as needed, with the understanding that the actual imple-
mentation of these methods will be provided by the target component that the initiator will
eventually get connected to. At the same time, a target component contains a connector
object that must provide the implementation of methods that are assumed to be available by
any initiator component connecting to this target component. Transaction connector object
killii. refers to the different kinds of connector objects that are used in the initiator and target
components, and for routing transaction interfaces through layers of component hierarchy
(section 9.1). Transaction connector object type is defined by the set of methods that is sup-
ported by a connector object.

Tr~ns~ction connector objects are linked when the component hierarchy is created.
Once thiS hnk is in place. calling a preddined method of the connector object in the initiator
component. has the effect of calling the implementation of that method in the target compo-
nent. A major advantage of a communication infrastructure implemented using this approach
Transaction Connector Objects 209

is that both the initiator and target components can be implemented independently, and with-
out requiring any special knowledge about the internal implementation of the other compo_
nent.
A common use of a transaction-based connection is where multiple consumer compo_
nents initiate requests to a common producer component asking for a transaction. This means
that transaction interfaces must support connections between multiple transaction consumers
that are also the initiator with a single transaction producer which is also the transaction tar-
get. Another common use of a transaction-based connection is where a transaction producer
broadcasts its next transaction to multiple target components that each receive a copy of the
broadcasted transaction. A classic example of this use model is where multiple verification
components (e.g., scoreboard, coverage collector, etc.) receive packets from a monitor com-
ponent. An analysis interrace defines a special type of transaction interface where an initia-
tor component that is the producer of transactions can broadcast its transactions to multiple
subscribing target components. Transaction-based connectivity does not support the case
where a single initiator component that is a transaction consumer is connected to multiple
target components that are transaction producers. The reason is that transaction-based con-
nectivity does not support any arbitration mechanism for determining which producer should
supply the transaction requested by the consumer.
The GVM class library provides the following transaction interface types:
• Unidirectional transaction interfaces
• Bidirectional transaction interfaces
• Analysis interfaces
• Transaction channels
The implementation of unidirectional and bidirectional interfaces are described in sec-
tions 9.3 and 9.4, respectively. Analysis interfaces provide a specialized transaction-based
mechanism for allowing multiple target components that are transaction consumers to sub-
scribe to a single initiator component that is a transaction producer. Analysis interfaces are
described in section 9.5. Transaction channels provide specialized class objects that contain
the implementation of the predefined methods required by transaction connector objects,
thereby simplifying both the modeling and the implementation of transaction interfaces.
Transaction channels are described in section 9.7.

9.1 Transaction Connector


~- -
Objects
Figure 9.2 shows an example of a hierarchical verification environment. In this example,
components compo, comp1, comp2, and comP4 are transaction initiators, while components
compa and comps are transaction targets. While building the environment hierarchy, connec-
tor objects are needed for the following purposes:
• To instantiate in transaction initiators (e.g., Po, Ph P2, Ps)
• To instantiate in transaction targets (e.g., i o, i1)
• To route connections to higher layers of the hierarchy (e.g., Pa, P4)
• To route connections to other layers ofthe hierarchy (e.g., eo)
210 OVM Transaction Interfaces

The OVM class library defines three connector object kinds: imp objects, port objects,
and export objects. Imp objects are used inside target components to provide the implementa-
tion of the predefined methods that are supported by a transaction interface (e.g., i o, i1 in fig-
ure 9.2). Port objects are used inside initiator components to provide access to the predefined
methods of the transaction interface (e.g., Po, Ph P2, Ps in figure 9.2). Port objects are also
used to route transaction interfaces to the higher layers of the hierarchy (e.g., P3, P4 in figure
9.2). EXQort objects are used to route transaction interfaces to the other layers of the hierar-
chy (e.g., eo in figure 9.2).

OVM Connector Object Kinds

D Port Object

D Export Object
LJ Imp Object

Figure 9.2 OVM Transaction Connector Objects and Hierarchical Connections

The OVM class library defines the following unidirectional connector objects:
ovm_UNIOIR_TYPE_imp #(trans_type, imp_parenUype)
ovm_UNIOIR_TYPE_port #(trans_type)
ovm_UNIOIR_TYPE_export #(trans_type)

In the above description, UNIDlR_TYPE refers to one of the unidirectional interface types
supported by the library (section 9.3). Parameter trans_type identifies the class type of the
transaction moving across the interface, and imp_parent_type identifies the class type of the
target component in which the imp connector object is instantiated. Examples of using these
connector object kinds are given in section 9.3.
The OVM class library defines the following bidirectional connector objects:
ovm_BIOIR_TYPE_imp #(req_trans_type. rsp_trans_type, parent_class_type)
ovm_BIDIR_TYPE_port #(req_trans_type, rsp_trans_type)
ovm_BIOIR_TYPE_export #(req_trans_type, rsp_trans_type)

In the above description, BIDIR_TYPE refers to one of the bidirectional interface types
supported by the library (section 9.4). Parameter req_trans_type identifies the class type ofthe
transaction object moving from the initiator component to the target component. Parameter
rsp_trans_type identifies the class type of the transaction object moving from the target com-
ponent to the initiator component. Examples of using these connector object kinds is given in
section 9.4.
Binding Connector Objects 211

9.2 Binding Connector Objects


Every connector object class includes a predefined method used to connect it to the next con-
nector object. The syntax for this method is:
function void connect(this_type next_connector_object)
Each connector object is linked with the next connector object by calling its connectO
function with argument next_connector_object. In this context, next connector object is
defined based on a directional path starting from an initiator component to a target compo-
nent. For example, given the connection path from component comP2 to camps in figure 9.2,
next connector object for P2 is P4, next connector object for P4 is eo, and next connector object
for eo is i1.

Connector objects in two subcomponents should be connected by the common parent


component of these subcomponents. This approach is part of recursive construction guide-
line (section 7.1) for building a hierarchical environment.

Assuming connector object from_co is being connected to connector object to_co


through statement "from_co.connect(to_co)", the following rules should be followed:
• A connector object can be connected only to a single next connector object. In other
words, function connect() of from_co cannot be called with any other connector object
once it is called with argument to_co. A connector object can, however, receive con-
nections from multiple connector objects. For example, connector object P4 in figure
9.2 is receiving two connections but is connected only to one object eo (see analysis
interfaces in section 9.5 for an exception to this rule).
• If from_co is a port connector object, then to_co can be a port connector object, an
export connector object, or an imp connector object.
• If from_co is an export connector object, then to_co can be an export connector object
or an imp connector object.
• An imp connector object cannot be connected to any other connector objects since it
is always the last connector on a connection path.

Function connectO is used only to specify which connector objects must be connected. It
does not connect these objects. Function resolve_all_bindingsO of the top level component in
the hierarchy is used after alI connections between connector objects are specified by using
function connectO to actually establish the necessary connections. Note that by default, func-
tion resolve_all_bindingsO is called as part of the elaboration phase of simulation (table 7.1).
As such, calling this function explicitly is only required for specialized implementations
where the predefined simulation phases of OVM are not being used.
The following program shows an example of creating a unidirectional and a bidirec-
tional transaction interface across two components. Note that in this example, the focus is on
the mechanics of implementing the methods supported by a transaction interface and on
binding the connector objects that fonn this transaction interface. Details of unidirectional
and bidirectional interfaces are described in sections 9.3 and 9.4. In addition, the use of trans-
action interfaces in building a verification environment according to the guidelines ofOVM
is shown in chapter J 3.
212 OVM Transaction Interfaces

I Program9.1: Binding transaction interfaces


1 module top;
2 'include "ovm.svh"
3
4 class request extends ovm_transaction; byte packet; endclass
5 class response extends ovm_transaction; byte packet; endclass
6
7 class initiator extends ovm_component;
8 ovm_blocking_put_port #(request) bp_port;
9 ovm_nonblocking_transport_port #(request, response) nbtr-port;
10
11 function new(string name, ovm_component parent);
12 super.new(name, parent);
13 bp_port = new("bp_port", this);
14 nbtr_port = new("nblr_port", this);
15 endfu nclion
16 endclass
17
18 class target extends ovm_component;
19 ovm_blockin9_PUUmp #(request, target) bpjmp;
20 ovm_nonblockin9_transport_imp #(request, response, target) nbtUmp;
21
22 function new(string name, ovm_component parent);
23 super.new(name, parent);
24 =
bp_imp new("bpjmp", this);
25 =
nbtUmp new("nbtrjmp", this);
26 endfunction
27
28 task put(input request req); 'message(OVM_INFO, (req.packet)) endtask
29 function bit nb_transport(input request req, output response rsp); return 1;
30 endfunction
31 end class
32
33 class top_comp extends ovm_component;
34 initiator iO;
35 target to;
36
37 function new(string name, ovm_component parent);
38 super.new(name, parent);
39 iO = new("iO", this);
40 to = new("tO", this);
41 iO.bp_port.connect(tO.bpjmp);
42 iO.nbtr_port.connect(tO.nbtUmp);
43 endfunction
44 endclass
45
46 top_camp Ic;
47
48 initial begin
49 =
tc new("tc", null);
50 tc.resolve_all_bindings();
51 end
52 endmodule

This example provides a unidirectional interface of type blocking_put and a bidirectional


interface of type nonblocking_transport between an initiator and a target component. Classes
request and response define transaction types. and are defined first (lines 4-5). Class initiator
(lines 7-\6) is defined to include two pOll connector objects bp_port and nbtr_port (lines 8,
9). These objects are then initialized in the constructor for this class (lines 13, 14). Class lar-
get (lines 18-31) is defined to include two imp connector objects bp_imp and nbtr_imp (lines
19-20). These objects are initialized in the constmctor for this class (lines 24, 25). The inclu-
Unidirectional Interfaces 213

sion of these imp connector objects in class target requires that the implementation for the
predefined methods of these connector objects also be defined in class target (section 9.3).
The implementation for these predefined methods is given in class target (lines 28-30). The
top level component for this example, top_comp contains io and to, instances of classes initia-
tor and target respectively (lines 34, 35). These instances are initialized in the constructor for
this class (lines 39, 40), and the port and imp connector objects inside each instance are then
connected using function connect(), and according to the ordering rule required for making
such connections (lines 41, 42). The top level component is then instantiated in the top level
module (line 46), initialized (line 49), and all of its connections finalized by calling its pre-
defined function resolve_all_bindingsO (line 50).

9.3 Unidirectional Interfaces


The semantics of transaction flow across a transaction interface is defined by the methods
that an imp connector object supports. Table 9.1 lists all of the predefined methods that may
be supported by a unidirectional transaction interface.

Category Type Return Name Arguments


Blocking Put task void put (input TR t)
function bit tryyut (input TR t)
Non-blocking Put
function bit canyut 0;
Blocking Get task void get (output TR t)
function bit try_get (output TR t);
Non-blocking Get
function bit can_get 0;
Blocking Peek task void peek (output TR t);
function bit tryyeek (output TR t);
Non-blocking Peek
function bit canyeek 0;
Table 9.1: Transaction-Based Communication Methods

The behaviors implemented by these methods are as follows:


• put(): Sends a transaction of type TR from the initiator to the target. A target imple-
menting the interface will block the calling thread if it cannot immediately accept
delivery of the transaction. Because of the potential for waiting, only tasks may call
this method.
• try_putO: If possible, sends a transaction of type TR from the initiator to the target. If
the target is ready to accept the transaction argument, it does so and returns 1. Other-
wise it returns O.
• can_put(): Returns 1 if target is ready to accept a transaction from the initiator, else it
returns o.
• get(): Returns a new transaction of type TR from the target to the initiator. The calling
thread is blocked if target cannot immediately provide the requested transaction. The
new transaction is returned in the provided output argument. The implementation of
getO must regard the transaction as consumed. Subsequent calls to getO must return a
214 OVM Transaction Interfaces

different transaction instance. Because of the potential for waiting, only tasks may
call this method.
• try_getO: Returns a new transaction of type TR from the target to the initiator. If a
transaction is immediately available, it is returned in the provided output argument
and function returns 1. Otherwise, the output argument is not modified and 0 is
returned.
• can_getO: Returns 1 if target is ready to return a transaction immediately upon calling
gat() or try_get(). Otherwise it returns O.
• peekO: Returns a new transaction from target without consuming it. Ifa transaction is
available, then it is written to the provided output argument. If a transaction is not
available, the calling thread is blocked until one becomes available. The returned
transaction is not consumed. A subsequent call to peekO or gat() will return the same
transaction. Because of the potential for waiting, only tasks may call this method.
• try_peekO: Returns a new transaction without consuming it. Ifavailable, a transaction
is written to the output argument and 1 is returned. A subsequent call to peekO or gat()
will return the same transaction. If a transaction is not available, the argument
remains unmodified and 0 is returned.
• canJ)eekO: Returns 1 if a new transaction is available, 0 otherwise.

The OVM class library defines the following unidirectional interface types (xxx indi-
cates connector object kind, and can be one of "port", "export", or "imp"):
• ovm_blockin9.-puUcxX
• ovm_nonblocklng_puCxxx
• ovm_puCxxx
• ovm_blocking-'!etyxx
• ovm_nonblocklng_get_xxx
• ovm_get_xxx
• ovm_blocklng_peek_xxx
• ovm_nonblocklng_peek_xxx
• ovm_peek_xxx
• ovm_blocklng_get_peek_xxx
• ovm_nonblocklng_get_peek_xxx
• ovm_get_peek_xxx

The different interface connector object kinds (i.e., port, export, imp) are used to make a
physical connection between components where imp connector objects are instantiated
inside target components and port connector objects are instantiated inside initiator compo-
nents. On the other hand, the different connector object types listed above, define the direc-
tion and semantics of data exchange between the initiator and the target components.
Unidirectional interface types differ in the methods they support. Table 9.2 summarizes
the methods supported by each of these unidirectional interface types. For example, connec-
tor object kinds ovm_puCport, ovm_puUmp, and ovm_put_export support only methods putO,
try_putO, and can_put().

The set of methods supported by each interface type also define the data flow direction
of the interface. For example, peekO is supported by only an imp connector object that is
inside a producer component. Similarly, putO is supported by only an imp connector object
that is inside a consumer component. This means that an ovm_peek_xxx interface is used
when the initiator component is the transaction consumer, while an ovm_put_xxx interface is
used when the initiator component is the transaction producer.
Unidirectional Interfaces 215

Unidirectional Connector Object Types


(xxx gives connector kind: port, export, imp)

~
"'I
><
''"'I"" ''"" ''><"" tJ"
Used
><
''""
_I '" ~I
..,.1~ ...
.. I
~
when TLM
'" ::>
;1''"" "G>

"" ~
"
"
"j ..,..5~ ''""
target Methods :1
::> J j ~

~'I
is
~ ~ J
00
J I '"
:Q
''"" :.;;;'" :g :1''"" :.;;;'" :g ~I :.;;;'" ::0
OJ)
Q Q Q Q

~ ~
0 0 0

" '" .::>'::1 .D..9" " ::0 '" ~


<.J ()
0 0 0 0 0 0 0

~I "'IS ~ Sl '~I" '"~ ~I E'" I e"0, S1 ~I e"'l


::0
;> ;> ;> ;> ;> ;> ;> ;> ;>
0 0 0 0 0 0 0 0 0 0 0 0

putO li!:'I li!:'I


Consumer tryyutO It! It!
canyutO 0 0
getO 0 0 iii 0
try_getO iii 0 iii 0
can~etO 0 0 iii 0
Producer
peekO iii 0 0" 0
tryyeekO iii 0 0" 0
canyeekO 0 0 0" 0
Table 9.2: Methods Supported by Unidirectional Transaction Interfaces

Figure 9.3 shows an example of the interaction between a producer and a consumer
component. In this example, both the producer and the consumer can be the transaction initi-
ator. If a transaction is initiated by the producer, then it places a transaction into the con-
sumer component through its put_port. If a transaction is initiated by the consumer
component, then it can either peek into, or get a transaction, from the producer through its
get_peek_port. A level of hierarchy is added to show how connector objects should be used to
route transaction interfaces through hierarchy layers. Because of the inclusion of a puCport
and a get_peek_port, the implementation of this example highlights the use of all predefined
methods listed in table 9.2. The implementation of this example is shown in the remainder of
this section.
Top Level Block

Figure 9.3 Unidirectional Transaction Interface Example


216 OVM Transaction Interfaces

The first step in defining this environment is to define the base transaction type. This
definition is shown in the following program:

'Program 9.2: Defining the transaction object


1 class transaction extends ovm_transaction;
2 byte packet;
3 . endclass
L---____________.________

The implementation of producer core component highlights the use of connector objects
ovm_put_port, ovm_get_peek_imp and implementation of methods that must be supported by
imp connector object geCpeek_imp. This implementation is shown below:

'Program 9.3: Producer core implementation


1 class producer_core extends ovm_component;
2 ovm_put_port #(transaction) put_port;
3 ovm_get_peek_imp #(transaction, producer_core) get_peek_imp;
4
5 function new{string name, ovm_component parent);
6 super.new{name, parent);
7 =
put_port new("put_port", this);
8 get_peekjmp = new("get_peek_imp", this);
9 end function
10
11 transaction to_consumer[$j;
12
13 function bit can_getO; return (to_consumer.sizeO > 0); endfunction
14
15 task get(output transaction tr);
16 if (can_getO ==0) @(can_get());
17 tr =to_consumer.pop_front();
18 endtask
19
20 function bit try_get(output transaction tr);
21 if (can_getO == 0) return 0;
22 tr = to_consumer.pop_frontO;
23 return 1;
24 endfunction
25
26 function bit can_peekO; return (to_consumer.size() > 0); endfunction
27
28 task peek(output transaction tr);
29 if (can_getO ==0) @(can_get());
30 =
tr to_consumer[O);
31 endtask
32
33 function bit try_peek(output transaction tr);
34 if (can_peekO == 0) return 0;
35 =
tr to_consumer[O);
36 return 1;
37 endfunction
38 endclass

In this implementation, class producer_core is defined to have port connector object


put_port of type ovm_put_port (line 2) and imp connector object get_peek_imp of type
ovm_get_peek_imp (line 3). These connector objects are initialized in the constructor for tbis
class (lines 7, 8). The implementation of methods that must be supported by get_peek_imp
(lines 13-37) is based on the assumption that produced transactions are placed on queue
to_consumer (line II). The part of implementation that interacts with put_port is not shown,
Unidirectional Interfaces 217

but is assumed that a call to any of the predefined methods of put_port results in calling the
implementation of that method in the consumer core component. Any call to predefined
methods of a port object connector of type ovm_get_peek_port in another component that is
connected to get_peekJmp of this class, results in the implementation of that method in this
class to be called.
The implementation of consumer core component highlights the use of ovm_puUmp,
ovm_get_peek_portand implementation of methods that must be supported by imp connector
object get_puUmp. This implementation is shown below:

'Program 9.4: Consumer core implementation


1 class consumer_core extends ovm_component;
2 ovm_puUmp #(transaction, consumer_core) puUmp:
3 ovm_get_peek_port #(transaction) get_peek_port;
4
5 function new(string name, ovm_component parent);
6 super.new(name, parent);
7 puUmp = new("puUmp", this);
8 =
get_peek_port new("get_peek_port", this);
9 endfunction
10
11 transaction from--.producer[$:5];
12
13 function bit can_putO;
14 return (from_producer.sizeO < 5);
15 endfunction
16
17 task put(input transaction tr);
18 if (can_putO == 0) @(can_put());
19 from_producer.push_back(tr);
20 endtask
21
22 function bit trLPut(input transaction tr);
23 if (can_putO == 0) return 0;
24 from_producer.push_back(tr);
25 return 1;
26 endfunction
27 end class

In this implementation, class consumer_core is defined to have imp connector object


puUmp of type ovm_puUmp (line 2) and port connector object get_peek_port of type
ovm_geCpeek_port (line 3). These connector objects are initialized in the constructor for this
class (lines 7, 8). The implementation of methods that must be supported by puUmp (lines
13-26) is based on the assumption that transactions to be consumed are placed on queue
from_producer (line 11). The part of implementation that interacts with get_peekJ>ort is not
shown, but it is assumed that a call to any of the predefined methods of geCpeek_port results
in calling the implementation of that method in the producer core component. Any call to
predefined methods of a port object connector of type ovm_putJ>ort (e.g., putO) in another
component that is connected to puUmp of this class, results in the implementation of that
method in this class to be called.
The implementations of producer and consumer components highlight the inclusion of
connector objects for the purpose of crossing hierarchy layers. These implementations are
shown in the following program:
218 OVM Transaction Interfaces

~ogram 9.5: Producer and consumer component implementation


1 class producer extends ovm_component;
2 ovm_put_port #(transaction) put_port;
3 ovm_getyeek_export #(transaction) geCpeek_export;
4 producer_core core;
5
6 function new(string name, ovm_component parent);
7 super.new(name, parent);
8 put_port = new("puCport", this);
9 get_peek_export =new("get_peek_export", this);
10 core = new("producer_core", this);
11 core.putyort.connect(put_port);
12 get_peek_export.connect( core .get_peek_imp);
13 endfunction
14 end class
15
16 class consumer extends ovm_component;
17 ovm_put_export #(transaction) put_export;
18 ovm_get_peek_port #(transaction) get_peek_port;
19 consumer_core core;
20
21 function new(string name, ovm_component parent);
22 super.new(name, parent);
23 put_export =new("put_export", this);
24 get_peek_port = new("get_peek_port", this);
25
26 core =new("consumer_core", this);
27 put_export.connect(core. puUmp);
28 core.geCpeek_port.connect(get_peek_port);
29 endfunction
30 . endclass
~

The implementation of class producer (lines 1-14) contains object core which is an
instance of producer_core, port connector object put_port used to connect core.puCport to the
higher layer, and export connector object geCpeek_export used to connect the higher layer to
core.get_peek_imp. These objects are initialized in the class constructor (lines 8-10) and then
appropriate connections are made between connector objects in the local scope and connec-
tor objects in object core (lines II, 12). The implementation of class consumer (lines 16-30)
follows the same flow.

The implementation of component unidir_example_top highlights the linking of connec-


tor objects of components that exist in the same level of the hierarchy. In this implementa-
tion, shown below, connector objects inside instances of producer and consumer components
are connected in the class constructor for class unidir_example_top:
~-----------------

,Program 9.6: Example top component implementation


1 class unidir_example_top extends ovm_component;
2 producer prod;
3 consumer cons;
4
5 function new(string name, ovm_component parent);
6 super.new(name. parent);
7 prod =new("prod", this);
8 cons = new("cons", this):
9
10 prod .put_port.connect( cons.put_export);
11 cons.get_peek_port. can nect(prod .get_peek_export):
Bldlrectionallnteriaces 219

12 endfunction
13 endclass

The implementation of environment top level, shown below, highlights the step for
resolving all bindings made in the hierarchy rooted at a component, and illustrates the use of
a transaction port by calling its predefined method:

~ram 9.7: Top level component instantiation and binding resolution


1 module top;
2 'include "ovm.svh"
3 'include "program_9.2" 1/ transaction class
4 'include "program_9.3" 1/ producer_core class
S 'include "program_9.4" /I consumer core class
6 'Include "program_9.S" /I producer and consumer class
7 'include "program_9.6" /I unidir_example_top class
8
9 transaction tr;
10 unidir_example_top extop;
11
12 initial begin
13 extop =new("unidir_example_top", null);
14 extop.resolve_aILbindingsO;
1S tr =new();
16 extop.prod.core.put_port.put(tr);
17 end
18 endmodule

In this implementation, an instance of component unidir_example_top is first created


(line 13). The connections between all components in the hierarchy rooted at extop are then
resolved by explicitly calling predefined function resolve_all_blndings() of extop derived from
base class ovm_component. A new transaction is then created (line 15) and passed to method
put() of port object put_port of component extop.prod.core. This call results in calling method
put() of instance extop.cons.core with argument tr.
To keep the implementation concise, the example shown in this section uses the class
constructor to create and link connector objects. In a full-scale verification environment, the
steps for creating and linking connector objects are perfonned as part of verification hierar-
chy construction flow (section 7.2). In addition, function resolve_all_blndings() is called by
default in the elaboration phase of a verification flow (section 7.4), therefore removing the
need for explicitly calling this function in a verification environment managed through the
predefined simulation phases of the OVM class library.

9.4 Bidirectional Interfaces


Unidirectional interfaces allow transactions to move in only one direction. In contrast, bidi-
rectional interfaces allow transactions to move in both directions across the interface. A bidi-
rectional interface is identified by two transaction types, one for each transter direction.
Bidirectional interfaces use the methods defined for unidirectional interfaces (table 9.1) to
moVe data in both directions. In addition. bidirectional interfaces may support methods listed
in table 9.3.
220 OVM Transaction Interfaces

Category Type Returll Name Arguments


Transport task transport (input REQ req. output RSP rsp)
Non-blocking Transport function bit nb _transport (input REQ req. output RSP rsp)

Table 9.3: Transaction-Based Communication Bidirectional Methods

The behavior implemented by methods listed in table 9.3 is as follows:


• transportO: Sends a transaction request to the target component for execution. Upon
return, a response is provided by the target in the output argument rsp. Blocking
might occur during execution, so only tasks may call this method.
• nb_transport(): Sends a transaction request to the target component for immediate exe-
cution. Execution must occur without blocking. The response is provided by the tar-
get in the output argument. If for any reason the request could not be executed
immediately, a 0 must be returned, otherwise 1 is returned.
The OVM class library defines the following bidirectional interface types (xxx indicates
connector object kind, and can be one of "port", "export", or "imp"):
• ovm_blocking_master_xxx
• ovm_nonblockin9_master_xxx
• ovm master xxx
• clVm=blockir;"{Lslave_xxx
• ovm_nonblockin9_slave_xxx
• ovm slave xxx
• ovm=blocklng_transport_xxx
• ovm_nonblockin9_transport_xxx
• ovm_transporCxxx

Bidirectional interface types have the ability to use a single interface to send one type of
transaction from the transaction initiator to the transaction target, and receive a different kind
of transaction from the transaction target. The types of these transactions are given by the
parameters specified when instantiating bidirectional interface objects (see section 9.1 for the
required parameter list for bidirectional connector objects). Master or slave connector objects
use methods defined for unidirectional interfaces to move transactions between the initiator
and the target on a single interface. Transport connector objects use methods especially intro-
duced for bidirectional transfer of transactions. Table 9.4 summarizes the methods supported
by each of these bidirectional interface types.
A transaction initiator that is a producer contains a master connector object (e.g.,
ovm_blocking_master_port), uses putO methods to move a transaction of type req to the target
component, and uses getO or peekO methods to ask the target component for a transaction of
type rsp. In contrast, a transaction initiator that is a consumer contains a slave connector
object (e.g., ovm_blockin9_slave_port), uses get() or peekO methods to receive a transaction of
type req from the target component, and uses putO methods to return to the target component
a transaction of type rsp.

A transport connector object uses a single method call to send a transaction of type req
and receive a transaction of type rsp. This type of interface can be only used by transaction
initiators that are producers. Master interface types a l10\\' the sending and receiving of trans-
actions to be performed using separate method calls. Transport interface types require that
this exchange be performed using a single method call.
Bidirectional Interfaces 221

Bidirectional Connector Object Types


(xxx gives connector kind: port, export, imp)

''"" ''"'""
'..." 1 ~
><,
''"" 1:;1

'><...'"" 1 '" 0
~ '',,I'""" ,,'> t: 1 ~

5
TLM 0
~ '"E '" ~
Methods i:J
E
oJ 10
U;
~ C
e! ''""
oJ
C
:.;;; '''""" oJ
C
:.;;; '" -I C
~0
'"
t0 l
'~I"
Of)
C '-' ... 1 C u C
0 0
:.;;; :;;; :;;; ~
:0 ~ :0 u :0
'"0 C
0
'" "0
:0
C
0
>
'" .9 0
C C
e!
~I
:0 1 ci
~I
ci
~I
.0
\
E
> > >
13 1
>
E
> >
a> E
>
;1
>
0 0 0 0 0 0 0 0 0

Table 9.4: Methods Supported by Bidirectional Transaction Interfaces

Figure 9.4 shows an example of interaction between a producer and a consumer compo-
nent. In this example, the producer is the transaction initiator. The producer component con-
tains two bidirectional ports of types ovm_transport_port and ovm_blocking_master_port. In
both these connections, the producer component is the transaction initiator. The difference
between these two interfaces is that in the ovm_transport interface, the sending of the request
transaction and the receiving of a response transaction occurs with one method call, where in
the ovm_blocking_master interface, this exchange occurs using separate method calls. The
implementation of this example is shown in the remainder of this section.

Top Level Component

Figure 9.4 Bidirectional Transaction Interface Example


222 OVM Transaction Interfaces

The first step in implementing this environment is to define the base request and
response transaction types. This definition is shown in the following program:

'Program 9.8: Defining the request and response transaction objects


1 class request tr extends ovm transaction;
2 byte request; -
3 endclass
4 class response tr extends ovm transaction;
5 byte response; -
6 endclass

The implementation of a producer component highlights the use of ovm_transporCport,


and ovm_blocking_master_port connector object types. This implementation is shown below:

'Program 9.9: Producer implementation


1 class producer extends ovm_component;
2 ovm_transport_port #(requesUr, response_lr) transport_port;
3 ovm_blocking_master_port #(requesUr, response_tr) b_master_port;
4
5 function new(string name, ovm_component parent);
6 super.new(name, parent);
7 =
transport_port new("transport_port", this);
8 b_master_port:= new("b_master_port", this);
9 endfunction
10 endc/ass

In this implementation, class producer is defined to have port connector objects


transport_port of type ovm_transport_port (line 2), and b_master_port of type
ovm_blockin9_master_port (line 3). These connector objects are initialized in the constructor
for this class (lines 7, 8). The part of implementation that interacts with transport_port and
b_master_port is not shown, but it is assumed that a call to any of the predetined methods of
these connector objects results in calling the implementation of that method in the consumer
component.

The implementation of a consumer component highlights the use of imp connector


object types ovm_transporUmp, and ovm_blocking_masteUmp, and the implementation of
methods that must be supported by these objects. This implementation is shown below:

'Program 9.10: Consumer implementation


1 class consumer extends ovm_component;
2 ovm_transport_imp #(request_tr, response_tr, consumer) transportjmp;
3 ovm_blocking_masteUmp #(requesUr, response_tr, consumer) b_masterJmp;
4
5 function new(string name, ovm_component parent);
6 super.new(name, parent);
7 transport_imp = new("transportJmp", this);
8 b_masteUmp := new("b_masterJmp", this);
9 endfunction
10
11 function bit must_wait_for_response(request_tr req_tr); endfunction
12 function void handle_request(requesUr req_tr); endfunction
13 fUnction response_tr get_responseO: endfunction
14 function response_tr peek at responseO; endfunction
15 task wait_until_next_request":-can_be_accepted(); endtask
16 task wait_unlil_response_is_readyO; endtask
17
18 task transport(input requesUr req_tr. output response_lr rsp_tr):
Bidirectional Interfaces 223

19 wait_until_next_request_can_be_acceptedO;
20 handle_request(req_tr);
21 wait_until_responseJs_readyO:
22 rsp_tr = get_response();
23 endtask
24
25 function bit nb_transport(input requesUr req_tr, output response_tr rsp_tr);
26 if (must_waiUor_response(req_tr» return 0;
27 handle_request(req_tr);
28 rsp_tr = get_response();
29 return 1;
30 endfunction
31
32 task put(input requesUr req_tr);
33 wait_until_next_requesl_can_be_accepledO;
34 handle_request(req_Ir);
35 endtask
36
37 task get(output response_tr rsp_tr);
38 wail_untiUesponse_is_readyO;
39 rsp_tr = get_responseO;
40 endtask
41
42 task peek(output response_tr rsp_tr);
43 wait_untiUesponse_is _read yO;
44 =
rsp_tr peek_at_responseO:
45 endtask
46 endclass

In this implementation, class consumer, derived from base class ovm_component, is


defined to have imp connector objects transport_imp of type ovm_transporUmp (line 2), and
b_master_imp of type ovm_blockin9_masteUmp (line 3). These connector objects are initial-
ized in the constructor for this class (lines 7, 8). This class includes a number of methods
used for handling request and response transactions (lines 11-16). The implementation of
these methods is not shown, but should be clear from usage and method names. The imple-
mentation of methods that must be supported by imp connector object transport_imp is shown
on lines 18-30. The implementation of methods that must be supported by imp connector
object b_masteUmp is shown on lines 32-45. It should be noted that the argument type for
task putO (line 32) is requesUr which is the first parameter used in declaring b_masteUmp
(line 3), and the argument type for tasks getO and peekO is response_tr which is the second
parameter used in declaring b_masteUmp (line 3).
The implementation of component bidir_example_top highlights the linking of connector
objects at the same level of the hierarchy. In this implementation, shown below, connector
objects inside instances of producer and consumer components are connected in the con-
structor for class bidir_example_top:

~m 9.11: Example top componenl implementation


1 class bidir_exarnple_top extends ovm_component;
2 producer prod;
3 consumer cons',
4
5 function new(string name, ovm_component parent):
6 super.new(name, parent};
7 =
prod new("prod". this);
8 cons = new("cons", this);
9
10 prod.transport_port.connect(cons.transport_imp):
224 OVM Transaction Interfaces

11 prod.b_master_port.connect(cons.b_masterJmp);
12 endfunction
13 endclass

The implementation of environment top level, shown below, highlights the step for
resolving all bindings made in the hierarchy rooted at a component, and illustrates the use of
a transaction port:

IProgram 9.12: Top level component instantiation and binding resolution


1 module top;
2 'include "ovm.svh"
3 'include ·program_9.8" /I request/response classes
4 'include "program_9.9" 1/ producer class
5 'include ·program_9.1 0" /I consumer class
6 'include "program_9.11" II bidir_example_top class
7
8 requesCtr req_tr;
9 response_tr rsp_tr;
10 bidir_example_top extop;
11
12 initial begin
13 extop = new("bidir_example_top", null);
14 extop.resolve_aILbindingsO:
15 req_tr = newO;
16 extop.prod .transport_port.transport( req_tr, rsp _ tr);
17 'message(OVM_INFO, (rep_tr.response))
18 end
19 end module

In this implementation, an instance of component bidir_example_top is first created (line


13). The connections between all components in the hierarchy rooted at extop is then
resolved by explicitly calling the predefined function resolve_all_bindings() of extop (line 14).
A new request transaction is then created (line 15) and passed to method transport!) of port
object transport_port of component extop.prod. This call results in calling method transportO
of instance extop.cons with argument req_tr and rsp_tr. The value of rsp_tr is updated by the
time statement on line 17 is reached.

9.5 Analysis Interface - - - - -


The implementation of transaction interfaces, as presented so far, does not allow a single ini-
tiator component to be connected to multiple target components. The reason for this limita-
tion is that in this type of connection, and when the initiator component is the transaction
consumer, it is not clear which target component should respond when task get() is called by
the initiator component. Connecting a single initiator component to multiple target compo-
nents is, however, very useful if the initiator component is the transaction producer. This type
of connection provides these important benefits:
• A transaction producer can broadcast its transactions to multiple target components.
• The transaction producer does not need any knowledge ofho\\' many consumer com-
ponents are receiving the transaction that is being broadcasted.
• Transaction consumers can subscribe to the broadcast as needed.
Analysis Interface 225

One common use model for this type of connection is when a monitor component col-
lects packets from a DUV port, and then broadcasts these collected packets. The broadcasted
packet can then be received and processed by mUltiple components requiring access to this
packet (e.g., scoreboard).
The OVM class library provides the analysis interface for implementing this type of
broadcast connectivity. The analysis interface supports the following connector object kinds:
ovm_analysis_port #(tUype)
ovm_analysis_export #(tUype)
ovm_analysisjmp #(tUype, imp_parenUype)

In this syntax, tUype gives the type of broadcasted transaction, and imp_parent_type
gives the class type for the component in which the imp connector object is placed.
An analysis interface supports only a single method given by the following function
prototype:
fu nctlon void write(input tUype tr);

The implementation ofthis function should be supplied in a target component, which by


definition of an analysis interface, is also the transaction consumer. Calling this function in
the initiator component results in calling the implementation of this function in all ofthe tar-
get components that are attached to this initiator component. Note that each target compo-
nent may consume the broadcasted transaction in a different way, and as such, the
implementation of this function in each target component may be customized to the local
scope of that target component.
Figure 9.4 shows an example of interaction between a broadcaster that produces trans-
actions and multiple subscriber components. In this example, the initiator component con-
tains an analysis port of type ovm_analysis_port. The target components each contain an
analysis imp connector object analysis_imp of type ovm_analysis_imp. The implementation of
this example is shown in the remainder of this section.

Top Level module

Figure 9.5 Analysis Connection Example

The first step in definii1g this environment is to define the base request and response
transaction types. This definition is shown in the following program:
226 OVM Transaction Interfaces

'Program 9.13: Defining the transaction object


1 class transaction extends ovm transaction;
2 byte packet; endclass -
3 endclass

The implementation of component broadcaster highlights the use of port connector


object ovm_analysis_port. This implementation is shown below:

'Program 9.14: Broadcaster implementation


1 class broadcaster extends ovm component;
2 ovm_analysis_port #(transaction) analysis_port;
3
4 function new(string name, ovm_component parent);
5 super.new(name, parent);
6 analysis_port = new("analysis_port", this);
7 endfunction
8 endclass

In this implementation, class broadcaster is defined to contain analysis port connector


object analysis_port of type ovm_analysis_port (line 2). This connector object is initialized in
the constructor for this class (lines 6). The part of implementation that interacts with this
analysis...:port is not shown, but it is assumed that calling the predefined function writeO of
analysis_port results in calling the implementation of that method in all of the target compo-
nents attached to this port object.

The implementation of component subscriber highlights the use of analysis imp connec-
tor object ovm_analysis_imp, a.nd the implementation of its predefined function writeO. This
implementation is shown below:

'Program 9.15: Subscriber implementation


1 class subscriber extends ovm_component;
2 ovm_analysis_imp #(transaction, subscriber) analysis_imp;
3
4 function new(string name, ovm_component parent);
5 super.new(name, parent);
6 analysis_imp = new("analysis_imp", this);
7 end function
8
9 function void write(transaction tr);
10 'message(OVM_'NFO, ("received Ir in subscriber", get_name{)))
11 end function
12 endclass

In this implementation, class subscriber is defined to contain analysis imp connector


object analysis_imp of type ovm_analysis_imp (line 2). This connector object is initialized in
the constructor for this class (lines 6). The implementation of function writeO that must be
provided by analysis_imp is shown on lines 9-11. This is a generic implementation that sim-
ply displays the received transaction, but can be changed to any required processing of the
incoming transaction.

The implementation of component analysis_exam pIe_top highlights the linking of one


analysis port connector object to mUltiple analysis imp connector objects:
Analysis Interface 227

1
'Program 9.16: Analysis example top component implementation
class analysis_exam pie_top extends ovm_component;
-----
2 broadcaster bcaster;
3 subscriber scribers(3);
4
5 function new(string name, ovm_component parent);
6 super.new(name, parent);
7 mon = new("mon", this);
8 for (int i = 0; i <= 3; i++) begin
9 string inst_name; $sformat(inst_name, "ms[%Od)", i);
10 =
scribers[i) new(inst_name,this);
11 bcaster.analysis_port.connect(scribers[i].analysisjmp);
12 end
13 endfunction
14 endclass

In this implementation, class analysis_example_top is defined to include bcaster, an


instance of broadcaster (line 2), and scrlbers, an array of objects of type subscriber (line 3).
Instance bcaster is initialized in the constructor (line 7), and then in a loop, each subscriber is
created (line 10) and connected to the broadcaster through its connector object (line 11).
The implementation of environment top level, shown below, highlights the steps for
resolving all bindings made in the hierarchy rooted at a component, and illustrates the use of
the analysis interface:

rprogram 9.17: Top level component instantiation and binding resolution


1 module top;
2 'include "ovm.svh"
3 'include "program_9.13" II transaction class
4 'include "program_9.14" /I broadcaster class
5 'include "program_9.15" /I subscriber class
6 'include "program_9.16" /I analysis_example_top class
7
8 transaction tr;
9 analysis_example_top extop;
10
11 initial begin
12 =
extop new("analysis_example_top", null);
13 extop.resolve_all_bindingsO;
14 =
tr new();
15 extop.bcaster.analysis_port.write(tr);
16 end
17 endmodule

In this implementation, an instance of component analysis_example_top is first created


(line 12). The connections between all components in the hierarchy rooted at extop is then
resolved by explicitly calling predefined function resolve_all_blndlngsO of extop (line 13). A
new request transaction is then created (line 14) and passed to method writeO of port object
analysis_port of component extop.bcaster. This call results in calling method writeO of every
instance of subscribers that is connected with analysis_port.
228 OVM Transaction Interfaces

Instantiating an imp connector object in a component leads to predefined methodes) being


added to the definition of that component (tables 9.2 and 9.4, and section 9.5). The behavior
supported by a transaction interface is implemented by defining the body of these methods.
One complication of this approach is that two imp connector objects that require the same
method cannot be added to the same component. For example, two or more
ovm_blockin9_puUmp connector objects cannot be placed inside the same component since
each of these objects requires a dedicated function putO, and the class that models a compo-
nent can contain only one definition of function put().
The OVM class library resolves this issue by providing special macros for declaring
new imp connector object types whose predefined method names are different from the
default method names assumed by the standard imp object types of the class library. Column
1 in table 9.5 summarizes the macros that are available for declaring new imp connector
objects for different types of transaction interfaces. Column 2 shows the new type that is cre-
ated by each macro.

Macro for Declaring New Imp Type Imp Connector Type Name
I 'ovm_ blocking--.put_1mp_ decl( SFX) ovm_ blockmg--.puUmp_ SFX
'ovm_ nonblocking--.puUmp _ decl(SFX) ovm_ nonblocking--.puUmp_ SFX
'ovm--.puUmp_ decl(SFX) ovm--.puUmp_SFX
'ovm_blocking~et_imp_decl(SFX) ovm_ blocking~eUmp_ SFX
'ovm_ nonblocking~eUmp _ decl(SFX) ovm_ nonblockinLgeUmp_ SFX
'ovm_geUmp_ deC\(SFX) ovm _geUmp _ SFX
'ovm_blocking_peek jmp_decl(SFX) ovm_ blocking--'peek_imp _ SFX
'ovm_ nonblocking--'peek_imp_ decl(SFX) ovm _ nonblocking_peekjmp_ SFX
'ovm_peek_imp_decl(SFX) ovm--'peek_imp_SFX
'ovm_ blockinLgetyeek_imp_decl(SFX) ovm_blockinLget--'peekjmp_SFX
'ovm_nonblocking~etJleek_imp_decl(SFX) ovm_ nonblocking_getJleek jmp_ SFX
'ovm_getJleekjmp_ decl(SFX) ovm _getyeek_imp_ SFX
'ovm_ blocking_master_imp _decl(SFX) ovm_ blocking_master_imp _ SFX
'ovm_nonblocking_master_imp_decl(SFX) ovm_ nonblocking_master_imp_ SFX
'ovm_master_imp_decl(SFX) ovm_master_imp_SFX
'ovm_blocking_slave_imp_ decl(SFX) ovm_blocking_slave_imp_SFX
'ovm_non blocking_ slave_imp decl(SFX) ovm_nonblocking_slave_imp_SFX
'ovDl_slave_imp_decl(SFX) ovm_slave_imp_SFX
'ovm_ blocking_transporUmp _decl(SFX) ovm_blocking_transport_imp_SFX
'ovm_ non_ blocking_transport_imp _decl(SFX) ovm_non_blocking_transport_imp_SFX
'ovm_transportjrnp_ decl(SFX) ovrn_transport_imp_SFX
'ovm__analysis_imp _decl(SFX) ovm analysis imp SFX

Table 9.5: Macros for Defining New Imp ConneCtor Object Types

The use of this approach to include multiple imp connector objects in one component is
shown in the following program fragment:
Transaction Channels 229

,I Program 9.18: Declaring new imp object types

1 'ovm_blocking_puUmp_declUcv)
2 'ovm_blocking_puUmp_decILxmt)
3
4 class rcv_xmCcomponent #(type T=int) extends ovm_component;
5 ovm_blocking_puUmp_rcv #(T) rcvJmp;
6 ovm_blocklng_puUmp_xmt #(T) xmUmp;
7
8 function void puUcv(input T t);
9
10
11 endfunction
12
13 function void put_xmt(input T t);
14
15
16 endfunction
17 endclass

In this implementation, macro ovm_blockin9_puUmp_decIO is used to declare two new


imp connector objects types (line 1,2) that support the blocking put behavior. Note that these
macros define new class types, and as such, are placed at the same syntactical level as a class
declaration.
The use of macro ovm_blockin9_puUmp_decl() (lines I, 2) defines new imp connector
object types ovm_blockin9_puUmpJcv and ovm_blockin9_puUmp_xmt. These new imp con-
nector types are then used to instantiate imp connector objects rcv_imp and xmUmp in class
rcv_xmCcomponent (lines 5,6), adding functions puUcv() and puCxmt() respectively. Calling
function putO of a port connector object linked with imp connector object rcv_imp results in
calling function put_rcv(), while calling function put() of a port connector object linked with
imp connector object xmUmp results in calling function puCxmt().
A full implementation, using this approach to add two analysis imp connector objects to
a scoreboard, is shown in section 13.10.

9. 7 Transaction Channels
Transaction interfaces described so far in this chapter, are passive in nature. This means that
the interface simply acts as a conduit of transactions, and one side of the interface must act as
the transaction initiator and the other side must act as the target providing the implementa-
tion of methods that are called by the transaction initiator.
Introducing a communication channel that connects a producer and consumer allows
components on both sides of the interface to both be either the transaction initiator or the tar-
get. In this configuration, the communication channel would hold the infrastructure that is
necessary for managing the transfer oftransactions from the producer to the consumer. Table
9.6 shows a summary of the roles a transaction producer and consumer can take in exchang-
ing transactions.
230 DVM Transaction Interfaces

Producer Consumer Channel Type Control Flow Notes


Producer decides when a transaction
Initiator Target Not Needed Producer to Consumer should be created and passed to the
consumer.
The consumer decides when it needs
Target Initiator Not Needed Consumer to Producer a new transaction and requests the
producer to create a new transaction.
Producer decides when it needs to
send a new transaction, the con-
sumer decides when it needs a new
From both Initiators transaction. The channe I stores each
Initiator Initiator Passive
to the Channel request and acts as the intermediary
between the initiators. The channel
has to have the ability to store trans-
actions.
The channel actively requests the
next transaction from the producer
and pushes that transaction into the
From Channel
Target Target Active consumer. The timing of all transac-
to both targets
tions is decided by the channel. The
channel can be pass-through or have
memory to store transactions.

Table 9.6: Transaction Producer and Consumer Roles

Rows 1 and 2 summarize the case where one of the two communicating components is
the initiator and the other side is the target. These two cases are implemented using transac-
tion interfaces described in chapter 9.
Row 3 summarizes the case where both the producer and the consumer are initiators. In
this case, a producer decides when to pass its transaction to the channel, and a consumer
decides when to take that transaction from the channel. In this configuration, the channel is
passive (i.e., does not decide when transfers occur), and must have the ability to store trans-
actions received from the producer.
Row 4 summarizes the case where both the producer and consumer are targets. In this
case, the channel is active (i.e., decides transaction transfer timing), and can either be a
pass-through or store the transactions received from the producer.
Figure 9.6 shows the architectural view ofa passive transaction channel. As shown, the
producer and consumer contain port connector objects, and the channel contains the imp
connector objects. An important benefit derived from this configuration is that the imple-
mentation of all methods required by the imp connector objects must be provided as part of
the channel. This means that once a passive channel is implemented for a given transaction
type, there is no longer a need to provide method implementations inside either the producer
or the consumer, effectively eliminating a major part of the effort required for building a
transaction-based connection. So, given a passive channel for a given transaction type,
implementing the connection between a producer and consumer is as simple of instantiating
port connector objects inside each component and connecting them to the channel.
Passive channels are more useful than active channels. The reason is that passive chan-
nels eliminate the need for providing method implementations in the producer and consumer
components, but at the same time, leave the timing oftransaction transfer to the producer and
consumer components. Active channels, however, require that method implementations be
Transaction Channels 231

Producer Passive Channel Consumer

~ puUmp geumpg <§ '''yo"


Figure 9.6 Architectural View of Passive Channel Connections

provided by the producer and the consumer, while taking control of the timing for transaction
movement. Clearly, a passive channel is a more useful structure for connecting components.
The following passive channel types are supported by the OVM class library:
• TLMFIFO
• Analysis FIFO
• Request/response channel
These channel types are described in the following subsections.

9.7.1 TLM FIFO Channel


The OVM class library provides class tim_fifo for modeling a TLM FIFO channel. The archi-
tecture ofthis component is shown in Figure 9.7. This component provides an instance of all
unidirectional connector object types along with the implementation of the methods that
must be supported. The name and type of each connector object is given in this figure. A pro-
ducer component can connect to any of the putO type connector objects, and a consumer
component can connect with any of the getO or peekO type connector objects. This implemen-
tation also provides an analysis interface for put operations and an analysis interface for get
operations. These analysis interfaces can be connected to components that need to be
infonned anytime a transaction is passed to the FIFO channel and anytime a transaction is
received from the FIFO channel.
Given a TLM FIFO channel, the connection of a transaction producer and consumer is
implemented as follows:
• A producer component containing a port connector object supporting its required
methods is instantiated.
• A consumer component containing a port connector object supporting its required
methods is instantiated.
• A TLM FIFO channel, with its transaction type defined using parameter tUype, is
instantiated.
• Port connector objects in the producer and consumer components are connected with
the appropriate imp connector objects in the channel.
With this implementation, the producer decides when to pass a transaction to the chan-
nel, and the consumer decides when to receive a transaction from the channel. Meanwhile,
analysis ports put_ap and geCap can also be used to listen to the transaction that was put into
or received from the channel.
232 OVM Transaction Interfaces

-Inside labels give object type


-Outside labels give object name

blockin9_get_export

nonblocking_get_export
blocking_put_export
get_export
nonblocking_puCexport
blockingyeek_export
pucexport
nonblocking_peek_export

peek_export

blocking..Jjetyeek_export

ovm_nonblocking..J1et_peekJmp nonblocking_get_peek_export

get_peek_export

Figure 9.7 Implementation View oftlm_fifo

9.7.2 Analysis FIFO Channel


An analysis interface is a powerful construct for broadcasting transactions to mUltiple sub-
scriber components. One limitation of a straight connection between an analysis port object
and an analysis imp connector object is that problems can arise if the transaction producer
creates multiple transactions in the same simulation time when an analysis interface sub-
scriber cannot handle multiple transactions in one simulation time. An analysis FIFO chan-
nel provides a buffer between the transaction producer and any of its subscribers that may
not be able to handle multiple incoming transactions. The use of an analysis fifo channel is
shown in figure 9.8 where slow subscribers use an analysis channel so that they can take the
next transaction broadcasted by the producer at their own pace.

tim anal sis fifo # Ir t e


ovm_analysisJmp

Slow Subscribers
} (transaction initiators)

Fast Subscribers
'--_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ~ } (transaction targets)

Figure 9.8 Implementation View of tim_analysis _fifo

The OVM class library prm'ides class tim_analysis_fifo for modeling an analysis FIFO.
This class is implemented by adding analysis imp connector object analysis_export of type
ovm_analysisJmp to an unbounded TLM FIFO channel. The implementation of function
Transaction Channels 233

write() for this component writes the incoming transaction into the TLM FIFO. A producer is
then connected to analysis_export, and subscribers are connected to put_ap.

9.7.3 Request/Response Channel


The OVM class library provides class tlm_req_rsp_channel for modeling a two-way transac-
tion channel. This channel contains two TLM FIFO channels, one channel identified by
transaction type req_type is used for moving request transactions from the producer to the
consumer, and one channel identified by transaction type rsp_type is used for returning a
response transaction from the consumer to the producer (figure 9.9).

Figure 9.9 Implementation View oftlmJeCLrsp_channel


234 OVM Transaction Interfaces
235

PART 4
Randomization Engine
and Data Modeling
236
CHAPTER 10 Constrained Random
Generation

Constrained random generation is a key technology and a fundamental requirement for


implementing a coverage-driven verification environment. SystemVerilog provides the full
range of constructs and features necessary for generating random data objects subject to a set
of constraints, where these constraints can be selectively activated, deactivated, or further
extended.
Effective use of SystemVerilog randomization features requires a good understanding
of how random values are assigned to variables and how constraints affect the value and
probability distribution of the generated values. This knowledge will prove especially useful
as program size grows and constraint complexity and interdependencies lead to seemingly
unexpected problems in both failed generations and unexpected probability distributions of
the generated values.
This chapter provides a detailed look at the constrained random generation feature of
System Veri log. Section 10.1 describes the abstract operation of a constraint solver. Section
10.2 provides an overview of random generation in SystemVeri log and the use of constraint
blocks for controlling randomization. Sections 10.3 and lOA describe constraint operators
and constraint guards respectively. Section 10.5 describes how constraint operators and con-
straints guards are used to control randomization. Section 10.6 defines random stability and
describes how it is achieved in SystemVerilog. Section 10.7 summarizes SystemVerilog ran-
domization system functions.

10.1 Random Generators and Constraint Solvers


Random generation engines and constraint solvers are only as good as their implementation.
Two constrained random generators that provide the same interface and look very similar on
the surface can in practice behave very differently. Implementation quality of a constrained
random generation engine is measured in terms of:
• Its ability to find solutions to a set of constraints, if one exists
• Uniformity of the random values it generates within its reachable set I
238 Constrained Random Generation

• How quickly it converges on a solution


A poorly implemented constraint solver may not be able to identify all comer cases that
are reachable based on the given set of constraints. This problem may occur when the ran-
domized variables are assigned one at a time instead of considering all the randomized vari-
ables at the same time. Once the randomization engine identifies a reachable space defined
by the constraints, it should produce random variables that are uniformly distributed within
that reachable set. Correct implementation of these goals requires the constraint solver and
the randomization engine to solve more complex problems and, therefore, take longer to
complete a given task. As such, speed becomes a major consideration when rating random-
ization engines. As described later in this section, the SystemVerilog constrained random
generation engine is implemented to produce results that meet these targets, with the compu-
tation speed subject to different vendor offerings.
Constrained random generation is a simple concept to understand. However, its effec-
tive application to a real problem requires a good understanding of the following issues:
• How constraints are solved
• How random numbers are assigned
• The impact of constraints on the generated values (reachable set)
• The impact of constraints on the probability distribution of generated values
• The impact of explicit and implicit variable orderings, imposed because of con-
straints, on the probability distribution of generated values
• How randomness can be controlled in a predictable manner (i.e., random stability)
SystemVerilog random generation constructs can be explained in two phases. The first
step is to understand how constraint solving and random generation work for a set of random
variables and constraints. The second step is to understand the utilities and constructs that
SystemVerilog provides for deciding what variables are randomized and how constraints are
specified and controlled.

10.1.1 Constrained Randomization and Variable Ordering Effects


A constrained randomization problem is stated as follows: Given a set of integral variables
(e.g., taking discrete values) and a set of constraints describing a relationship between these
variables, assign random values to these variables that meet the specified constraints.
Consider the following simple randomization problem:
Example 10.1: Assign random values to bits VI and V2 subject to constraint:
V2 >= v1
First, the reachable set for this problem is computed subject to the specified constraint. This
reachable set is RS(Vl.v2)={(O.O).(O.1).(1.1)}. Note that combination Vl,V2=(1.0) is not reachable
since vl=1 is greater than v2=O.
The next step is to assign values to random variables VI and V2' The SystemVerilog random
generation engine considers all \'ariables at the same time, This means that it does not assign val-

1. The reachable set RS("'\'2'''\n) for random ,'ariables \',_ "2. ,.. \'n' is defined as the set of all valid
simultaneous value 3s,ignmenrs to these variables that satisfy a set of randomization constraints.
Random Generators and Constraint Solvers 239

ues to V1 and v2 one at a time, but instead picks a value from the reachable set. In this case, the
random generation engine picks a value from the set {(O,O),(O,1),(1,1)} with equal probability of
1/3 for each possible value.
It is instructive to compute the probability of variables V1 and V2 taking different values. It can
be seen from RS(V1,V2) that v1=1 for one out of three choices in the reachable set giving
prob(v1==1)=1/3, and v2=1 for two out of three choices in the reachable set giving
prob(vr=1)=2/3. In summary:
prob(v1==1)=1/3 prob(v1.v2==OO)=1/3 prob(v1.v2==10)=0
prob(v2==1 )=2/3 prob(v1.V2==01 )=1/3 prob(v1' v2==11 )=1/3

The above example is simple. In practice, constraints are more complex and may in fact
lead to cases where a variable has to be decided before the reachable set for another variable
can be computed. Consider the following randomization problem:
Example 10.2: Assign random values to bits V1 and V2 subject to constraint:
v2 >=' myfunc(v1)
In this example, the relationship between V1 and v2 cannot be immediately understood by sim-
ply looking at the given constraint, since function myfunc() may implement any function of vari-
able V1' In such cases, the value for variable v1 must be known before the constraint for variable
V2 can be considered. This requirement imposes an ordering for the assignment of random values
to variables V1 and V2, where V1 must be assigned a random value before V2 is assigned a random
value.
For simplicity, assume myfunc() returns the value of its argument. Therefore, this constraint is
in reality the same as v2>=v1' As mentioned, V1 must be assigned a value before the constraint for
V2 can be solved, therefore the focus is to first do random assignment for V1'
First, the reachable set for variable V1 is computed. No constraints apply exclusively to Vlo
therefore RS(v1), the reachable set for V1 is the set of all possible values for V1 which is the set
{0,1}. The random generation engine then randomly picks a value for V1 with equal probability
from RS(v1}'
Next, depending on the value assigned to V1, the reachable set for variable V2 is computed. If
V1 is assigned a 0, then RS(v2)={0,1}. Ifv1 is assigned a 1, then RS(v2)={1}.
It is instructive to compute the probability of a variable or a set of variables being assigned
each of their possible single or combined values. Variable v1 is assigned first where a value is
selected with equal probability from the set {0,1}, therefore prob(v1==1)=112. Ifv1 is set to 0, then
V2 may be assigned either a value of 0 or 1 with equal probability of 1/2 each, making
prob(v1.v2==OO)=1/4, and prob(v1.v2==01)=1/4. If v1 is set to 1, then V2 must be assigned to 1,
making prob(v1.v2==11)=112. Considering the probability for each combination, gives
prob(v2==1)=3/4. In summary:
prob(v1==1)=112 prob(v1.v2==OO)=114 prob(v1.V2==10)=O
prob(v2==1 )=3/4 prob(v1.V2==01)=1/4 prob{v1.V2==11)=1/2

As can be seen from this example, ordering of variables led to completely different vari-
able probabilities than those in example 10.1 because of the use of a function in describing a
constraint. Having a good sense for the probability distributions of a set of randomly gener-
ated variables. therefore, requires good understanding of the orderings that may be imposed
during the generation process.
Variable ordering that is imposed because of constraints can cause the random genera-
tion engine to fail when there is in fact a viable random assignment of variables that meets
the specified constraints. Consider the following example:
240 Constrained Random Generation

Example 10.3: Assign random values to bits Vi and V2 subject to constraint:


v2> myfunc(vi)
In this example, the use of a function call in specifying the constraint imposes an ordering in
solving the constraint. For simplicity, assume myfunc() returns the value of Vi'
In this example, RS(vi,v2)={(O,1)} since vi=O and v2=1 is the only assignment where V2 is
strictly larger than Vi' But the randomization engine may fail to find this possible valid assign-
ment. The constraint solver first assigns a random value to Vi because of the ordering imposed by
the use ofmyfunc() function call. Given that no other constraints on variable Vi exists, the random
generation engine may assign a value of 1 to variable Vi. In this case, random generation fails
because no value for v2 will satisfY the constraint when Vi is set to 1.
As shown in this example, variable ordering can cause the random generation engine to
fail when there does in fact exist a viable solution to the randomization problem. As such,
constraints that impose a variable ordering should be avoided as much as possible. Con-
straints are labeled as unidirectional constraint if they impose a variable ordering, and bidi-
rectional constraint if no such ordering is imposed.
Variable ordering has two effects on the random generation process:
• It may lead to false fails of the generation engine.
• It changes the probability distribution of the values assigned to random variables.
The first problem should be avoided as much as possible. The second side effect, how-
ever, can be useful at times. Consider the following example:
Example 10.4: Assign random values to bit Vi and 32-bit unsigned integer V2 subject to the
following implication constraint:
(vi==O implies that v2==0)
In this example, RS(Vi,V2)={(O,O),(1,O),,,.(1,232.1)}, Note that this set contains 232+1 elements
where vi=O for only the first element. The random generation engine picks a value with equal
probability from this set. This means that prob(vi==O) is 1/(232+1) which is almost zero, There-
fore, the solution provided by this generator will almost never assign Vi to a value ofO.
Recall that random value generation consists of two phases: 1) computing the reachable
set for all random variables involved, and 2) selecting an entry from this reachable set which
simultaneously assigns a value to all variables involved (e.g., in the above example, selecting
(1,0) from RS(vi,v2) assigns v1=1 and V2=O). In some cases, it is possible to better control the
probability distribution of random variables by specifying a variable ordering for the second
phase of random value generation. This type of variable assignment ordering does not suffer
from the drawback of constraint imposed orderings where the randomization engine may fail
even when a viable assignment exists.
In the above example, assume Vi is to be assigned a value before V2 but after RS(Vi,v2) is
computed. This is in contrast with selecting a value from RS(vi,v2) which assigns values to
both variables Vi and V2 at the same time. In this case, first RS(vi) is computed by including
all values for Vi that appear in RS(vi,v2)' In the above example, RS(vi)={O,1} and a value from
this set is randomly selected for variable Vi' Second, RS(v2) is computed by including all val-
ues for V2 that appear in RS(vi,v2) whose value assignment for Vi is the currently selected
\'alue for Vi' A value for V2 is then selected randomly from RS(v2)' By allowing variable Vi to
be assigned before variable V2, it is now possible to assign Vi to both 0 and 1 with equal prob·
Random Generators and Constraint Solvers 241

ability. Note that the process described in this example can easily be extended to mUltiple
variables (e.g., assign V1 and V2 before V3 and V4)'
Examples provided in this section identify three modes of operation for a constrained
random generator:
• Non-ordered: No variable ordering
• Assignment-based ordered: Variable ordering only during random value assignment
• Constraint-based ordered: Variable ordering during constraint solving (and hence
during value assignment)
A constrained random generation engine should optimally be operating in non-ordered
mode, with selective use of assignment-ordered mode to control random value probabilities.
Constraint-ordered mode will be imposed by some constraints and at times cannot be
avoided. The following sections describe the random generation utilities of SystemVerilog
with these properties and behaviors in mind.

10.1.2 Random Generation Engine


Operation of a random generation engine is affected by assignment-based and con-
straint-based ordering requirements. Details of how these orderings are implicitly or explic-
itly imposed will be described as part of introducing SystemVerilog randomization
constructs. In this section, the operation of the random generation engine is described assum-
ing these orderings have already been indicated.
Given a set of random variables and a set of constraints describing the relationships
between these variables, the random generation engine operation can be outlined as shown in
the following pseudo-code. Note that this pseudo-code is a logical view of how the engine
operates, and the actual implementation will vary from this description.

'Program 10.1: Random Generation Engine Pseudo-Code:


1 -Rule out circular dependencies in constraint-based and assignment-based orderings.
2 -Group random variables based on constraint-based ordering of variables.
3 -For each group G in the ordered set of groups,
4 -Identify constraints to consider for variables in G.
5 -Solve the constraint and compute the reachable set for variables in group G.
6 -Identify the reachable set for variables in group G marked with randc.
7 -Assign values for variables in group G marked with randc.
8 -Group unassigned vars in G into G1 and G2 based on assignment-based ordering.
9 -Identify reachable set RS1 for the variables in G1 given randc assignments.
10 -Select a member with uniform probability from RS1 and assign to variables in G1.
11 -Identify reachable set RS2 for variables in G2 given G1 and randc assignments.
12 -Select a member with uniform probability from RS2 and assign to variables in G2.
13 -Continue with the next group G, if any.

The first step in solving the randomization problem is to rule out any circular dependen-
cies in any constraint orderings. Consider the following example:
Example 10.5: Assign random \'alues to bits V1 and V2 subject to constraints:
V1 > myfunc(v2)
solve v, before v2
The set of constraints for this example leads to a circular dependency. The first constraint
leads to a constraint-based ordering of\'ariables where the reachable set for V2 must be computed
242 Constrained Random Generation

first and Y2 assigned a value before Y1 can be assigned a value. The second constraint leads to an
assignment-based ordering requiring that v1 is assigned a value before Y2. An error message will
be generated when such circular dependencies are detected.
The next step is to partition the random variables into ordered groups of variables where
the variables in each group must be assigned values before variables in the following group
can be processed. Constraint-based ordering is used to identify these partitions. Consider the
following example:
Example 10.6: Assign random values to 2-bit V1, Y2, V3, V4, Vs subject to:
1: Y3 > myfunc(v2)
2: v3 > myfunc(v4)
3: Ys > myfunc(v4)
4: Y1 < v3
5: v2 <v4
6: solve v3 before V1
A general guideline for this grouping is to postpone assigning a variable to as late a time as
possible. The goal here is to increase the flexibility in assigning values to variables in the later
groups. Based on this guideline, and the constraint-based required ordering of assigning V2 before
V3, a~signing v4 before Y3, and assigning V4 before vs, the ordered groups obtained for this exam-
ple are: {V2,V4} followed by {V1,V3,vS}. Note that V1 is not involved in any required ordering so it
is placed in the last group.
Given a group G (e.g., group {V2,V4} in example 10.6), the next step is to identify the set
of constraints that must be considered for the variables in this group. Any constraint that
includes at least one reference to variables in this group, and depends on constants and state
variables 2 is included. Constraints that include variables that have not yet been processed are
excluded from this set. In the previous example, only constraint 5 is considered for group
{V2' V4}. Other constraints depend on variables that are not yet processed. Note that when
processing group {V1> Y3, Ys}, the set of constraints will include constraints 1, 2, 3, and 4.
Also note that when this second group is being processed, values for V2 and V4 have already
been assigned and as such, V2 and V4 are considered as state variables when group {V1, V3, vs}
is being processed.
The next step is to compute the reachable set for the set of variables in each group G of
all groups identified for a randomization problem. Continuing with the results in example
10.6, the following two examples show this calculation for group {V2, v4} solved along with
its relevant constraint 5, and group {V1' V3, vs} solved along with its relevant constraints I, 2,
3, and 4.
Example 10.7: Assign random values to 2-bit integers v2, V4 subject to constraint:
5: v2 < v4
In this case RS(V2.v4)={(0.1).(O,2).(O.3).(1.2).(1.3).(2.3)}.
Assume we select member (0.1), thereby setting v2=O and v4=1. Note that a peek into the
requirements for the next group shows that \"iable assignments to \·ariables Y1, V3, and Ys are only
possible for Y2.v4={(O.1).(O.2).(1.2)}. As such. during the processing of this group, selecting any

~" State I"ariables in the context of randomization cl'nstraims reters to \"ariables that have been assigned .\
\alue when the Cllrrent random assignment iteration starts and as such whose ,"alue can be conside;ed a
constant.
Randomization In SystemVerllog 243

member of set v2,v4={(O,3),(1,3),(2,3)} will lead to generation failure when processing the next
group of random variables.
Example 10.8: Assign random values to 2-bit integers V1, V3, Vs subject to constraints:
I: v3> 0
2: v3> I
3: vs> I
4: v1 < V3
This randomization problem is for the second group of variables assuming that v2=O and v4=1,
and that function myfunc() returns the value of its argument.
In this case, RS(v1,v3.vS)={(O,2,2), (0,2,3), (0,3,2), (0,3,3), (1,2,2), (1,2,3), (1,3,2), (1,3,3), (2,3,2),
(2,3,3)}.
The next step is to order the variables in each group according to assignment-based
ordering requirements. Group {V2,V4} does not have any assignment-based ordering require-
ments. Group {V1,V3,VS} does, however, require that V3 is assigned a value before Vs. This
operation is shown in the following example:
Example 10.9: Assign random values to 2-bit integers Vlo V3, Vs with the reachable set com-
puted in example 10.8, and constraint:
solve V3 before V1
The variable assignment groups are identified as {vu and then {v1,VS} where Vs is placed in
the last group in order to leave maximum flexibility in assigning values to later variables. Given
that variable V3 is to be assigned first and RS(V1,v3'vS), then RS(v3)={2,3}. Variable V3 is assigned
to a randomly selected member of RS(v3) before considering assignment for group {Vl,VS}'
Ifv3=2, then RS(V1.VS)={(O,2),(O,3),(1 ,2),(1 ,3)}.
If v3=3, then RS(v1.vS)={(O,2),(O,3),(1 ,2),(1 ,3),(2,2),(2,3)}.
Once a value is assigned to V3 and RS(Vl'vS) is computed, a member is selected randomly
from RS(v1'vS) to simultaneously assign values to variable Vl and Vs.
Without a constraint-based or assignment-based ordering requirement for the example
solved in this section, the reachable set for all variables would be computed at the same time,
and an entry selected from this set to simultaneously assign values to all random variables.
The examples in this section also highlight the fact that constraint-based ordering
requirements can lead to the possibility of assigning values to V2 and V4 that could have
resulted in generation failure. In addition, constraint-based and assignment-based orderings
led to different random probability distributions for each variable in this example.

10.2
--
Randomization
_-...
in System Verilog -----

Using random generation in SystemVerilog requires knowledge of the following topics:


• Random variables: How variables are marked as random
• Constraint blocks: How constraints are specified
• Randomization methods: How to assign random values to variables
These topics are described in the following subsections.
244 Constrained Random Generation

10.2.1 Random Variables


This section describes how variables are marked as random in SystemVerilog and how
unconstrained random variables are randomized. Constraint specification is described in the
next section.
In System Verilog, only integraP variable types can be randomized. This randomization
considers only 2-state values. This means that 4-state values (x, z) and 4-state operators (e.g.,
==, 1--) are illegal and their use in randomization constructs results in an error. In general,
any integral variable declared in modules, tasks, functions can be randomized (i.e., scope
variable randomization). The main focus in SystemVerilog, however, is on modeling ran-
domization using objects defmed as classes. The reason is that random variables and their
associated constraints can be packaged as a reusable class object which can be later
extended, inherited, constrained, overridden, enabled, disabled, and merged with or sepa-
rated from other objects.
The following program segment shows examples of how random variables can be spec-
ified in a SystemVetilog program:

'program 10.2: Specifying random variables


1 module top;
2 typedef enum {short, med, long} packeUype;
3 class rand_class;
4 rand int unsigned int_var;
5 rand bit bit_var;
6 rand bit [10:0] dynamic_array-varn;
7 rand byte static_array_var[10);
8 rand rand_class class_ptr_var;
9 rand packet_type enum_var;
10 randc packet_type enum_cyclic_var;
11 endclass
12
13 logic [10:0] module_var [];
14
15 ~nitial begin
16 int scope_int_var;
17 rand_class rc;
18 rc = new();
19 rc.class_ptr_var = newO;
20 assert(randomize(scopeJnt_var,module_var)); 111 for success, 0 for failure
21 assert(rc.randomize()); Ilreturns 1 for success, 0 for failure
22 end
23 endmodule
L

In ~eneral, any integral variable in the scope of a module, program, or interface can be
randomIzed by calling the system function randomize(). In the example above, variable
scop~_int_var and module_var are randomized by passing them as arguments to the system
functIOn randomize(). This function retums a 1 upon success and a 0 upon failure.
~s mentioned, earlier, randomization is best leveraged when random variables are pack-
aged m a class object. Class rand_class (lines 3-11) contains variables that are marked as
rultd or runde. As shown in the class definition, variables of integral type can be random-

-'. The term inregral refers to data tvpes th t " .


d . k d' - a can represent a slm!!" baSIC lIlte~er data type. packed arra\'.
parke struct, pac -e UnIon, enum. or time. - -. -
Randomization in SystemVeriiog
-
245
---------------------
ized. Variable int_var (line 4) is an integer, variable biCvar (line 5) is a bit, dynamic_arraLvar
(line 6) is a dynamic array of II-bit words, static_arraLvar (line 7) is a static array of ten
bytes, class_ptr_var (line 8) is a pointer to a class object, enum_var (line 9) is an enumerated
type variable (defined on line 2), and enum_cyclic_var (line 10) is a cyclic random variable of
the same enum type.
A class object is randomized by calling the randomizeO predefined function of its class
(line 21). When calling the randomizeO function of a class object, all class members that are
marked with rand or randc are randomized.
In the absence of any constraint, each scalar variable is set to a random variable within
its valid range. For example, after executing line 21, bit_var in object rc is set to a value in the
range {O:1}, int_var is set to a value in the range {O:232_1}. Static arrays have a fixed size, there-
fore the contents of static arrays are randomized within the range of their type. Each of the
ten members of static_array_var (line 7) are set to a random value in the range (O:255}. The
size for dynamic arrays is not fixed, so first the size of a dynamic array is randomly assigned,
and once the size is set, each member is set to a random value within the range of its type.
Object pointers are ignored if the pointer is null. Otherwise, the content of the object is ran-
domized recursively, according to the same condition.
Each call to randomizeO assigns a random non-repeating value to a variable marked
with randc. Once all possible values for that variable have been generated randomly, a new
iteration is started where the order of the new set of generated values is different from the
previous pass through all possible values. Consider the following example:

"Program 10.3: rand versus randc class fields


1 class rand_container; 1/ Consecutive calls to randomizeO for this object:
2 rand bit[1:0] rand_var; II 01 21 3200 1 332 .. .
3 randc bit [1:0] randc_var; 110312 2130 3012 .. .
4 endclass

One possible set of values generated for this object for consecutive calls to randomizeO
is shown in the comment section of the above example. As shown, values generated for
randc_var (line 3) are random but every possible value for this variable is generated before a
new iteration of values is started. Values generated for rand_var may be any of the values in
the range of {O:3}, for example, value 1 is generated two times before value 3 is generated
(line 2). The permutation sequence for a randc variable is recomputed when constraints on
that variable change, or none of the remaining values satisfies the constraints on that vari-
able. The behavior of a randc variable is similar to that of dealing every card in a shuffled
deck of cards before it is shuffled again. The behavior of a rand variable is similar to that of
throwing a dice where at every roll, all possible values may appear. It should be noted that
using a randc variable that has a large set of possible values makes the constraint problem
more difficult to solve.
Variables marked with randc have a strict requirement to go through all possible values
before a generated yaille can be repeated. Becallse of this requirement, such variables are
always generated before variables marked with rand. Also, for dynamic arrays, the size of
the array must be generated before array elements can be randomized. As such, the implicit
variable that defines the size of a dynamic array is generated before the elements of that
246 Constrained Random Generation
----------------------

array. This requirement imposes an implicit constraint-based ordering between the size of a
dynamic array and the elements of that dynamic array.
The following properties apply to random variables:
• Any integral variable in the scope of a module, program, or interface can be random-
ized by calling system function randomizeO.
• Random variables are best packaged in a class, since they can be grouped along with
their constraints and reused.
• Class members marked with rande are assigned values that cycle through all of the
values in a random permutation of their declared range. Such variables can be of type
hit (or packed bit array) or an enumerated type.
• Class members marked with rand are assigned a value (possibly repeating) from their
valid range, where this value has a uniform distribution over its valid range subject to
randomization constraints that must be met.
• An object pointer in a class can be marked with rand in which case all of this object
pointer's variables and constraints are solved and randomized concurrently with the
variables and constraints of the object that contains the pointer.
• A null object pointer in a class marked with rand is ignored by the randomization
engine.
• When randomizing a class object, an object pointer in that class not marked with
rand will not be randomized, even if it has members that are marked as rand or
randc.
• Variables marked as ronde are assigned before variables marked as rand.
Random dynamic arrays introduce special constraint-based ordering and also require
special considerations when pointing to class objects. Random dynamic arrays and con-
straint-based random generation are discussed in the following sections.

10.2.2 Random Dynamic Arrays


Random dynamic arrays are used extensively in building dynamically sized data objects.
These constructs do, however, require special handling for randomization purposes. These
considerations are described in this section.
The size of a randomized an-ay must be decided and the alTay resized before any of its
members can be randomized. Therefore, randomization constraints specified for the size of a
dynamic array must be solved before any of the constraints specified for array members.
This requirement imposes an implicit constraint-based ordering between the size of a
dynamic array and members ofthat dynamic array. Given that the size of the array is already
decided by the time array members are being randomized, the size of a dynamic array is con-
sidered as a state variable when constraints for array members are being solved.
Arrav randomization consists of three steps:
• Array size randomization
• Array Rtsizing
• Array member randomization
Randomization in SystemVerliog
-
247
--------------------------------------------------------------------
Arrav size randomization is performed by considering the size of a dynamic array as a
random variable, solving any constraints specified for this random variable to identify its
reachable set, and then randomly selecting a value from this reachable set. All constraints
involving array members are excluded from the set of constraints that are solved to compute
the reachable set for the size of a random array. This exclusion leads to the implicit con-
straint-based ordering that exists between the size of an array and its elements.
Array resizing is the process of changing the number of elements in an array to the
newly generated size. In this step, the main focus is on how array member values are main-
tained across the resizing process. This special handling allows an array of objects to be
resized without loosing the object pointers at indices that remain in the array after resizing.
Given sizenew (the new randomly decided size of a dynamic array) and sizeold (its size before
randomization started), the following guidelines are used for maintaining array member val-
ues during the resizing of the array:
• If sizenew is the same as sizeold then array size or members are not modified and after
resizing the value of each array member remains the same as before randomization
started.
• If size new is larger than sizeold then array size is increased to the new size. After resiz-
ing, the value for the first (sizeold) members of the array remain the same as before
randomization started. This means that if array members are object pointers, then
these pointers are not lost and remain as before randomization started. The remaining
(size new • sizeold) members are set to the default value of the array member data type
(e.g., null for array of objects, 0 for integer, etc.).
• If sizenew is smaller than sizeold then array size is reduced to the new size. After resiz-
ing, the value for the first (size new) members of the array remain the same as before
randomization started.
Arrav member randomization is performed after the array is resized. During this step,
constraints involving array members are solved while considering array size as a state vari-
able. Each member of the array is then randomized by randomly selecting a value from its
reachable set computed by solving its constraints.
The randomization engine never allocates a new object. This means that for an array of
objects, each array member is randomi.zed if its value is not null.

10.2.3 Constraint Blocks


Constraints can be specified as constraint blocks in classes, or in-line when the randomizeO
function is called. A constraint block contains a list of expressions that specify a limit for a
single variable or relationships between mUltiple variables. Constraints are inherited along
with other class properties. This feature allows a hierarchy of constraints to be built along
with class inheritance hierarchy. SystemVerilog also provides mechanisms for disabling
existing constraint blocks as needed by a specific generation scenario.
The following program segment shows examples of constraint blocks:

[Program 10.4: Specifying constraint blocks


1 module top:
2 typedef enum {short, average. tall} box_type:
248 Constrained Random Generation

3 class box;
4 rand box_type bt;
5 rand int unsigned width;
6 rand int unsigned height;
7 constraint valid_range;
8 constraint short_box {bt == short -> height < 10;}
9 constraint average_box { bt == average -> height inside {[30:70)};}
10 constraint tali_box {bt== lall-> height> 90;}
11 endclass
12 constraint box::valid_range {width < 100 && height < 100;}
13
14 class short box extends box;
15 constraint sb { bt==
shoJi;}
16 endclass
17
18 class short and wide box extends short box;
19 constraintswb (width" 90 :} -
20 constraint short_box {bl ==
short -> height < 20;}
21 endclass
22
23 initial begin
24 short_and_wide_box swb:
25 short box sb;
26 swb ;; new();
27 sb = newO:
28 assert(sb.randomize());
29 assert(sb.randomizeO with {width == 95;});
30 assert(swb.randomizeO);
31 end
32 endmodule

The above example shows a hierarchy of constraints used to implement an object with
properties in different range of values. Class box (lines 3-11) contains the generic description
of a box and constraint blocks describing the valid ranges of its width and height properties.
It also contains implication constraints constraining the height property, depending on the
box type (lines 8-10). A constraint box can be declared outside a class declaration. Con-
straint name validJange is first declared as part of class declaration (line 7) and its descrip-
tion is then specified outside the class declaration (line 12).
Class short_box is a class inherited from box and contains only an additional constraint
that the generated box type property should be constrained to short. Class short_and_wide_box
further extends this definition to include a constraint for its width property. It also overrides
the short_box constraint defined in its parent class in order to redefine the range for a short
box.

Random values generated for variable sb (line 28) will meet all constraint blocks on
lines 8,9, 10, 12, and 15. The randomizeO function can be called with in-lined constraints
(line 29). In this example, the random values generated for variable sb (line 29) will meet the
additional constraint defined using with keyword. Random values generated for variable swb
(line 30) will meet all constraint blocks on lines 9, 10, 12, 15, 19 and 20.
Constraint-specific operators are discussed in the next section.
Constraint-Specific Operators 249

A constraint can be any System Verilog expression with integral variables and constants, or
one of the constraint-specific operators. Constraint-specific operators. are described in the
following subsections.

10.3.1 Set Membership Constraints


The inside operator is used to specify that a random variable should be assigned a value from
a set specified with this operator. Absent any other constraints, a value is selected from the
given set with uniform probability. The negation of this operator indicates that the random
value should not be in the indicated set.
The inside operator is a bidirectional constraint operator. This means that variables used
in specifying the set, and the random variables being assigned are solved concurrently. In
fact, the inside operator can also be written as a disjunction of equality constraints:
constraint {a inside {b,c,d}} is equivalent to {a == b II a ==c II a==d}
If variables are used to specify members of the set, it is possible that the ranges speci-
fied for the set overlap. The probability of each set member is still uniformly distributed even
if a set member is repeated multiple times in describing the set. Consider the following
example:

'Program 10.5: Repeated set member in specifying "inside" constraint


1 class randobj;
2 integer b=2;
3 rand integer a;
4 constraint c1 {a inside {[1 :3].[b:b+2]};}
5 endclass

In the above example, values 2 and 3 are specified two times in the set description. Each
value in the set (i.e., 1, 2, 3, 4), however, is assigned with equal probability.
Examples of this operator are shown below:

'Program 10.6: examples of "inside" constraint operator


1 class randobj;
2 rand integer a, b, c;
3 integer d;
4 constraint c1 {a inside {1,3,5,[6:91, [b:2*bl,[c:c+14]};}
5 constraint c2 {!(a inside {b,c};}
6 constraint c3 {d inside {a,b,c};}
7 endclass

Non-random variables used in specifying the arguments to the inside operator are con-
sidered as state variables whose value at the time of randomization is used to solve the con-
straint.
250 Constrained Random Generation

10.3.2 Distribution Constraints


The distribution oQeralor allows the value assigned to a random value to be selected with a
weighted probability distribution from a set of values. Distribution constraints act both as a
relational test for set membership and also to specify a statistical distribution for set mem-
bers.
The distribution operator evaluates to true if the value of the random variab Ie is in the
specified set. In the absence of any other constraints which may prevent the assignment of a
set member to the random variable, the probability of selecting each member in the set fol-
lows its given weight.
The distribution set is a comma-separated list of integral expressions and ranges. A
weight can be assigned to each expression or range. For ranges, it is possible to assign a
range for each member of that range (using the ":=" operator) or assign a weight that will be
divided equally between members of that range (using the ":1" operator). The total weight
specified for all set members need not add to 100, and each weight assignment acts as only a
relative weight to other set members. In the absence of a weight operator, a default setting of
":=1" is assumed.

The distribution operator cannot be used with variables marked with randc. Also, a dis-
tribution expression must contain at least one variable marked with rand.
The distribution operator is a bidirectional constraint and does not impose any ordering
on the random generation process.
An example of distribution operator are shown below:

'Program 10.7: Constraint distribution operator


1 class randobj;
2 rand integer a,b;
3 constraint c1 {a dis! {1 :=1, 2:=3, [4:5]:=5, [b:b+21:/3};}
4 . end class
L--___________________

In the above example, value 1 has a weight of 1, value 2 has a weight of 3, values 4 and
5each have a weight of 5, and values in the range b to b+2 each have a weight equal to the
number of values in this range divided by 3.

10.3.3 Implication Constraints


The implication operator "->" is used to declare a conditional (predicated) relation between
logical conditions.
An implication operator has the following general form:
boolean_condition1 -> boolean_condition2

This form is equivalent to the following Boolean constraint:


! boolean_condition1 II boolean_condition2

As such, the implication operator is a bidirectional operator that does not impose any
ordering on the random generation process.
Constralnt·Specific Operators 251

An example of the implication operator is shown below.

'Program 10.8: Implication constraint operator


1 module top;
2 typedef enum {short, legal, long} packeUype;
3 class packet;
4 rand packet_type pt;
5 rand integer packeUength;
6 rand bit payloadD;
7 constraint payload_size {payload.size() ==
packeUength;}
8 constraint short_pack { pt== short -> packeUength < 64;}
9 constraint legaLpack { pt== legal -> packeUength inside {[64:1 024]};}
10 constraint long_pack { pt== long -> packeUength > 1024;}
11 endclass
12
13 initial begin
14 packet p;
15 =
P newO;
16 assert(p.randomizeO with {pt == legal;});
17 end
18 endmodule

In the above example, the class declaration of a packet contains implication constraints
deciding the packet size depending on the packet enumerated type of short, legal, or long. The
payload size is also constrained to be set by variable packeUength. This implementation
allows packet type to be decided when the packet content is being randomized (line 16).

10.3.4 If-Else Constraints


SystemVerilog if-else constraints can be considered as a compound implication constraint.
This constraint operator has the following general form:
if (boolean_expression) constraint_set1 [else constraint_set2]

This operator can be written as an equivalent implication operator:


boolean_expression -> constraint_set 1;
!boolean_expression -> constraint_set2 ;

As such, if-else operator is also a bidirectional operator that does not impose any order-
ing on the generation order.
The if-else operator provides a more structured look for writing long constraint sets and
also provides a more efficient way by relating constraint sets to the value of a single boolean
expression.
The following example, shows the packet declaration in the previous example rewritten
using an if-else constraint.

'Program 10.9: if-else constraint operator


1 class packet:
2 rand packet_type pt:
3 rand integer packeUength:
4 rand bit payload[]:
5 constraint payload_size {payload.sizeO == packeUength:}
6 constraint packet_size {
7 ==
if (pt short)
8 packeUength < 64:
252 Constrained Random Generation

9 else if (pt==legal)
10 packeUength inside {[64:1024]};
11 else if (pt==long)
12 packeUength > 1024;
13 }
14 endclass

The drawback with the above approach, however, is that individual constraints for short,
legal, and long packets cannot be disabled as can be done when separate constraint blocks are
given for each constraint.

10.3.5 Iterative Constraints


Iterative constraints allow array members to be constrained using indexing expressions and
loop variables. This construct acts as a shorthand notation for specifying constraints for each
member of an array and relationships between array members and other random and state
variables. Iterative constraints provide a compact syntax for describing constraints for mem-
bers of static arrays whose size is known. Iterative constraints are, however, a necessary con-
struct for dynamic arrays since the number of elements is not known beforehand and
constraints must, therefore, be described in terms of indexing expressions.
An example of an iterative constraint is shown below:

iprogram 10.10: Iterative constraint operators


1 rnodule top;
2 class randobj;
3 /I 2 3 4 2 dimension numbers
4 /I kim i j iterative var name
5 rand bit [5:01 [4:0] [3:1] bit_var [10:1] [9:1];
6 ==
constraint c1 {foreach (bit_var [i,j,k,l,m]) bit_var[i][j][k][l][m] O;}
7 endclass
8 endmodule

The above example shows the relationship between index variables (i.e., ij,k,l,m) and
the dimension of the array variable.
The following properties apply to iterative constraints:
• The number of loop variables must not exceed the number of array dimensions.
• The scope of each loop variable is the context of the foreach construct (line 6).
• The type of each loop variable is implicitly declared to be consistent with the type of
array index.
• A loop variable can be skipped by leaving its place in the order of loop variables
blank (hence two commas appearing back to back), in which case, that dimension is
not iterated over.

Size of an array can be used in iterative constraints. This usage is shown in the follow-
ing example:

~ 10.11: Array size and iterative constraint operators


1 module top;
2 class randobj;
3 rand bit dbaD;
4 constraint c1 {dba.sizeO == 1GO;}
Constraint-Specific Operators 253

5 constraint c2 {foreach (dba[i]) (i < dba.sizeO-1) _> (dba[i+1l == dba[i]);}


6 endmodule

Recall that the use of a constraint on the size of dynamic array causes an implicit con-
straint-based variable ordering where the size of array is decided before any of the con-
straints on its members are solved. As such, when solving constraint c2 (line 5), the size of
array is used as a state variable and is assumed to be a fixed value.
Iterative constraints provide a shorthand notation for describing relationships between
array members and other random variables. As such, they do not impose any ordering on the
random generation order unless the constraint specified in the foreach constraint block
imposes such an ordering (i.e., using a function call in specifying the constraint) or using the
array size predicate as described in the previous paragraph.

10.3.6 Global Constraints


Global constraints refer to constraints that specify relationships between members of two
different class objects.
Consider the following example:

I Program 10.12: Global constraints across two separate class objects


1 module top;
2 class payload;
3 rand bit data [];
4 rand int unsigned length;
5 constraint c1 {length < 1024;}
6 constraint c2 {data.sizeO ==
length;}
7 endclass
8
9 class packet;
10 rand payload p11;
11 rand payload p12;
12 constraint c1 {pI1.length > pI2.length;}
13 function newO:
14 pl1 = newO;
15 pl2 =newO;
16 endfunction
17 endclass
18
19 initial begin
20 packet p;
21 =
P newO:
22 assert(p.randomize());
23 end
24 end module

In this example, constraint block C1 (line 12) describes a relationship between the length
parameter of two objects contained inside its class. As such, this constraint is aglobal con-
straint.
The call to ralldomizeO (line 22) solves the constraints for all variables in all involved
objects concurrently. The set of variables to randomize and applicable constraints are derived
as follows:
• First all objects that are to be randomized are found. The set of objects starts with the
254 Constrained Random Generation

object whose randomizeO method is called and any object with a rand pointer
declared inside that object (lines 10, 11). This definition is recursive in that any rand
pointers inside the newly identified object are also added to the set.
o All variables marked with rand or rande inside the identified objects are added to the
list of variables to be randomized. Any variable that is disabled using rand_modeO
function (section 10.5.2) are removed from the set of variables to be randomized.
o All constraints in the identified objects that relate to the identified variables to be ran-
domized are included in the set of constraints to be solved. Any constraints specified
along with the randomizeO function is added to this list. And any constraints dis-
abled using constrainLmodeO function (section 10.5.1) is removed from this list.
Once the set of all random variables and relevant constraints are identified, all con-
straints are solved concurrently, subject to any implicit/explicit constraint-based or assign-
ment-based orderings.

10.3.7 Variable Ordering Constraints


Variable ordering constraints allow some variables to be assigned before other variables are
assigned values. Note that variable ordering constraints do no impose an order on how the
constraints are solved but only how variables are assigned random values (see description for
example 10.4 in section 10.1.1).
An example of an ordering constraint is shown in the following program:

'Program 10.13: Ordering constraii,t operator


1 class payload;
2 rand bit vi;
3 rand int unsigned v2;
4 constraint c1 {vi == 1'bO -> v2 == a;}
5 constraint c2 {solve Vi before v2;}
6 endclass

In the above example, the reachable set for variables V1 and V2 is given by set {(O,O),
If both V1 and V2 are assigned a member from this set with equal probability,
(1,O), ... (1,2 32 _1)}.
then the probability of setting V1 to 0 is 1fe2+1) which will almost never occur. By adding con-
straint C2 (line 5), variable Vi is assigned a value first from the possible values in the reach-
able set of V1'V2' This means that both values 0 and 1 will be assigned to Vi with equal
probability. After V1 is assigned, then V2 is assigned a value from the newly computed reach-
able set for V2' This means that if Vi is set to 0, then the reachable set for V2 is {O}, and if Vi is
assigned a 1, then the reachable set for V2 is {O,1 •... 232 _1}. In either case, a value is selected
from the reachable set for V2 with equal probability.
The following properties apply to ordering constraints:
o Only variables marked with rand can be used in an ordering constraint.

• Variables marked with rande are not allowed in ordering constraints. As mentioned
earlier, these variables are always assigned before any variable marked with ralld.
o Explicit ordering constraints, along with other implicit ordering constraints should

not lead to circular dependencies (e.g., solve a before b, solve b before c. solw c
before a is not allowed).
Constraint Guards 255

10.3.8 Function-Call Constraints


Constraint expressions containing a function call impose an explicit constraint-based order-
ing requirement. This means that the constraints for any random variables passed as argu-
ments to such a function are solved first and assigned random values before the constraint
containing the function call is solved. Consequently, all variables passed to a function call in
a constraint are treated as state variables whose values are assumed to be fixed.
The following guidelines apply when using function calls in constraint expressions:
• Arguments passed to functions used in a constraint expression cannot be an output of
type ref(const refis allowed).
• Functions used in constraint expressions should not contain any state information
(i.e., should include only automatic variables).
• Functions used in constraint expressions cannot modify the randomization process
(e.g., by calling rand_modeO or constrainCmodeO).
• Functions used in constraint expressions may be called multiple times. This is one
reason that such functions should not have state information, as all calls to this func-
tion must behave exactly the same.

10.4 Constraint Guards


When evaluating a constraint expression, it is possible that variables used in this expression
may not have valid settings. For example, in writing a global constraint which uses object
pointers to specify a relationship between mUltiple objects, it is possible that an object
pointer is set to null and as such accessing a member of that object may lead to a runtime
errors. As another example, consider an iterative constraint that specifies that each array
member should be larger than the next array member. Obviously, for the last member of this
array, no next member exists and as such, accessing that variable will lead to a runtime error.
In such cases, it is, therefore, necessary to predicate the constraint expression with an
expression that provides conditions which must be satisfied before that constraint expression
is included in the randomization process.
Constraint Guards are expressions included in a constraint block that protect constraints
from such runtime errors. Constraint guards are sub-expressions written as part of the
expression in a constraint block and are only differentiated from other sub expressions in that
they include only the following items:
• Constants
• State variables
• Object pointer comparisons
• Loop variables in iterative constraints
• Array size in iterative constraints
Consider the following example:
256 Constrained Random Generation

iprogram 10.14: Constraint guards


1 module top;
2 class payload;
3 rand bit data [);
4 rand int unsigned length;
5 rand int unsigned tlength;
6 constraint c1 { length < 10;}
7 constraint c2 {data.sizeO == length;}
8 end class
9
10 class packet;
11 rand payload pi I];
12 int unsigned length;
13 rand int unsigned tlength;
14 constraint c1 {foreach (pl[i)) (I == OJ-> (pl[i].t1ength == pl[lj.length);}
15 constraint c2 {foreach (pl[l]) (i>O)-> (pl[ij.tlength == pl[i-1j.tlength + pl[ij.length);}
16 constraint c3 {foreach (pl[i]) (i == length)-> (tlength == pl[il.tlength);}
17 constraint c4. {foreach (pl[i]) (I > length)-> (pl[il.length == OJ;}
18
19 function newO;
20 pi =new[6];
21 for (int i = 0; i < 6; i++) pl[il = newO;
;22 endfunction
23 endclass
24
25 initial begin
26 packet p;
27 P =newO;
28 p.length = 3;
29 assert(p.randomize(»;
30 end
31 end module

This example shows the implementation of class packet composed of an array of payload
objects. Field length of payload specifies the size of its data payload, and field tlength of pay-
load gives the total length of all payloads in its containing packet before and including this
payload. Field length of packet specifies the number of payloads that are contained in packet
(active payloads), and the field tlength of packet gives the total length of all active payloads.
Note that field length of class packet is not a random variable.
Constraint blocks in packet class declaration (lines 14-17) provide the relationships that
must be maintained between class object fields during randomization. Constraint C1 specifies
that for the first payload in dynamic array pi, the field tlength should be equal to field length.
Constraint block C2 specifies that for payloads after the first payload, the tlength field is com-
puted by adding the length field for that payload, and the tlength field for the previous pay-
load. Constraint Ca specifies that the tlength field of packet should be set to the tlength field of
the last active payload. Constraint C4 specifies that the payload size for all inactive payloads
should be set to O.
Each of these constraints contain a guard expression. For constraint Ch C2, Ca, and C4, the
guard expressions are (i==O), (1)0), (i==length), and (1)length) respectively. Note that these guard
expressions are composed of either loop variables, constants, or state variables (i.e., length).
Controlling Constrained Randomization 257

10.5 Controlling Constrained ---_


Randomization
..-----------._.__ _----_
--------------------- ---_.--.,,_ .. -"" .. .. .....

The generic model of a random object, as described in its class declaration, is not applicable
to all randomization uses of that object. The randomization utility should provide constructs
that allow for modification of the default randomization assumptions as defined in a class
declaration. These constructs should handle the following requirements:
• It may be needed to disable or enable a constraint block for a specific use of a ran-
domized object.
• It may be needed to prevent the randomization of a variable already marked with
rand or randc.
• Building a hierarchical random object requires that the object hierarchy be prepared
for randomization before randomization starts, and final steps carried out after ran-
domization takes place. Hook methods should be provided for programming these
steps as part of randomization.
These topics are discussed in the following subsections.

10.5.1 Controlling Constraints


Constraint blocks can be activated or deactivated during program runtime. Turning on or off
a constraint block marked as static affects that constraint block for all instances of that
object. Turning on or off a constraint block not marked as static affects only the given
instance of the object for which that constraint block was disabled.
SystemVerilog provides a task and a function for controlling constraints. The syntax is
as follows:
task object[.constrainUdentifier]::constraint_mode(bit on_off);
function int object.constrainUdentifier::constrainCmodeO;

The task is used to change the active state of a constraint block. The function is used to
query the current status of a constraint block. The use of these methods is shown in the fol-
lowing example:

I Program 10.15: Constraint block activation and deactivation


1 module top;
2 typedef enum {short, average, tall} box_type;
3 typedef enum {valid, incorrect_area} erroUype;
4 class box;
5 rand box_type bt;
6 rand error_type et;
7 rand int unsigned width;
8 rand int unsigned height;
9 rand int unsigned area;
10 static constraint valid_area { (et ==
valid) ==
(area ==
width' height);}
11 constraint short_box {bt ==
short -> height < 10;}
12 end class
13
14 initial begin
15 int result; box b1. b2;
16 b1 = newO; b2 = new();
17 result = b1.valid_area.constraint_mode(); II result 1;=
18 =
result b2.valid_area.constraint_modeO; /I result 1; =
19 b1.valid_area.constraint_mode(O); /I turns off valid_area in b1
258 Constrained Random Generation

20 =
result b1.valid area.constraint modeO; /I result = 0;
21 =
result b2.valid- area.constraint-modeO: /I result = 0;
22 b1.shorl_box.constrainCmode(0): /I turn off short_box in b1
23 result = b1.short_box.constraint_modeO; /I result = 0;
24 result = b2.short_box.constraint_mode(); /I result = 1;
25 b1.constraint_mode(1 ); /I turn on all constraints in b 1
26 =
result b1.short_box.constraint_modeO; =
/I result 1;
27 result = b1.valid_area.constraint_mode(); =
/I result 1;
28 result = b2.valid_area.constrainCmodeO; 1/ result = 1;
29 end
30 end module

The initial active state of constraint block valid_area for both fields b 1 and b2 of box
object is printed on lines 17 and 18, respectively. Note that valid_area is marked as a static
constraint. The active state of this constraint block is set to off for field b 1 on line 19. This
change will affect not only instance b1, but also instance b 2, as shown on lines 20 and 21. On
line 22, constraint block short_boX of b1 is set to off. This change, however, does not affect
instance b 2 since this constraint block is not marked as static. All constraint blocks of
instance b 1 are set to active state on line 25. Note that this change also affects constraint
block valid_state which is a static constraint. Therefore the statement on line 25 also affects
instance b 2 • This is shown on lines 26-28.

10.5.2 Disabling Random Variables


All variables marked with rand or randc are initially active. This means that if the object
containing these variables is randomized, these variables are also randomized by default.
These random variables can be activated or deactivated during program runtime. A disabled
random variable is treated as a state variable during the randomization process.
System Verilog provides a task and a function for controlling this feature. The syntax is
as follows:
task object[. random_variable I: :rand _ mode(bit on_off):
function int object.random_variable::rand_modeO;

Task rand_fflodeO is used to change the active state of a random variable. Function
rand_modeO is used to query the current status of a random variable. The following proper-
ties apply to this usage:
• For unpacked array variables, a single array element or the entire array can be used
with these methods. Using an index limits the method to a single element ofthe array.
Omitting the index applies the method to all elements of the array.
• For unpacked structure variables, individual members of the structure can be used
with these methods. Specifying a member limits the method to that member. Omit-
ting any member name applies the method to all elements of the structure.
• A compiler error will be generated if the specified variable is not marked with either
rand or randc.
• The function fonn of rand_11IodeO does not accept arrays. As such, if the random
\'ariable is an unpacked array, then a member of that array should be specified when
using this function.
Consider the following initial block in combination \\ith the object declaration in pro-
gram example 10.15:
Controlling Constrained Randomization 259

Iprogram 10.16: Random variable activation and deactivation


1 module top;
2 initial begin
3 int result;
4 box b1;
5 =
b1 newO;
6 result = b1.bt.rand_modeO; /I result = 1;
7 result = b1.et.rand_modeO; /I result = 1;
8 b1.rand_mode(0); /I turns off all random variables in b1
9 =
result b1.bt.rand_modeO; /I result = 0; -
10 result = b1.et.rand_modeO; =
/I result 0;
11 b1.bt.rand_mode(1); /I turns off valid area constraint in b1
12 =
result b1.bt.rand_modeO; =
/I result 1; -
13 result =b1.et.rand_modeO; /I result =0;
14 end
15 endmodule

The initial active state for random variables bt and et are printed on lines 6 and 7. The
statement on line 8 deactivates all random variables in instance b 1. Lines 9 and 10 show that
the active states for bt and et are changed to off. The statement on line 11 activates random
variable bt. The result of this activation is shown on lines 12 and 13 to only affect random
variable bt.

10.5.3 Randomization Flow Methods


Randomizing an object often requires pre-processing and post-processing operations to be
performed on the randomized object. These operations can be performed as part of the pro-
gram that calls the randomize() function to randomize an object. This approach, however,
has two drawbacks. First, the steps that must be performed are usually the same regardless of
where the object is randomized, so repeating these steps every time such randomization is
needed leads to reduced productivity. Second, performing these steps requires detailed
knowledge of the internal structure of a randomized object. This intimate knowledge may
not be available if the randomized object is a part of a package developed separate from the
program using that randomized object. As such, it is best to have a mechanism to include
such pre- and post-processing steps as part of the object.
SystemVeriiog defines predefined functionspre_randomizeO andposCrandomizeO to
facilitate this objective when dealing with class objects. Each class object has the following
default declarations for these functions:

IProgram 10.17: default declaration of pre_randomizeO and post_randomizeO


1 function void pre_randomizeO;
2 if{ super) super.pre_randomizeO;
3 endfunction
4
5 function void post_randomizeO;
6 if{ super) super.post_randomizeO;
7 endfunction

The following pseudo-codes describe two functions that are called before the random-
ization is started, and after randomization is completed:
260 Constrained Random Generation

Iprogram 10.18: hierarchical call order to pre_randomizeO and post_randomizeO


1 pre_randomize_prepare(obLptr) {
2 foreach member mem_obj of obLptr that is an object ptr and an active rand variable {
3 pre_randomize_prepare(mem_obj);
4 }
5 obLptr.pre_randomizeO;
6 }
7 posUandomize_wrapup(obLptr) {
8 foreach member mem_obj of obLptr that is an object ptr and an active rand variable {
9 post_randomize_wrapup(mem_obj);
10 }
11 obLptr.post_randomizeO;
12

Note that these functions are only meant to provide an overview of the order of execu-
tion for hierarchical objects and actual implementation will be different from what is shown
above. As shown in the pseudo-codes, the pre_randomzieO and posCrandomizeO functions
are called in a depth-first order where the function for each object is called before its parent
function is called.
A typical use ofpreJandomize() is in preparing an object containing a dynamic array
of class objects for randomization. In this case, randomizing each array member requires that
the array position already points at an object (section 10.2.2). The randomization problem
with this setup is that each member of a such a dynamic array should be allocated explicitly
before it can be randomized. This creates a conflict, since the size of the array is not known
before randomization and, therefore, it is not clear how to allocate the dynamic array before
randomization starts. The strategy is to assume a maximum size for the dynamic array, ini-
tialize the dynamic array size to this maximum size in function pre_randomize() and then
constrain the size of dynamic array to the randomly generated size that is less than the maxi-
mum size, so that after the array is resized during randomization, all its members point to
allocated objects that will be randomized.
A typical use of posCrandomizeO function is in computing packet properties based on
the randomly generated values. Checksum fields are one such field that must be computed
after randomization is completed.
The following example shows the implementation of these behaviors using the random-
ization methods.
I
Program 10.19: Example of using pre_randomize() and post_randomizeO
1 class payload; bit pi; endclass
2 class packet;
3 rand payload packeCpayload [];
4 rand byte size;
5 =
int max size 100;
6 byte erc;
7 constraint cO {size> 0 && size <= max_size;}
8 constraint c1 {packet_payload.size() ==
size;}
9
10 function void pre_randomizeO;
11 =
packet_payload new[max_size];
12 =
for (int i 0; i < max_size; i++) packet_payload[i] =new:
13 endfunction
14
15 function void post_randomize();
16 crc = 0;
Random Stability 261

17 for (Int I'" 0; I < paeket_payload.slzeO; 1++) ere += paeket_payload[I].pl;


18 endfunetion
19 : endelass
~--------------------
In this example, dynamic array packet--'payload is initially sized to max_size in
preJandomizeO (line 1) and a new object allocated for each array position (line 12). The
array size is constrained to be less than max_size (lines 7, 8). During randomization,
paeketJ)ayload is resized to its new randomly decided size and since each position of this
array already points to an allocated object, the content of each object is also randomized.
After randomization is completed, function pose randomizeO is used to update the ere field
of the packet.

10.6 Random Stability


------~------------------------------------------

One of the most important requirements for a randomized program is that it should predict-
ably generate the same sequence of random values across multiple runs of the same program
and using the same random seed value. If this property is not maintained, then such a pro-
gram becomes impossible to debug, and impossible to quantify as a contributor to the overall
verification progress. In addition. to this requirement, it is also desirable that incremental
changes in a program do not drastically modify its random behavior. Random stability refers
to this property of a randomized program and steps taken to minimize variations in program
behavior because of localized changes to the program.
Execution of a SystemVerilog program consists of concurrently running threads of exe-
cution, operating on multiple instances of class objects. System Veri log takes advantage of
this architecture for minimizing the effects of incremental program changes on random
behavior of the program by localizing random generation to each thread of execution and
each instance of class objects. In this approach, the sequence of generated random numbers
in each thread or each object is controlled by only its initial seed value. This means that the
creation of new threads or instantiating new objects has minimal effect on the random behav-
ior of previously existing threads and instances. In SystemVeri log, Random Stability refers
to this property.
Each time a new thread or object is created, a new random number generator context is
created and assigned to the newly created thread or object. The seed for this new randomiza-
tion context is set from the next available random number from the thread that created that
new thread or object. Given this approach, random stability in SystemVerilog is discussed in
terms of the following properties:
• Thread stability
• Object stability
Thread stabilitv is achieved by creating a new random generation context for each
newly created thread. This behavior is shown in the following example:

'Program 10.20: SystemVerllog thread random stability


1 module top;
2 /linitial $display($urandom);
262 Constrained Random Generation

3 initial begin
4 int x, y, z:
5 I/$display ($urandom):
6 fork
7 begin $display ($urandom,,$urandom): end
8 begin $display ($urandom,,$urandom): end
9 begin $display ($urandom,,$urandom); end
10 begin $display ($urandom,,$urandom): end
11 begin $display ($urandom,,$urandom): end
12 join
13 end
14 endmodule

In the above example, the initial block is assigned a new random generation context
when it is created. Also, each branch of the fork statement is assigned a new random genera-
tion context when that thread is created, where the seed for each new thread is taken from the
next available random number from its parent thread (i.e" the thread for the initial block).
Note that the seed assigned to each sub-thread of the fork is independent of the order of exe-
cution of these sub-threads. This is because the order of execution is independent ofthe order
.of creating these threads; order of thread creation follows program order. Because of the
properties described, the above program example generates the same random values for
every execution of this program.
Random stability of above program segment can be affected in two ways. First, if the
display statement on line 5 is un-commented, then the seed for each thread started by the fork
statement becomes different and as such, the program will generate a completely different set
of values for each of the display statements. Second, if the initial block on line 2 is un-com-
mented, then the seed value assigned to the initial block on line 3 will become different,
therefore causing the program to display completely different random values,
The solution to above problems is to self-seed each thread if the values to be generated
by that thread are important to remain the same when the thread is moved or code leading to
start of a new thread is modified. This modification is shown in the following program seg-
ment.

rp;:Qgram 10,21: Self-seeding threads to improve thread random stability


1 module top;
2 I/initial $display($urandom):
3 initial begin
4 process::self,srandom(100):
5 I/$display ($urandom):
6 fork
7 begin process::self,srandom(1): $display ($urandom,,$urandom); end
8 begin process::self,srandom(2): $display ($urandom,,$urandom): end
9 begin process::self,srandom(3): $display ($urandom,,$urandom): end
10 begin process::self.srandom(4): $display ($urandom,,$urandom): end
11 begin process::self,srandom(5): $display ($urandom,,$urandom): end
12 ~~
13 end
14 endmodule

In the above program segment. the initial block on line J is self-seeded on line 4, and
each thread started by the fork statement is also self-seeded with the first statement of that
thread. In this new program, un-commenting lines 2 and 5 will not change the values pro-
Random Stability 263

duced by the program. As such, this modified program using self-seeded threads exhibits full
thread stability.

Object Stability is achieved by creating a new random generation context for each
newly created object. This behavior is shown in the following exampl~:

'Program 10.22: SystemVeriiog object random stability


1 module top;
2 class packet;
3 rand byte payload;
4 endclass
5
6 lIinitial $display($urandom);
7 initial begin
B packet p:
9 lI$display($urandom);
10 p = newO:
11 assert(p.randomize());
12 end
13 end module

In the above program, the initial block on line6 is assigned a seed value when it is cre-
ated. In addition, object p is assigned a new random generation context and initialized with a
seed. This seed is the next available random number in the context of the initial block. The
random value assigned to p.payload is dependent on the seed assigned to the context of object
p.

The above program will generate the same value for p.payload for consecutive runs of
the same program. Object stability of this program will, however, be disturbed if either lines
6 or 9 are un-commented. Un-commenting line 6 will change the seed value set for the initial
block, therefore changing the seed value for object p. Un-commenting line 9 will result in a
different seed value for object p. In both cases, a different value will be generated for p.pay-
load.

The solution to the above problem is to self-seed the object when it is created. The
updated program is shown below.

'Program 10.23: Self-seeding threads to improve object random stability


1 module top;
2 class packet;
3 rand byte payload;
4 function new(int seed);
5 this.srandom(seed);
6 endfunction
7 end class
B
9 lIinitial $display($urandom);
10 initial begin
11 packet p;
12 lI$display($urandom);
13 =
p new(10);
14 assert(p.randomize());
15 end
16 : endmodule
L-
264 Constrained Random Generation

The above program will produce the same result for p.payload even if lines 9 or 12 are
un-commented.
It is not always practical or desired to self-seed every thread or every object. A good
policy to follow is to add new threads to the end of a program so that seeds assigned to the
existing threads do not change. This practice can significantly contribute to random stability.

10. 7 System Functions


-----------------------------------

SystemVerilog provides system functions and tasks for random number generation and con-
trol. These functions are described in the following subsections.

10.7.1 $urandom
The prototype for this function is:
function int unsigned $urandom [(int seed)l;
This function returns a new 32-bit unsigned random number every time it is called. The
seed argument is optional and determines the sequence of generated random numbers. This
function returns the same sequence of numbers when the same seed is used.
The $urandom function is different from the $random system function in that $uran-
dom returns unsigned integers and it is automatically thread stable.

10.7.2 $urandom_rangeO
The prototype for this function is:
function int unsigned $urandom_range(int unsigned maxval. int unsigned minval=O);
This function returns a new 32-bit unsigned random number within the specified range
of minval and maxval every time it is called. The minval argument can be omitted and defaults
to O. The maxval argument can be less than minval in which case, the program automatically
reverses the two argument. $urandom_rangeO function is automaticai1y thread stable.

10.7.3 srandomO
The prototype for this function is:
function void srandom(int seed)
This function initializes an object or thread's random number generator with the pro-
vided seed value. The following example shows the use of this function for a thread and for
an object.

I Program10.24: Use of srandom() function


1 module top;
2 class packet;
System Functions 265

3 rand bit data [J;


4 rand byte size;
5 constraint c1 {data.sizeO == size:}
6 endcJass
7
8 initial begin
9 int locaLrand;
10 packet p;
11 p = new();
12 process: :self.srandom(1 0);
13 assert(randomize(local_rand»;
14 p.srandom( 100);
15 assert(p.randomizeO);
16 end
17 : endmodule
~------------------

In this example, the seed for random number generator of the initial thread is initialize
on line 13. This seed affects random values that are generated in the context of this thread.
The randomization of local scope variable local_rand (line 14) is affected by the seed set on
line 13. The statement on line 15 initializes the seed for the random number generator in the
packet object. The randomization of packet p (line 16) is affected by the seed set on line 15.
266 Constrained Random Generation
CHAPTER 11 Data Modeling

Modem verification environments deal with data at different layers of abstractions. At the
lowest level, bits and bytes are assigned and read from physical wires in the environment. At
the highest level, objects encapsulating bundled values are routinely generated, manipulated,
and discarded throughout the environment. Dealing with data at transaction level leads to a
more productive and modular program, and also allows for operations related to that data
type to be packaged with its abstraction, thereby reducing the low-level complexity that has
to be managed.
Data can be created, duplicated, and discarded during simulation runtime. And it can, as
it usually does, travel from object to object. These common behaviors across data objects
suggests that a structured approach for creating and managing complex data objects can lead
to benefits in productivity and reducing the potential for mistakes in data manipulations.
Data in a verification environment can be put into two broad categories:
• Composite data objects
• Command and status (or actions and reactions)
Composite data objects represent a collection of interrelated data values that usually
represent objects in a standard or proprietary protocol (i.e., an Ethernet packet). Commands
are used to instruct a module to perform a certain activity (e.g., instructing a memory driver
to read a memory location). Status is used to provide feedback to the object issuing a com-
mand. Note that as with data packets, commands and status are generated, used, discarded,
and must travel (e.g., command traveling from a sequencer to a driver, or status moving from
monitor where it is observed to the scoreboard). As such, it is natural to model commands
and status similar to data that represents packets and signal values.
Ultimately, all abstract data types (i.e., command, status, or data objects) that must
travel through a DUV at the register transfer level must be transformed to its physical form.
For example, a data object representing an Ethernet packet should be translated into a bit
stream to be sent into a DUV port or a bit stream collected from an Ethemet port must be
translated into an abstract representation of an Ethernet packet. Packing and unpacking tech-
niques are used to control translation between abstract and physical forms.
268 Data Modeling

This chapter introduces a general view of data modeling and discusses issues related to
generation, and its related manipUlations.

11.1 Data Models in System Verilog


Data models are. more than just a container of interrelated data values. In fact, the main goal
in creating a data model is to provide an atomic object to the outside world where the data
object can be manipulated at an abstract level without intimate knowledge of its internals.
Some benefits for this approach are:
• Including data values and applicable operations in a data model inherently leads to a
reusable object that can be used in any project operating on such data type.
• Any implementation bug related to that data model is confined to its definition which
helps in debugging.
• Not all verification engineers dealing with this data type may have knowledge of its
internals and may only need to use it and manipulate it as it travels through their ver-
ification jurisdiction.
Hiding the internal complexity of a data model requires that commonly defined opera-
tions associated with an abstract data type be packaged with the data model. As such, all
composite data types should be modeled using the SystemVerilog class construct. The fol-
lowing properties of class construct motivate the need for this requirement:
• Only class-based objects can include constraint blocks.
• Only class-based objects can have a subset of its members defined as random.
• Classes can have properties and methods allowing for data behavior to be embedded
in the data object by using class methods.
• Class-based objects can be created and discarded during the simulation runtime.
• Class-based objects are inherently accessed by pointers (i.e., passing objects in task
and function calls), and as such, data movement is very efficient.
It maybe tempting to use the struct construct to model a composite data object. The fol-
lowing limitations of this construct, however, discourage such usage:
• Struct-based objects are copied by value and not by reference unless re!qualifier is
specifically used when passing it as a task or function argument.
• Struct-based objects can only be randomized as a whole.
• Methods cannot be embedded in a struct-based object.
The following program shows an example of a simple data packet modeled as a class:

IProgram 11.1: Data packet modeled as a class object


1 module topO;
2 class my-packet;
3 rand bit [3:0] field1;
4 rand bit [3:0] field2;
5 rand bit [3:01 field3;
6 constraint c1 {field1==field2 • fleld3;}
7 endclass
8
Data Model Fields and Constraints 269

9 initial begin
10 my-packet mp;
11 mp = new;
12 for(int i=O; i<10; i++)
13 assert(mp.randomizeO with {field1 ==1 O;});
14 end
15 end module

In the above example, the constraint solver assigns values to fleld 2 and field 3 in order to
satisfy the constraints specified on lines 6 and 13.

11.2- - -Data
-----.
Model Fields and Constraints
----------------- - - -

The abstraction implemented in a data model is achieved through its set of fields (i.e., prop-
erties) and constraints describing relationships between these fields. These topics are
described in the following subsections.

11.2.1 Data Model Fields


Data model abstractions consist of two types of fields:
• Physical fields
• Virtual fields
Physical fields contain values that represent the content of the abstract data object and
are present in the physical form of the data object (i.e., source address in an Ethernet packet).
Virtual fields contain either instructions for how the packet should be generated or status
fields indicating conditions detected when extracting a packet from the physical environment
(i.e., crc status in an Ethernet packet). Virtual fields may also be used to hold array sizes
inside the data model.
Physical fields follow closely the description of an abstract data type. For example, the
physical fields in an Ethernet packet model correspond directly to the fields defined in an
Ethernet packet. A data model that represents only the content of a packet contains only
physical fields. Virtual fields, however, are included based on the operations defined for a
given data model. For example, an Ethernet packet must have a field indicating whether or
not it holds a valid crc. In a generator, this field is used to control the random generation of
an Ethernet packet so that an incorrect crc is generated only when this flag is set. In a moni-
tor and during collection, this field indicates whether or not a crc error was detected when a
packet was collected from a DUV port. Virtual fields are also used to carry instructions about
how a packet should be handled as it travels through the verification environment. For exam-
ple, an Ethernet packet may contain a virtual field indicating the number of idle clocks
before the packet preamble is started. During transmission, this field is used by the driver to
control when this packet is sent. During receive, this field is updated by the driver to indicate
how many idle cycles were detected on the channel before this packet was recei\·ed.
270 Data Modeling

Figure 11.1 shows the structure of an Ethernet packet. In keeping with the general
guidelines of hiding data model details, a model of an Ethernet packet should include the fol-
lowing features and utility methods:
• Physical fields for:
• Source address
• Destination address
• Size
• Payload
• crc
• Virtual fields used during generation:
• Generate short, valid size, or long packet
• Generate valid or invalid crc
• Payload size to be generated (derived from valid)
• Virtual fields set during packet collection from DUT, indicating:
• If the collected packet was short, valid size, or long
• Ifthe collected packet had a valid or invalid crc when it was collected
• Whether a collision was detected when receiving this packet
• Payload size of the collected packet
• Utility methods to:
• Unpack a bit array into the packet structure
• Pack the physical fields and return a bit array
• Methods to copy, print, compare, record, and clone packets
• Random Generation facilities to allow for randomly setting the packet fields
Note that a data object is either generated and transmitted or is received from the DUV,
and as such, it is common practice to use the same virtual fields for both generation and col-
lection (i.e., use valid_ere to indicate if a valid ere should be generated during generation, and
to set the same field to true or false when the packet is received from the DUV).

SIZED

Figure 11.1 The Base Ethernet Packet Format

The following program shows a typical implementation for the payload portion of an
Ethernet data packet, showing only the fields.

'Program 11.2: Payload Implementation of an Ethernet packet


1 typedef enum bit {FALSE, TRUE} bool;
2 typedef enum {TOO_SHORT, SHORT, MEDIUM, LONG, TOO_LONG} ethJength_t;
3
4 class etherneCframe_sized_payload;
5 rand boollegaLsize;
6 rand ethJength_t Itype;
7 rand bit [15:0] ta9-'nfo;
8 rand inl unsigned data_size;
9 rand byte dala [];
10 endclass
Data Model Fields and Constraints 271

The physical fields in this implementation are tag_info and data. The virtual fields are
legal_size, Itype,and data_size. These three virtual fields are included in order to provide the
ability to control the size of generated payload at different levels of granularity. Field
legal_size is used to specify if the payload size should be legal or not, field Itype is used to
specify payload size in general terms as indicated by enum on line 2, and field data_size is
used to specify exact size of payload that should be generated.
The real challenge in implementing this data model is that all virtual and physical fields
must be set to their appropriate values when any of the other virtual of physical fields are
constrained using an in-line constraint when the object is being randomized. For example, if
data_size is set to 20 as a constraint when this data model is generated, then legal_size should
be set to FALSE, and Itype should be set to TOO_SHORT by the random generator. Alterna-
tively, if legal_size is constrained to FALSE during generation, then Itype should be assigned
one of TOO_SHORT or TOO_LONG by the random generator and data_size should match the
range defined by the setting of Itype. This goal is achieved through appropriate inclusion of
constraints in this data model. Data model constraints are described in the following section.

11.2.2 Data Model Constraints


Ideally, the abstraction in a data model should be implemented only through constraint
blocks that describe the relationship between its fields. This means that procedural assign-
ment statements should be avoided as much as possible when assigning values to the data
object, and all such assignments should be left to the randomization engine. The approach
that should be avoided is a flow where a series of alternating value assignments followed by
randomization are used to assign values to the data object. This guideline provides two bene-
fits:
• The random generation engine can provide more uniform random behavior when
considering all data fields at the same time.
• A procedural programming approach imposes an order where some data fields must
be set before other fields can be assigned. No such ordering requirement exists when
randomization constraints are used to assign values to data fields, since the constraint
solver considers all fields at the same time. This means that in a fully con-
straint-based implementation of a data model, any field can be constrained knowing
that other fields will be assigned values in the expected range.
The real challenge in specifying the constraints included in the implementation of a data
model is in making sure that all fields are assigned valid values for the initial set of con-
straints, no matter which fields are constrained using in-line constraints when an object is
being randomized.
The following program shows program 11.2 completed to include constraint blocks that
implement target features described for that example:

'Program 11.3: Using randomization constraints for computing data model fields
1 typedef enum bit {FALSE, TRUE} bool;
2 typedef en urn {TOO_SHORT, SHORT, MEDIUM, LONG. TOO_LONG} ethJength_t;
3
4 class ethernet_frame_sized_payload;
5 rand boollegaLsize;
272 Data Modeling

6 rand eth_length_t Itype;


7 constraint c1 {(legal_size) == (Itype inside {SHORT. MEDIUM, LONG});}
8
9 rand bit [15:0] lagJnfo;
10 constraint c2 {tagjnfo <= 1536;}
11
12 rand int unsigned data_size;
13 constraint c3 {(legal_size) == (data_size == tagjnfo);}
14 constraint c4 {(ltype==TOO_SHORT) -> (data_size inside ([i :45j});}
15 constraint c5 {(ltype==SHORT) -> (data_size inside ([46:800]});}
16 constraint c6 {(Itype==MEDIUM) -> (data_size inside {[801: i200]});}
17 constraint c7 {(Itype==LONG) -> (data_size inside ([120i : 1536]});}
18 constraint c8 {(Itype==TOO_LONG) -> (data_size inside {[1536:2000]});}
19
20 rand byte data (];
21 constraint c9 {data.size() == data_size;}
22 endclass

Constraint C1 uses an equivalence constraint instead of an implication constraint. An


equivalence constraint is used to represent mutual implication between two Boolean condi-
tions. This relationship is shown in the following example:
constraint impLconstr {(A) -> (B) && !(A) -> I(B);};
constraint equiv_constr {(A) == (B):};

In this example, A and B are Boolean conditions and constraints impl_constr and
equiv_constr represent the same constraint between A and B. As such, constraint block C1 in
the above example, requires that if virtual field legal_size is set to TRUE, then Itype should be
assigned to either SHORT, MEDIUM, or LONG, and vice versa (this is a bidirectional constraint).
It also requires that if virtual field legal_size is set to FALSE, then Itype is not set to SHORT,
MEDIUM, or LONG, which means Itype should be set to either TOO_SHORT or TOO_LONG. Note
that because of using an equivalence constraint, the value assigned to legal_size will always
reflect the value assigned to Itype and vice versa. As such, if Itype is constrained during ran-
domization using an in-line constraint, then legal_size is automatically set to a value reflect-
ing the setting for the constrained value of Itype, and if legal_size is constrained during
randomization, then Itype is automatically set to a value reflecting the setting for the con-
strained value of legal_size. Constraints C4-CS specify valid ranges for each setting of Itype.
Note that, again, if data_size is constrained using an in-line constraint during randomization,
then these constraint blocks will result in !type being assigned a value appropriate to the set-
ting for data_size, which in combination with constraint block C1 will set appropriate value
for field legal_size.

In summary, the provided set of constraints will result in a behavior where any of the
virtual fields or physical fields can be constrained using an in-line constraint during random-
ization and all other data fields will be assigned appropriately. This is shown in the following
example:
I
Program 11.4: Data model randomization constraints based on any of its fields
1 module topO;
2 'i nelude "program _11.3"
3
4 ethernel_frame_sized_payload efsp;
5
6 initial begin
7 efsp = new();
8 assert(efsp.randomize() with {legal_size == FALSE;});
Hierarchical Models 273

9 assert(efsp.randomizeO with {Itype ==


TOO_SHORT;});
10 assert(efsp.randomizeO with {data_size ==
199;});
11 assert(efsp.randomizeO with {data_size ==
2100;});
12 end
13 endmodule

In this example, randomization on line 8 results in Itype to be generated as either


TOO_SHORT or TOO_LONG with data_size set to a random value in the corresponding range.
Randomization on line 9 results in legal_size set to FALSE with data_size set to a random value
in the [1 :45] range. Randomization on line 10 results in legal_size set to TRUE and Itype set to
SHORT. Randomization on line 11 will fail because of a contradiction.

11.3 Hierarchical Models


Some data models are naturally described as a hierarchy of smaller composite objects. It is,
therefore, a good strategy to define such models to mirror their description. As will be dis-
cussed in the next section, a hierarchical model is implemented as a hierarchy of class
objects where a class object has other class objects as its members.
The important consideration in creating a hierarchical model is that it will contain point-
ers to other class objects. Care should be taken that these lower level class objects are also
created when the top level object is created or before randomization is started.
The following program shows the hierarchical implementation of a SIZED Ethernet
packet. Even though using a hierarchy for just one subtype is not necessary, this example is
shown here so that the addition of other subtypes can be shown in the next section.

I Program11.5: Hierarchical model of an Ethernet packet


1 typedef enum {SIZED} eth_frame_t;
2
3 class ethernet sized frame;
4 rand eth=frame-='t ptype;
5 rand ethJength_t Itype;
6
7 rand bool legal_size;
8 rand boollegaLcrc;
9
10 rand bit [47:0] dest_addr;
11 rand bit [47:0] src_addr;
12
13 rand int unsigned data_size;
14
15 rand etherneUrame_sized_payload sized_payload;
16 constraint cO {(ptype ==
SIZED) ->
17 sized_payload. legal_size ==legal_size;
18 sized_payload.data_size ==
data_size;
19 sized_payload.ltype == Itype;}
20
21 bit [31 :0] crc;
22 constraint c6 {(Iegal_crc) == (crc == compute_crc(legal_size));}
23
24 function bit [31 :0] compute_crc(bool flag);
25 endfunction
26
274 Data Modeling

27 function void preJandomize():


28 if(sized_payload ==
nUll) sited_payload = newO;
29 endfunction
30 endclass

This example shows a hierarchical implementation where the Ethernet frame contains
the payload class defined in program 11.3. A hierarchical constraint block is used to specify
the relationship between the payload object and the class being defined here. Note that all
hierarchical constraints in this example are specified in the parent object. The reason for this
guideline is that class etherneUrame_sized_payload may potentially be used as a stand-alone
object as shown in example 11.4, but class etherneCsized_frame defined in this example can-
not be used without the payload object contained in it. The approach of placing all hierarchi-
cal constraints in the parent object allows the implementation of the lower level object to
remain independent of the parent object.
A constraint guard should be used when defining hierarchical constraints. In this exam-
ple, a constraint guard is not used since special code is put in place (line 28) to make sure that
sized_payload is never null when randomization is taking place.

In this example, ere is computed using a function call in a constraint block (line 22). The
reason field legal_size is passed to this function is that ere should be calculated only when all
other fields are assigned random values: Using a function call with legal_size as an argument
guarantees that ere is assigned only after legal_size is assigned a value which requires that all
other fields also be assigned values. An alternative approach would be to assign ere in the
poscrandomizeO function of this class.

11.4 Data Model Subtypes


Any reasonably complex data model describes subtypes for which fields and field values
may be different. It may seem intuitive to model such a data obj ect and its subtypes using a
base class containing common members and derived classes that contain specialized fields
for each subtype. This approach, however, is not well suited for modeling subtypes of a data
model. To better understand the reason for this shortcoming, consider the following proper-
ties of classes in System Verilog:
• SystemVerilog does not support multiple inheritance l . This means that a set ofmem-
bers and methods common to some but not all derived classes of a parent class must
be explicitly implemented in all derived classes that contain these members and
methods.
• Consider two derived classes Band C of a parent class A. In System Veri log, it is not
possible to randomize an object of type A to produce objects of type B or C. In fact,
the only way to create objects of either type Bore is to declare a pointer of that type
and allocate an object for that pointer.

I . .-\ language supporting multiple inheritance allo\\"s a derived class to inherit properties and methods
fwm more than one parent class.
Data Model Subtypes 275

• Class members defined using tagged unions cannot participate in constraint specifi-
cations.
The lack of support for multiple inheritance implies that if data subtypes are modeled as
derived classes of a parent class, then properties and methods that are present in most but not
all subtypes have to be explicitly and separately implemented for each subtype, including
them. This is not a scalable approach, since a change in any of these properties or methods
would mean that all relevant subtypes have to be modified to reflect the new change.
The inability to choose randomly between derived classes of a parent class implies that
if data subtypes are modeled as derived classes of a parent class, then creating a data object
requires advance knowledge of its SUbtype. Since randomization occurs after object creation,
then data subtype is fixed when data randomization takes place. This behavior leads to multi-
ple drawbacks in using derived classes for modeling data subtypes:
• If data object subtypes must be decided before object creation and randomization,
then they cannot be decided based on randomly generated values for other fields of
that data object.
• When creating data objects, its subtype must be explicitly decided even if data sub-
type is not a focus of verification.
• Subtype randomization cannot be packaged inside a class object. The reason is that
the subtype for a data object is already decided by the time randomization is taking
place, and therefore, any special requirements for generating different subtypes with
different probabilities should be implemented outside the class describing the data
model.
Given the limitations of using derived classes in modeling data subtypes, it is best to
avoid using derived classes and use one class to represent all subtypes. In this case, subtypes
can be modeled using either a flat or a hierarchical approach. In a flat approach, all fields for
all subtypes are explicitly included in the class declaration of the base model. In a hierarchi-
cal approach, fields specific to each subtype are modeled with an independent class declara-
tion, which is then instantiated inside the class declaration of the base model. A hierarchical
approach leads to a modular and structured model but it has the disadvantage that constraint
guards should be used to deal with potentially null object pointers. A flat modeling approach
has the advantage of not needing constraint guards but tends to become difficult to manage
for anything but the simplest models.
The following guidelines should be followed when modeling a sub-typed data model:
• Use one class definition to represent all subtypes of a data model.
• Include a virtual field having an enumerated type indicating the SUbtype.
• Define constraints that dictate which fields are affected by the subtype field.
• Implement subtype fields either as a flat or hierarchical model.
• Do not use tagged unions in data models.
Figure 11.2 shows two subtype definitions for an Ethernet packet. Class declaration for
etherneUrame_sized_payload is shown in program 11.3. The following program shows the
implementation for etherneUrame_qtagged_payload. The implementation of the QTAGGED
Ethernet packet subtype payload follows the same guidelines as the one described for the
SIZED Ethernet packet SUbtype.
276 Data Modeling

SIZED Ethernet Packet


48 48 16 46 to 1536 bytes

QTAGGED Ethernet Packet


48 48 16 46 to 1536 bytes

Figure 11.2 Ethernet Packet Subtypes

Iprogram 11.6: Qtagged subtype implementation of an Ethernet payload


1 class ethernet_frame_qtagged_payload;
2 rand boollegaLsize;
3 rand eth_length_t Itype;
4 constraint c1 {(legal_size) == (Itype inside (SHORT, MEDIUM, LONG});}
5
6 rand bit [15:0J tagJnfo;
7 constraint c2 {tagJnfo == 16'h8100;}
8
9 rand bit [3:0] user_priority;
10 rand bit CFI;
11 rand bit [11:0J VLAN_id;
12
13 rand bit [15:0] tagJength;
14
15 rand int unsigned data_size;
16 constraint c4 {(data_size == tag_length);}
17 constraint c5 {(ltype==TOO_SHORT) -> (data_size inside ([1 :41]});}
18 constraint c6 {(Itype==SHORT) -> (data_size inside ([42:800]});}
19 constraint c7 {(ltype==MEDIUM) -> (data_size inside ([801:1200]});}
20 constraint c8 {(Itype==LONG) -> (data_size inside ([1201:1532]});}
21 constraint c9 {(ltype==TOO_LONG) -> (data_size inside [[1533:2000]});}
22
23 rand byte datal];
24 constraint c10 {data.sizeO == data_size;}
25 endclass

Given class declarations for SIZED and QTAGGED Ethernet packet SUbtype payloads
shown in programs 11.3 and 11.6, respectively, Hierarchical Implementation of an Ethernet
packet is shown below.

Iprogram 11.7: Hierarchical implementation of an Ethernet packet


1 typedef enum {SIZED, QTAGGED} eth_frame_t;
2
3 class etherneCframe;
4 rand eth_frame_t ptype;
5 rand ethJength_t Itype;
6 rand boollegal_size;
7 rand boollegal_crc;
Data Model Views 277

8 rand bit [47:0] dest_addr;


9 rand bit [47:0] src_addr;
10 rand int unsigned data_size;
11
12 rand etherneUrame_sizedJ>ayload sized_payload;
13 constraint cO {(ptype == SIZED) ->
14 sized_payload. legal_size == legaLsize;
15 sized_payload.data_size == data_size;
16 sized_payload.ltype == Itype;}
17
18 rand etherneUrame_qtagged_payload qtagged_payload;
19 constraint c1 {(ptype == QTAGGED) ->
20 qtagged_payload.legal_size == legaLsize;
21 qtagged_payload.data_size == data_size;
22 qtagged_payload.ltype == Itype;}
23
24 bit [31 :0] crc;
25 constraint c6 {(Iegal_crc) == (crc == compute_crc(!egaLsize));}
26
27 function bit [31:0] compute_crc(bool flag);
28 I/compute crc here
29 endfunction
30
31 function void pre_randomizeO;
32 if(sized_payload == nUll) sized_payload = new();
33 if(qtagged_payload == nUll) qtagged_payload = newO;
34 endfunction
35
36 function void post_randomize();
37 if (ptype==SIZED) qtaggedJ>ayload= null;
38 if (ptype==QTAGGEO) sized_payload= null;
39 endfunction
40 endclass

In the above example, pre_randomizeO method of class ethernet_frame is used to make


sure that sized_payload and qtagged_payload pointers are not null. If memory consumption is
a concern, the object pointer that is not relevant after random assignment can be freed after
randomization is complete. This is done in posCrandomizeO where depending on the ran-
domly generated data subtype, the non-relevant object pointer is set to null resulting in its
allocated memory to be reclaimed by the garbage collection feature of SystemVerilog. Also
note that in this implementation, constraint guards are not used with object pointers since the
implementation guarantees that object pointers are never null during randomization.

11.5 Data Model Views


-_.._ - - - - - - - - - - . _ - - _ ....._--_ .._.._ _. _ - - _ . _ . - ----.

A data model should provide the following views:


• Method view
• Constraint view
The method viell· is used to access data object properties. A constraint view defines how
the object can be constrained during randomization. These interfaces are discussed in the fol-
lowing sections.
278 Data Modeling

11.5.1 Method View


Ideally, properties of a data model should only be accessed or lTIodified through well defined
methods. This can be achieved by using the local and private keywords when declaring a
class. This approach, however, is not practical when additional constraints must be defined
for randomizable fields in a data model.
Either functions or tasks can be used to provide a procedural interface to a data model.
It is best to use only functions to provide such an interface. The reason is that a data model is
used to hold contents of a composite data object and not store any information related to the
state of simulation. Functions return in zero simulation time and as such are the preferred
mode of interacting with a data object where no simulation time is expected to pass when
interacting with the data object.
The following guidelines should be followed when hiding data model contents:
• All rand and randc data fields should be public (not local or private).
• Fields used for internal states or flags should be marked as local or private.
• Only functions should be used in data models. If tasks must be used, they must be
non-time-consuming tasks.

11.5.2 Constraint View


To improve user interaction with a data model, generation constraints should also be
included for the following purposes:
• Defining default values for valid data generation
• Identifying valid and invalid variations of the data model that are useful for verifica-
tion, and providing constraint blocks to easily generate such variations
The following subsections discuss approaches for defining such constraint blocks.

11.5.2.1 Abstract Ranges


Data model properties are controlled through values assigned to their fields. Even though a
given field can potentially be assigned many different values, it is usually ranges of values
that are of interest for verification purposes. Abstract ranges refer to ranges of parameters
defined for a data model that signify a more abstract concept such as validity, comer cases, or
qualitative size measurements (e.g., large, small, etc.).
Abstract ranges provide an easy mechanism for constraining an object during random-
ization to within a range of interest. Abstract ranges are identified by an enum type variable.
Defining abstract ranges also simplifies the specification of non-contiguous ranges during
randomization.
In program 11.3, enumerated type eth_length_t is an abstract range where the exact value
of packet size is abstracted to concepts of illegal values (TOO_SHORT, TOO_LONG) and
sub-ranges within the \'alid range (SHORT, MEDIUM, LONG).
Data Model Views 279

11.5.2.2 Coordinated Ranges


Coordinated ranges are used to define interesting relationships between different fields of a
data model. Coordinated ranges are used as a quick means of providing constraints for com-
monly used combinations of parameters. The following example shows the definition of
coordinated ranges for an Ethernet packet:

I Program 11.8: Use of coordinated ranges in data modeling


1 typedef enum bit {FALSE, TRUE} bool;
2 typedef enum {TOO_SHORT, SHORT, MEDIUM, LONG, TOO_LONG} ethJength_t;
3 typedef enum {OTHER, SHORT_GOODCRC, LONG_BADCRC} eth_pkCgen_t;
4 class ethernet frame;
5 rand eth::::length_t Itype;
6 rand boollegal_crc;
7 rand eth_pkt_gen_t pg_type;
8 constraint c1 {(P9_1ype==SHORT_GOODCRC) == (Itype==SHORT && legal_crc);}
9 constraint c2 {(pg_type==LONG_BADCRC) == (Itype==LONG && !legaLcrc);}
10 end class

Only the beginning part of the implementation is shown in this example. Enumerated
type eth_pkCgen_t is defined as interesting combinations of packet length and crc value.
Constraint blocks C1 and C2 specify the relationship between coordinated range parameter
P9_type and values for Itype and legal_crc. Note that if pg_type is not constrained using an
in-line constraint during randomization, then its value will be assigned according to ran-
domly generated values for Itype and legal_crc, and as such can be used as a field to quickly
identify the specific coordinated range that was generated during randomization. Also note
that if the combination ofltype and legal_crc does not correspond to those in constraint blocks
C1 and C2, then pg_type will be assigned to OTHER since assigning it to any other value would
violate constraint blocks (;1 and C2, and OTHER is the only remaining possible value.

11.5.2.3 Default Ranges


A data model should by default be implemented to produce typical behavior. This means that
default ranges should be provided as part of data model implementation to produce such
basic behavior. It is, however, necessary to override such constraints when generating varia-
tions that contradict the typical behavior of the data object. SystemVerilog provides mecha-
nisms to override previously defined constraint blocks to achieve this target. This use model
is shown in the following example:

~rogram 11.9: Overriding default ranges for a data model


1 module topO
2 class ethernet_frame;

3 constraint default_setting {pg_type == SHORT_GOODCRC;}

4 end class
5
6 ethernet_frame e;
7
8 initial begin
9 e = new;
280 Data Modeling

10 assert(e.randomize());
11 e.default_setting.constraint_mode(O); IIturn off defaulCsetting constraint
12 assert(e.randomlzeO with {
13 legal_crc == FALSE;
14 ptype == SIZED;
15 });
16 end
17 endmodule

This example shows the Ethernet frame model with an additional constraint block (line
3) which is used to indicate that in the absence of any constraints, short packets with good
crc should be generated. However, this default behavior can be disabled using a
constrainC modeO fimction call (line 11) and the default behavior changed (lines 12-15).

'11.6 Transactions: Command and Status


Building randomized verification environments requires not only random data generation,
but also random sequence of activities. Traditionally, such activities were modeled with tasks
where a customized loop would generate the scenario of interest. However, modeling an
activity and its resulting effect as objects that can be created, transferred, and destroyed,
opens an entirely new range of possibilities for generating random sequences of actions in a
verification environment.
Modeling commands as data objects has the following advantages:
• A command can be randomly generated during simulation.
• A command can be transferred from one verification module to another (i.e from
sequencer to a driver).
• Data relevant to a command can be embedded in the command itself (i.e., command
to transmit the enclosed packet).
• A command object can be scoreboarded.
Random generation of a command is the enabling idea behind detaching scenario gener-
ation from a fixed programming loop that cannot be changed during the simulation process.
By modeling a command as an object that can be generated, a variety of factors can control
the next action that will take place in the verification environment. A command object can
also travel from one module to another, possibly in non-zero time where it is queued before
reaching its destination, enabling the separation of command generation from command exe-
cution. }\.dditionally, methods related to a command can be encapsulated inside the command
object, and a command object can be scoreboarded by inserting it inside a scoreboard.
Modeling status conditions as objects has similar advantages to modeling commands as
objects. Status objects are created when status is observed in the verification environment.
For example, a collision detected on an Ethernet connection can be packaged in a status
object indicating a collision. which can then be sent to a scoreboard that is expecting a colli-
sion condition to be detected.

The modeling of data and commands as objects is the fundamental requirement for
transaction level modeling ofacti,ity in a \'erification environment (chapter 9).
281

PART 5
Environment Implementation
and Scenario Generation
1:82
CHAPTER 12 Module-Based VE
Implementation

Two fundamentally different approaches can be used to implement a verification environ-


ment:
• Module-based approach
• Class-based approach

In a module-based implementation, each component in the verification environment is


implemented using a SystemVerilog module construct, the component hierarchy is modeled
using the module hierarchy, and connections between components modeled as ports or inter-
face components. Class-based implementation is a completely different approach where each
component in the verification environment is modeled as a class object, component states are
modeled as object fields and properties, and component behavior is implemented using tasks
and functions. In a class-based approach, the component hierarchy is implemented as a hier-
archy of objects defined using class declarations, with communication between components
modeled using transaction interfaces.
Either module-based or class-based implementation approaches can be used to imple-
ment a verification environment that follows the architectural guidelines of a modem verifi-
cation environment (chapter 3). A module-based approach has the advantage of being
familiar to engineers with experience in HDL-based programming. As such, a module-based
implementation approach can be quickly adopted and used by all members of a design and
verification team. A module-based approach, however, is not well suited to large-scale verifi-
cation projects whose success depends on flexibility, scalability, and reusability of the under-
lying verification environment. The main reason for this shortcoming is that, first, a
module-based implementation is a fixed implementation that cannot be changed after the
simulation runtime is started. In addition, the randomization features of the SystemVerilog
language are best used with class objects, and therefore, using modules to represent compo-
nents reduces the flexibility of environment randomization.

Following a class-based implementation approach has the additional advantage of


allowing developers to take full ad\'antage of object-oriented programming techniques (e.g.,
polymorphism. property hiding, object encapsulation). Benefiting from this advantage, how-
ever, requires good knO\dedge of object-oriented programming paradigms.
284 Module·Based VE Implementation

This chapter illustrates a module-based implementation approach for building a verifi-


cation environment. Section 12.1 provides a general understanding of a module-based imple-
mentation approach through an example of a small module-based implementation. Section
12.2 introduces the XBar design which will act as the DUV for the verification environment
described in the remainder of this chapter. The class-based implementation approach is dis-
clil'ssed in chapter 13 .

.! 2.1 Module-Based In!pl~mentation Overview


T4Itis section provides an overview of the module-based implementation approach by show-
ing a module-based implementation for a small example. The focus in providing this exam-
,pie is not on following correct methodology but on providing a clear overview of elements
involved in a module-based implementation. Class-based implementation of this example is
shown in chapter 13.1 and can be used to contrast between these two implementation
approaches.

Agent DUV

Sequencer Driver
I II I

0 0-
Monitor
I I
/\
elK I
Figure 12.1 D-FF Verification Environment

Consider the D-FF and its verification environment shown in figure 12.1. The imple-
mentation of this DUV is shown in the following program.
~-------------------
Program 12.1: Implementation of a OFF and its interface wrappers
1 module dff(input byte unsigned 0, input bit elk, output byte unsigned 0);
2 always @(posedge clk)
3 0<= D;
4 endmodule
5
6 interface dffjf(input bit clk);
7 byte unsigned 0;
8 byte unsigned a;
9 endinterface
10
11 module dfCwrapper(dffjf phyjf, input bit clk);
12 dff dffi(phy_if.D. clk. phy if. a);
13 : end module -
L-

The first step in building the verification environment for this component is to define an
interface block and define a wrapper that allows other components to interact with this DUV
Module-Based Implementation Overview 285

by using the interface block. In the context of a verification environment, this interface block
is considered the physical interface.
The verification environment architecture for this design (figure 12.1) consists of a
sequencer, a driver, and a monitor that simply prints the values collected from the physical
interface.
Module-based implementation of this verification environment is shown in the follow-
ing program. It should be emphasized that this implementation does not strictly follow the
guidelines for implementing a verification environment (e.g., using ports to communicate
between drivers and sequencer components, etc.). The focus in this example is to highlight
the structure of a module-based implementation of a verification environment.

iProgram 12.2: Module-based verification environment for a OFF


1 'include "program-12.1" IIdff implementation
2
3 module xmCdriver(dff_lf phyjf);
4 task drive(byte unsigned value);
5 @(negedge phyJclk);
6 phLlf.O = value;
7 endtask
8 end module
9
10 module monitor(dffjf phyjf);
11 always @(negedge phyJclk)
12 $display("Oata Collected ", phyJQ);
13 endmodule
14
15 module xmt_sequencer(dffjf phyjf);
16 class transaction;
17 rand byte unsigned A;
18 endclass
19
20 transaction tr;
21
22 task gen_and_drive(input inl count);
23 Ir = new;
24 for (int i=O; i<count; i++) begin
25 void'(tr.randomize());
26 $display("driving ", tr.A);
27 xmt_drvr.drive(tr.A);
28 end
29 endtask
30 endmodule
31
32 module agent(dffjf phy-if);
33 xmt_driver xmt_drvr(phyjf);
34 xmt_sequencer xmt_sqnsr(phyjf);
35 monitor moni(phyJf);
36 end module
37
38 module dfCmodule_based_testO;
39 initial
40 dff_teslbench.ve_agent.xmt_sqnsr.gen_and_drive(4);
41 end module
42
43 module dfUestbench():
44 bit clk;
45 dffj dff_if(clk):
46
47 dfCwrapper dff_wrapper(dffjf. elk):
286 Module-Based VE Implementation

48 dfCmodule_based_test dfUeslO;
49 agent ve_agent(dffJf);
50
51 always #5 elk = -elk;
52 endmodule

In module-based implementation of the environment, a System Veri log module is used


to implement each component in the verification environment (lines 3, 10, 15,32). The hier-
archy is imptemented by instantiating each of these components in their intended place in the
hierarchy (e.g., instantiating the driver, sequencer, and monitor in the agent on tines 33-35).
Functions and tasks are used to implement the procedural behavior of each component (e.g.,
task gen_and_driveO on lines 22-29), and always blocks and initial blocks are used to initiate
the functions in each component (e.g., starting the sequencer on line 40). The testbench is
implemented by instantiating the design wrapper, the module for the top level agent contain-
ing the verification environment components, and a module block that contains the activa-
tion of the sequencer in the agent.
The next section introduces the XBar design. The remainder of this chapter illustrates
the implementation of the verification environment for this design using a module-based
approach.

12.2 XBar Design Specification


-_ _-._------_
---.--------- - .------- - ,---- .. -
----- -_.. - ... .. --- ... .. ---,--------.~----------------- --'-_.....-- ----------_._-----

Figure 12.2 shows the interface view of the XBar crossbar switch. This design consists of
four receive and transmit ports.

I
I
data[3:0] - - . . - - . . data[3:0]
adc\r[3:0] - - . .
XBar - - . . addr[3:0]
inpv - - . . Rev XMT - - . . Dutv
inpa~ ~Dutr

Figure 12.2 XBar Design

A receive port consists of the following signals:


• data: (4-bit input to design). Data received by design.
• addr: (4-bit input to design). Address of port to route data to.
• inpv: (I-bit input to design). Indicates input is valid.
• inpa: (I-bit output from d·~sign). Indicates that device is ready to accept data.

The transmit port consists of the following signals:


• data: (4-bit output from design). Data that was routed to this port.
• addr: (4-bit output from design). Receive port where this data came from.
XBar Design Specification 287

• outv: (l-bit output from design). Indicates output data is valid .


• outr: (i-bit input to design). Indicates that outside agent has read current output.

The receive port protocol is as follows: the device samples inputs and updates the out-
put on the negative edge of the clock. Inputs are applied to the device at the positive edge of
clock. If inpv is not set (current input is not valid), then new input can be applied at any time
(inpv should be set when data is applied). If inpv is set then data at the receive port can be
changed only when inpa is set to 1 (meaning device has read the input signals data and addr).

The transmit port protocol is as follows: the device transfers data from a receive port to
the transmit port indicated on the addr line of the receive port. It sets the transmit port signal
addr to the number of the receive port where the data was received. Device updates transmit
port outputs on the negative edge of the clock. If outv is not set, then device can write to the
output at any time. If outv is set, then device can write to an output port if outr signal is set
(meaning last data applied was read by the outside agent).

The implementation ofXBar design is shown in the following program:

'Program 12.3: XBar Design implementation


1 module xbar_duv(input wire [15:0) rcv_data, input wire [15:0) rcv_addr,
2 output logic [3:0) rcv_inpa, input wire [3:0) rcv_inpv,
3 output logic [15:0] xmt_data, output logic [15:0) xmt_addr,
4 input wire [3:0] xmt_outr, output logic [3:0) xmt_outv,
5 input wire reset, input wire elk);
6
7 int pending_active_inputs;
8
9 always @(negedge elk or posedge reset) begin
10 if (reset) begin
11 rcvJnpa = 4'b1111;
12 xmt_data =12'bz:
13 xmt_addr = 12'bz;
14 xmt_outv = 4'bOOOO;
15 pending_active_inputs =0;
16 end
17 else begin
18 pending_activeJnputs = 0;
19 for (int i =0; i < 4; i++) if (xmt_outr[i) === 1'b1) xmt_outv[iJ = 1'bO;
20 for (int i = 0; i < 4; i++) begin
21 bit [3:0) dest_addr;
22 if (rcvJnpv[iJ !== 1'b1) continue;
23 dest_addr = rcv_addr[4*i +:41;
24 if (xmt_outv[dest_addrJ === 1'b1)begin
25 rcv_inpa[iJ = 1'bO;
26 pending_active_inputs ++;
27 end else begin
28 rcv_inpa[i) =1'b1;
29 xm,-outv[dest_addrl =rcvJnpv[i];
30 xmCadqr[4*dest_addr +:4J = i:
31 xmt_data[4*dest_addr +:4] = rcv_data[4*i +:4);
32 end
33 end
34 end
35 end
36 end module
L ______________________

In defining the XBar sample design, the goal has been to keep the low-le\'el details (i.e.,
protocol complexity) to a minimum while including features that make the structural compo-
288 Module-Based VE Implementation

sition of its verification environment and sequence type requirements a super-set of what
needs to be covered in a majority of designs. These features include:
• Interacting with multiple design ports that use the same protocol, therefore needing
multiple agents per interface verification component.
• DUV receive port requiring active interaction with the verification environment for
receiving an input (therefore, requiring a master transactor).
• DUV transmit port can be set to operate with no verification environment interaction
(by setting outr signals to a constant 1) and could also be driven by a slave transactor
that manipulates signal outr.
• Ability to verify the XBar design through independently running random stimulus
generators at each port, therefore allowing this design to be verified by using only
interface verification components. An extension to this design, described in section
14.1, is used to show the implementation of hierarchical sequences that require an
environment containing module and system verification components.

12.3 XBar VE Architecture


The architecture of the verification environment for the XBar design is shown in figure 12.3.
The following components are identified in this architecture:
• Receive interface: Abstractions for XBar receive port signals.
• Transmit interface: Abstraction for XBar transmit port signals.
• xbar_wrapper: Provides an abstraction ofXBar to the verification environment.
• XBar ReV agent: Contains sequencer, slave driver, and monitor/collector compo-
nents to interact with transmit port of the XBar.
• XBar XMT agent: Contains sequencer, master driver, and monitor/collector compo-
nents to interact with receive port of the XBar.
• xbaUvc_env: Represents an interface verification component containing two agents
that interact with a receive and transm it port of X8ar.
• xbar_sve: Represents the verification environment top level containing the full hierar-
chy of the verification environment components.
• xbar_test: Represents the abstraction of a testcase in a verification environment where
a testcase represents a specific configuration of xbar_sve.
• xbar_testbench: This component is the container of the entire environment.

Component xbar_sve represents the top level of the verification environment hierarchy.
Component xbaUest implements the specific scenario that is carried out by the infrastructure
provided by xbar_sve. Both xbar_sve and xbaUest are placed inside component
xbar_testbench. The implementation of the verification environment architecture shown in
figure 12.4 is described in the remainder of this chapter.
DUVWrapper 289

xbaUestbench

xbaUest

xbar sve xbar wra er

xbar ivc env3


xbar ivc env2
xbar ivc env1
xbar ivc envO
XBar Rev Agent

XBar
I Sequencer I

XBar XMT Agent

I Sequencer

Figure 12.3 XBar Module-Based Verification Environment Architecture

12.4 DUV Wrapper


---_._--- ..,---_.,..... _ . - ,.._....__._---_. __.. _._ .. _---------------------------
The implementation of receive, transmit, and internal interface components are shown in the
following program. The internal interface component is used to selectively make internal
DUV signals available to the verification environment. In the implementation below, internal
DUV signal pending_active_inputs is an internal design signal that is included in the internal
interface.

rp;:ogram 12.4: XBar Design Interface Blocks


1 interface xbar_duvjnternaUf(input wire reset, input wire clk);
2 int pending_activejnputs;
3 endinterface
4
5 interface xbar_duv_rcvjf(input wire reset, input wire clk);
6 logic [15:0] data;
7 logic [15:0] addr:
8 logic [3:0] inpv; 1/ input is valid (input 10 DUV)
9 logic [3:0] inpa: /I input allowed (output from DUV)
10
11 task gel_data_addr(input int port, output logic [3:0] idata. output logic [3:0] iaddr);
12 idata = dala[4'port +:4]; iaddr = adqr[4'port +:4];
13 endtask
14
15 task set_data_addr(input int port, input logic [3:0] idata. input logic [3:0]iaddr);
290 Module-Based VE Implementation

16 data[4*port +:4] <= idata:


17 addr[4*port +:4] <= iaddr:
18 endtask
19 endlnterface: xbar_duv_rcv_if
20
21 Interface xbar_duv_xmUf(input wire reset, input wire clk):
22 logic [15:0] data:
23 logic [15:0] addr;
24 logic [3:0] outv; 1/ output is valid (output from DUV)
25 logic [3:0] outr; 1/ output was read (input to DUV)
26
27 task geCdata_addr(input Int port, output logic [3:0] Idata, output logic [3:0] laddr):
28 = =
idata data[4*port +:4]: iaddr addr[4*port +:4]:
29 endtask
30 : end interface: xbar duv xmt if
L- - - -

The XBar design wrapper, implemented using the receive, transmit, and internal inter-
face blocks, is shown in the following program:

'program 12.5: XBar design wrapper module


1 module xbar_duv_wrapper (xbar_duv_rcvjf xbar_duv_rcvi,
2 xbar_duv_internaUf xbar_duv_inti, xbar_duv_xmUf xbar_duv_xmti);
3
4 assign xbar_duv_intLpending_activejnputs = duv.pending_actlvejnputs;
5
6 xbar_duv duv(
7 .rcv_data(xbar_duv_rcvLdata),.rcv_addr(xbar_duv_rcvi.addr),
8 .rcvjnpa(xbar_duv_rcvLinpa),.rcvjnpv(xbar_duv_rcvi.inpv),
9 .xmt_data(xbar_duv_xmtLdata),.xmCaddr(xbar_duv_xmtLaddr),
10 .xmt_outv(xbar_duv_xmti.outv), .xmt_outr(xbar_ duv_xmti.outr},
11 .reset(xbar_duv_xmtLreset), .clk(xbar_duv_xmtLclk»:
12 endmodule

Components in the verification environment interact with the DUV only through this
wrapper and interface blocks exposed by its ports. Each interface block contains a set of
helper functions to read and write values to the DUV port.

12.5 Library Package


... __.._".._, ...... - _ . - - - - - - -... _-_._.

A library package can be defined to hold type declarations, constants specific to a given
implementation, and global functions and tasks. The package file for the XBar example is
shown below:
I .
Program 12.6: XBar Package Declaration
1 package xbar_pkg:
2 'include "ovm.svh"
3
4 typedef enum bit {FALSE. TRUE} bool:
5
6 class xbar packet extends ovm transaction:
7 IlloQlcal fields -
8 rand int unsigned wait_cycle;
9 rand int port;
10
11 /I physical fields
L.ibrary Package 291

12 rand bit [3:0] src_addr;


13 rand bit [3:0] dest_addr;
14 rand bit[3:0] data;
15
16 constraint cO {src_addr == port;}
17 constraint c1 {src_addr 1= descaddr;}
18 constraint c2 {src_addr < 4;}
19 constraint c3 {dest_addr < 4;}
20 constraint c4 {waiCcycle < 20;}
21
22 'ovm_object_utils_begin(xbar_packet)
23 'ovm_fieldjnt(data,OVM_ALL_ON)
24 'ovm_fieldjnt(src_addr, OVM_ALL_ON)
25 'ovm_fieldjnt(dest_addr,OVM_ALL_ON)
26 'ovm_fieldJnt(waiCcycle,OVM_ALL_ONIOVM_NOCOMPARE)
27 'ovm_field_int(port,OVM_ALL_ONIOVM_NOCOMPARE)
28 'ovm_object_utils_end
29
30 function new (int pn=O, string name="");
31 super.new(name);
32 port = pn;
33 endfunction
34 end class
35
36 function void print_2_packets(xbar_packet pkt1, xbar_packet pkt2);
37 pkt1.print(""); pkt2.print(,"');
38 endfunction: prinC2_packets
39 endpackage:xbar_pkg

This package first includes the header file for the OVM class library (line 2). Including
this file allows all features of the OYM class library to be available when package xbar_pkg is
loaded. This package includes the declaration of special data type bool (line 4) which can be
used by importing this package. This package also contains the declaration of XBar packet
data type. This packet is described in section 12.6.
The packet declaration also includes helper functions that can be used throughout the
verification environment. Function prinC2_packetsO (lines 36-38) is one such function that
prints two packets.
This package can be used by importing it into all modules making use of its contents.
Alternatively, the name resolution operator can be used to directly access the contents of the
package (e.g., xbar_pkg::xbar_packet). Importing the package into each module instead of into
the global space is a better approach since modules in the same file may use different pack-
ages. This usage is shown in the example below:

Iprogram 12.7: Importing a package into a module


1 module xbarJvc_env(interface xbar_ve_rcvi, interface xbar_ve_xmti);
2 import xbar_pkg::*;
3 endmodule
292 Module-Based VE Implementation

12.6 XBar Data Packet


Class objects must be used to model anything but the most simple stimulus. The use of a
class object allows data to be generated during program runtime, randomized, and moved
around using pointers. The use ofa class object also allows methods operating on a data type
(e.g., clone, compare, etc.) to be packaged along with the data description.

The implementation of the XBar data packet xbar_packet is shown in program 12.6
(lines 6-34). Class xbar_packet is used for both transmitted and received packets. This model
contains logical fields walCcycle and port, representing the number of cycles to wait before
applying a packet to the DUV port and the port number to which a packet belongs. This
model also includes physical fields data, src_addr, and dest_addr. A set of named constraints
are used to define legal behavior for this model (lines 16-20). The name of each constraint
can be used to disable that constraint during simulation runtime.

Class xbar_packet is derived from base class ovm_transactlon (line 4) which provides all
infrastructure utilities of class ovm_transaction (e.g., copyO, clone(), print(), compareO) to class
xbar_packet. Field automation macros are used to indicate which fields are included in the
operation ofeach of these utilities (lines 22-28). For example, field walt_cycle is included in
all utility functions (e.g., waiCcycle is printed when function printO ofxbar-packet is called)
except compare operation (line 26). Field automation feature of the OVM class library is
described in detail in section 6.4.

Communication between components in a verification environment falls into two categories:


• Physical connections
• Procedural interfaces

Physical connections are used for communicating with the device physical ports. Proce-
dural interfaces are used for transaction based communication between two verification envi-
ronment components where communication is handled through procedure calls. The use of a
procedural interface allows each module interacting through this procedural interface to be
developed without the other module being available. The reason for this flexibility is that
each module interacts with only the procedural interface and is not aware of the other module
being attached to this interface. Figure 12.4 shows a pictorial view of physical and proce-
dural connections between modules using interface blocks.

The implementation of the interface block used for connecting to the XBar design is
shown in section 12.2. It is important that all interactions with the DUV take place through
such physical interface blocks. This approach provides a clean boundary between the design
and the verification environment.

A procedural interface is used for communication between two \'eritication environ-


ment components. Transaction based communication model (e.g .. blocking, non-blocking
calis, etc.) should be used when implementing procedural interaction between t\\·o compo-
Transaction Interfaces 293

module module

DUV

Verification
Component1

module

Verification
Component2

Figure 12.4 Module-Based View of Physical vs. Procedural Communication

nents. In the module-based implementation approach, the facilities that provide transaction
based communication should be implemented in an interface block.
The following program shows the procedural interface block used for communication
between the sequencer and driver in the verification environment. This implementation is
used for both receive and transmit agents and is used to pass an XBar data packet from the
sequencer to the driver. As will be shown, on the transmit side of the verification environ-
ment (i.e., receive port of DUV), a packet is used to indicate the transfer content. On the
receive side of the verification environment (i.e., DUV transmit port), a packet sent to the
driver indicates the number of wait cycles before a packet should be collected from the DUV
transmit port.

iprogram 12.8: Transaction interface block


1 interface xbar_driverJf;
2 import xbar_pkg::*;
3
4 xbar_packet put_pkt; II placed by putO
5 bit putykt_ready =0;
6
7 xbar_packet done_pkt; II last transfer doneO;
8 event done_pkCready;
9
10 semaphore driver_sem =new(1);
11
12 task automatic put(inout xbar_packet pkt);
13 driver_sem.getO;
14 $cast(put_pkt. pkt.clone());
15 put_pkUeady = 1;
16 @done_pkCready; II Wait transaction to be taken by getO;
17 pkt =done_pkt; II return the packet returned by consumer
18 driver_sem.put():
19 endtask
20
21 task automatic get(output xbar_packet pkt);
22 while (put_pkUeady == 0) @(put_pkUeady);
294 Module-Based VE Implementation

23 put_pkUeady = 0;
24 =
pkt put_pkt;
25 endtask
26
27 function automatic void done(input xbar_packet pkt);
28 $cast(done_pkt, pkt.clone());
29 -> done_pkUeady;
30 endfunction
31 end interface: xbar_driveUf

The above program shows the implementation of the passive transaction channel inter-
face xbar_driver_if shown in figure 9.6 using an interface block. The methods supported by
this interface allow the transaction producer to perfonn a blocking put into the channel and
the transaction consumer (i.e., driver) to perform a blocking get from the channel. In addi-
tion, this implementation allows the transaction producer to synchronize with the consumer
by waiting until the last transaction placed into the channel is consumed (line 16). The inter-
action canied through this interface block consists of the following steps:
• Producer places a packet into the interface by calling function put(). The interaction
implemented here guarantees that the last packet is consumed by the time this func-
tion is called again, and therefore the packet can be placed in the interface immedi-
ately.
• A consumer calls function get(). This function blocks until a packet becomes avail-
able (line 22). Meanwhile, the producer is blocked, waiting for the processing of this
packet to be completed by the consumer (line 16).
• Upon processing the packet, the consumer calls function done(), triggering event
done.JlkUeady (line 29).
• Triggering event done_pkUeady allows function put() to continue beyond line 16.
Before returning, the packet returned by the consumer is copied into inout argument
pkt of function put(). This mechanism allows the producer to receive a reply packet
from the consumer in return to the packet it had placed inside the interface.
Note that method put() is a time consuming task, and therefore, uses a semaphore to cre-
ate mutual exclusion among possibly multiple callers to this task. This means that no new
packet is placed into the transaction interface until the packet that is already placed in the
interface is consumed.

Event interfaces are used as a means of event based communication between verification
environment components. For example, event interfaces are used by a monitor to broadcast
information about conditions that are being monitored. Other components in the environ-
ment can track events defined in an event interface to perform tasks related to conditions
flagged by these events. An event interface is not as powerful as a transaction interface. but
is simple to implement and easy to understand. Module-based implementation of XBar
agents makes use of the following event interfaces implemented using interface blocks:
Building the Hierarchy 295

:Program 12.9: Event interface implementation


1 interface xbar_xmt_agent_evenUfO:
2 import xbar_pkg::*:
3
4 event start all:
5 event stop=all;
6 event inpuUo_duv_coliected:
7 xbar_packet xbar_xmt_pkt:
8 endinterface
9
10 interface xbaucv_agent_evenUfO;
11 import xbar_pkg::*;
12
13 event start all;
14 event stop=all;
15 event duv_valid_output_observed;
16 event olltpuUrom_duv_coliected;
17 xbar_packet xbaucv-Ilkt;
18 end interface

The transmit agent event interface (lines 1-8) contains event InpuUo_duv_collected
which is emitted by the transmit agent monitor indicating that an input to DUV was collected
(defined as when a packet applied to the input is accepted by the DUV). In this case, the
monitor sets xbar_xmt"pkt to the packet collected from the DUV receive port.
Receive agent event interface (lines 10-18) contains event duv_valld_outpuCobserved
emitted by the receive agent monitor indicating that the DUV has produced a valid output,
and event outpuUrom_duv_collected emitted by the receive agent monitor indicating that a
packet was collected from the DUV output (defined as when the receive driver accepts the
packet). In both cases, xbaucv_pkt is set to the observed packet.
The internal engines of both receive and transmit agents (e.g., sequencer) start their
operation when event start_all is detected and stop when event stop_all is detected. These
events are used as a means of controlling verification start and stop time.
As will be shown in the following sections, these event interfaces will be used by both
the sequencer and the scoreboarding processes.

12.9 Building the Hierarchy - - _.._ - -

Now that XBar package and all interfaces have been defined, the hierarchy of the verifica-
tion environment can be built. In this section, the focus is on how verification environment
components are declared, instantiated, and connected. The core function of each component
is described in the following sections.
The implementation of the hierarchy for the receive agent (figure 12.3) is shown in the
following program:

Iprogram 12.10: Receive Agent Hierarchy


1 module xbar_rcv_driver(interface phyJf, interface driverjf, interface evenUf);
2 import xbar_pkg::*;
3 parameter int PORT_NUM 0: =
296 Module-Based VE Implementation

4 endmodule: xbar_rcv_driver
5
6 module xbar_rcv_monitor(interface phyjf, interface event_if);
7 import xbar_pkg::*;
8 parameter int PORT_NUM = 0;
9 endmodule: xbar_rcv_monitor
10
11 module xbar_rcv_sequencer(interface driver_if, Interface evenUf);
12 import xbar_pkg::*;
13 =
parameter int DEF_COUNT OVM_UNDEF;
14 parameter int PORT NUM 0; =
15 parameter int MAXj~ANDOM_SEQS 10; =
16 endmodule: xbar_rcv_sequencer
17
18 module xbaucv_agent(interface phyjf);
19 importxbar_pkg::*;
20 parameter ovm_active_passive_enum AP _TYPE = OVM_ACTIVE;
21 parameter Int PORT_NUM = 0;
22 parameter int DEF_COUNT = 0;
23
24 xbacrcv_agenCevenUf evenUfO;
25 xbaucv_monitor #(.PORT_NUM(PORT_NUM)) rcv_monitor(phy-if, evenUf);
26
27 1/ instantiate driver, driveUf, and sequence driver If active
28 generate
29 if (AP_TYPE == OVM_ACTlVE) begin: active
30 xbar_drlveUf driverjf();
31 xbaucv_driver #(.PORT_NUM(PORT_NUM))
32 driver(phyjf, driveUf, evenUf);
33 xbaucv_sequencer #(.PORT_NUM(PORT_NUM),
34 .DEF_COUNT(DEF_COUNT)) sequencer(driveUf, evenUf);
35 end
36 endgenerate
37 endmodule: xbar_rcv_agent

The following observations apply to this implementation:


• XBar package is imported into all modules (lines 2, 7, 12, 19).
• Physical inteliace passed to the receive agent (line 18) is the interface block attaching
directly to the transmit port of the DUV (program 12.4 line 21).
• An event interface evenUf is instantiated at the top level of receive agent (line 24)
and passed as a port to all of its sub-modules (i.e., driver, monitor, sequencer).
• Parameter AP _TYPE of receive agent (line 20), in combination with a generate state-
ment, is used to decide (line 28) if the driver and the sequencer should be instantiated
in the receive agent.
• Physical transaction interface driver_if of type xbar_driverJf (program 12.8) is instanti-
ated inside an active receive agent (line 30) and used in conjunction with event_if to
connect the driver and the sequencer.
• Each DUV port is attached to a receive agent. This port number is identified by
parameter PORT_NUM (line 21) and is used to set the port number for the monitor,
driver and the sequencer (lines 25, 31, and 33).
• Parameter DEF_COUNT (line 22) is used to control the default behavior of the
sequencer module by using it to set parameter DEF_COUNT of the sequencer. Use of
this parameter is shown in section 12.12.
Building the Hierarchy 297

The implementation hierarchy of XBar transmit agent xbar_xmt_agent (figure 12.3) is


the same as the one for the receive agent. As such, this implementation is not shown in this
text.
An interface verification component for the XBar design (xbaUvc_env in figure 12.3)
contains one receive and one transmit agent. The implementation of this component is shown
in the following program:

Iprogram 12.11: XBar interface verification component implementation


1 module xbaUvc_env(interface xbar_ve_rcvi, interface xbar_vejmti);
2 import xbar_pkg::*;
3 =
parameter ovm_active_passive_enum AP_TVPE OVM_ACTIVE;
4 parameter int PORT_NUM 0; =
5 =
parameter int RCV_DEF_COUNT OVM_UNDEF;
6 =
parameter int XMT_DEF _COUNT OVM_UNDEF;
7
8 xbar_xmt_agent#(.AP_TVPE(AP _TVPE),.PORT_NUM(PORT_NUM),
9 .DEF_COUNT(XMT_DEF _COUNT» xmt_agent (.phyjf(xbar_ve_xmti));
10 xbaucv_agent #(.AP _TVPE(AP_TYPE),.PORT_NUM(PORT_NUM),
11 .DEF_COUNT(RCV_DEF _COUNT» rcv_agent (.phLif(xbar_ve_rcvi));
12 endmodule: xbaUvc_env

Component xbaUvc_env contains ports xbar_ve_rcvi and xbar_ve_xmtl connecting to the


receive and transmit ports of the DUV. This component contains an instance of receive agent
and an instance of the transmit agent (lines 8, 10). Parameters defined for component
xbaUvc_env (Jines 3-6) are used to configure these two instances. This allows the receive
and transmit agents to be configured by specifying the parameters for component
xbaUvc_env. Each agent instance is also connected to the appropriate DUV port through port
objects xbar_veJcvi and xbar_ve_xmtl.
The structure of the verification environment takes shape in xbar_sve component (figure
12.3) implemented in the following program. This component contains four xbaUvc_env
components.
Iprogram 12.12: Component xbar_sve implementation
1 module xbar_sve(interface xbar_ve_rcvi, interface xbar_vejnti, interface xbar_ve_xmti);
2 import xbar_pkg::*;
3 =
parameter ovm_active_passive_enum AP _TVPE OVM_ACTIVE;
4
5 xbar_scoreboard #(.NUM_PORTS(4» sbO;
6 'include "program-12.21" /lconnecting the scoreboard to monitors
7
B xbaUvc_env #(.AP _TVPE(AP _TVPE),.PORT_NUM(O),
9 .RCV_DEF _COUNT(1 OOOOOO),.XMT_DEF _COUNT(OVM_UNDEF)
10 ) xbaUvc_envO (.xbar_ve_rcvi(xbar_ve_rcvi),.xbar_ve_xmti(xbar_ve_xmti»;
11
12 xbaUvc_env #(.AP _ TVPE(AP_ TVPE), .PORT_NUM( 1),
13 .RCV_DEF_COUNT(1000000),.XMT_DEF _COUNT(OVM_UNDEF)
14 ) xbarjvc_env1 (.xbar_ve_rcvi(xbar_ve_rcvi),.xbar_ve_xmti(xbar_ve_xmtill;
15
16 xbarjvc_env #(.AP _ TVPE(AP _ TVPE),.PORT_NUM(2),
17 .RCV_DEF_COUNT(1 OOOOOO) .. XMT _DEF _COUNT(OVM_UNDEF)
18 ) xbaUvc_env2 (.xbar_ve_rcvi(xbar_ve_rcvi),.xbar_ve_xmti(xbar_ve_xmti)l;
19
20 xbarjvc_env #(.AP _TVPE(AP _TVPE) .. PORT_NUM(3),
21 .RCV_DEF_COUNT(1000000),.XMT_DEF _COUNT(OVM_UNDEF)
298 Module-Based VE Implementation

22 ) xbaUvc_env3 (.xbar_ve_rcvi(xbar_ve_rcvi),.xbar_ve_xmti(xbar_ve_xmtill;
23 end module: xbar_sve

This component contains four instances of xbar_ivc_env module (lines 8-22) and an
instance ofa scoreboard defined to have four ports (line 5). For each instance ofxbaUvc_env,
the port number, the active status, and the default count for receive and transmit sequencers
are specified as parameters. Note that the default receive is set to a very large number since
by default we would expect the environment to receive any number of packets transmitted by
DUV. The default transmit count is set to OVM_UNDEF, which as will be shown, indicates that
a random number of packets should be transmitted on that port.
Component xbar_sve implemented above is the top level of the verification environment
hierarchy. The use of this module to create the complete testbench is shown below:
I Program 12.13: Testbeneh top level implementation
1 module xbaUestbench();
2 import xbar_pkg::*;
3
4 bit reset = 0;
5 bit clk = 0;
6
7 xbar_duvymUfxbar_duv_xmt/(reset, clk);
8 xbar_duv_rcvJf xbar_duv_revi(reset, clk);
9 xbar_duvJnternaUf xbar_duvJnti(reset, elk);
~O
11 xbar_duv_wrapper xbacduvw(.*);
12 xbar_sve xbar_sve(.xbar_ve_rcvi(xbar_duv_xmti),
13 .xbar_veJnti(xbar_duv_inti), .xbar_ve_xmti(xbar_duv_rcvi));
14 xbaUest xbaUest(.*);
15
16 always #5 clk =-clk;
17
18 initial begin
19 #0 reset = 1'b1;
20 #51 reset =1'bO;
21 end
22 endmodule: xbaUestbeneh

Three interfaces are instantiated in xbaUestbench, the top level corresponding to


receive, transmit, and intemal interface with the DUV. The top level contains an instance of
the DUV (line 11), xbar_sve an instance of the verification environment hierarchy (line 12),
and an instance of xbaUest which is used for carrying out a target scenario using the infra-
structure available in xbar_sve. This test component acts as the central controller of the veri-
fication activation control. The implementation of this component is shown in section 12.14.

12.10 Monitor
-_. ,.-.-.. --....- - - - - - - -______.___________....,",___ .__ ,__ .____ ..._. __ .__ ..__.__ . ______ ._u_ .__

Receive and transmit monitors of the verification environment track signal activity on the
transmit and receive ports of the DUV respectively. These monitors serve the following pur-
poses:
• Marking DUV port status through events
Monitor 299

• Extracting data from DUV ports


• Verifying correct DUV port traffic
• Collecting coverage on monitored traffic

The monitor components depend on only DUV port values, and are independent of
other components in the agent. This independence allows monitors to be used in passive
agents that do not contain a driver or a sequencer.

The implementation of receive agent monitor is shown in the following program:

lprogram 12.14: XBar receive monitor implementation


1 module xbar_rcv_monitor{interface phLif, interface evenUf):
2 import xbau>kg::*:
3 parameter int PORT_NUM = 0:
4
5 int port_num = PORT_NUM:
6 xbar_packet rcvJ>kt = new{PORT_NUM):
7 event cov_pkt_collected:
8 coy_packet COy = new:
9
10 task rcv_monitor_main_loop{input int port):
11 forever begin
12 @(posedge phLif.clk iff phLif.outv[port] === 1):
13 phLif.geCdata_addr{port, rcv_pkt.data, rcv_pkt.src_addr):
14 rcv_pkt.dest_addr = port:
15
16 $cast{rcv_pkt, rcvJ>kl.clone{)):
17 evenUf.xbar_rcv_pkt =rcv_pkt:
18 -> evenUf.duv_valid_outpuCobserved:
19
20 @(negedge phLif.clk iff phLif.outr[port] === 1):
21 -> evenUf.output_from_duv_coliected:
22 -> cov_pkt_collected:
23 end
24 endtask: rcv_monitor_main-,oop
25
26 covergroup coy_packet @cov_pkt_collected:
27 packet_src_addr: coverpoint rcv_pkt.src_addr:
28 packet_dest_addr: coverpoint rcv_pkt.dest_addr:
29 packeCdata: coverpoint rcv_pkl.data:
30 endgroup
31
32 always @(negedge phLif.clk) begin
33 assertAddrUnknown:assert property (disable iff{phy-'f.reset)
34 ({phy-'f.outv[port_num]) 1-> !$isunknown{phy-'f.addr[4*port_num +:4]))
35 ) else $error{"Address X or Z when outv=1 on duv transmit port");
36 end
37
38 initial rcv_monitor_main_loop{port_num);
39 endmodule: xbar_rcv_monitor

This program highlights the implementation of port monitoring, coverage collection


(lines 26-30), and assertion specification (lines 32-36).

Task rcv_monitor_main_loop() (lines 10-24) is the start point of the main loop for passive
monitoring of the DUV port connected through port phLif. The main loop of the monitor
performs a number of operations:
• It identifies when valid data is placed on the DUV output. Upon detecting this condi-
tion, it sets xbar_rcv_pkt in the event interface event_if to information collected from
300 Module·Based VE Implementation

the physical interface (data and address fields) and emits duv_valid_outpuCobsarved in
avenUf (lines 16, 17). This information is used by the receive sequencer to set tield
walt_cycle in the next generated packet, according to the source address of the incom·
ing packet.
• It identifies when DUV output is read, as signified by assertion of outpr signal (line
19). Upon detecting this condition, it sets xbaucvJ)kt in evant_if and emits event
outpuCfrom_duv_coliected in avenUf (lines 21, 22).
• It emits event cov_pkCcoliectad which is used by the covergroup (Jines 26-30) to col-
lect coverage on the collected packet.
The implementation of this monitor is self contained and depends on only its module
ports. As will be shown, events emitted in evenUf are used by the receive sequencer to syn-
chronize the generation of next packet (used only for passing value of field walt_cycle) to be
passed to the receive driver.
The implementation of the transmit agent monitor is similar in structure and style to
receive agent monitor and is not shown in this text.

12.11 Driver
Transmit and receive drivers interact with transmit and receive sequencers, respectively, to
transmit and receive packets. The implementation of receive driver is shown in the program
below:

'Program 12.15: XBar receive slave driver implementation


1 module xbar_rcv_driver(interface phyJf, interface driver_if, interface evenUf);
2 import xbar_pkg::*;
3 parameter int PORT_NUM = 0;
4 =
int port PORT_NUM;
5
6 task get_packet_and_collect();
7 xbar_packet driver_packet;
8 driveUf.get(driver_packet); IInumber of cycles to wait before collecting
9 collect_packet(port, driver_packet);
10 driverjf.done(driver_packet);
11 endtask
12
13 task collec,-packet(int port, xbar_packet pkt);
14 @(posedge phyJf.clk iff phyJf.outv[port] === 1'b1);
15 repeat (pkt.wait_cycle) @(posedge phyJf.clk);
16 phYJf.outr[port] <= 1'b1;
17 =
pkt.dest_ad dr port;
18 phYJf.get_data_addr(port, pkt.data, pkt.src_addr);
19 @(negedge phyJf.clk);
20 phYJf.outr[port] <= 1'bO;
21 endtask: collect_packet
22
23 always @(negedge phyJf.reset)
24 phyJf.outr = 1'bO; II reset the interface
25
26 initial begin
27 @(evenUf.start_all);
28 $display("RCV collector started on port ", PORT_NUM);
Sequencer 301

29 forever get_packet_and_coliectO;
30 end
31 : endmodule: xbar rCIi driver
L - -

As shown in the above implementation, the receive driver interacts with phy_if (physical
port), driveUf (transaction interface), and evenUf (event interface). It uses driver_if to receive
transactions from the sequencer connected to the other end of this port (line 8) by doing a
blocking get, and indicate that transfer is completed (line 10). The value offield wait_cycle in
the packet received from the sequencer is used to determine the number of cycles to wait
before a packet read is acknowledged on the DUV interface (line 15). Receive driver uses
phy_if to collect the packet from the DUV transmit port. The event interface evenUf is passed
to the driver as a general guideline and is not used in this implementation. The important
observation about this implementation is that because of the use of different types of inter-
faces, the implementation of this driver is completely self contained. The implementation of
the transmit agent driver is similar in structure and style to receive agent driver and is not
shown in this text.

12.12 Sequencer
Receive and transmit sequencers are used to generate and pass packets to receive and trans-
mit drivers, respectively. Sequencers form the core of scenario generation utilities, and as
such, should provide a rich set of features that allow for all scenarios to be generated.
The implementation of a sequencer is divided into four aspects:
• Structure and initialization
• Sequencer main loop
• Item generation
• Sequencer activation
The following program shows the implementation of the XBar receive sequencer using
a module block. This implementation highlights the initialization of the sequencer using
module parameters.

I Program12.16: XBar receive sequencer module


1 module xbaucv_sequencer(interface driveUf, interface evenUf);
2 Import xbar_pkg::*;
3 =
parameter int DEF_COUNT OVM_UNDEF;
4 parameter int PORT_NUM = 0;
5 parameter int MAX_RANDOM_SEQS = 10;
6
7 int count =DEF COUNT;
8 =
int port_num PORT_NUM;
9
10 'include "program-12.17" limain loop functions
11 'include "progra m-12.18" /litem generation fields and functions
12 . include "program-12.19" IIstart of generation
13 endmodule: xbar_rcv_sequencer
302 Module-Based VE Implementation

XBar receive sequencer has ports driver_if connected to the receive driver and event_if
connected to all subcomponents of the receive agent. Module parameters (lines 3-5) are used
to initialize this sequencer.
The following program shows the implementation of the main generation methods of
this sequencer:

'Program 12.17: XBar receive sequencer main sequence generation loop


1 function void set_count(input int new_cnt);
2 if (count 1= OVM_UNDEF && new_cnt != count)
3 $display(nSetting main's count to a new value, %Od n, new_cnt);
4 count = new cnt;
5 endfunction: seccount
6
7 task mainO;
8 if (count == OVM_UNDEF) assert (randomize(count) with
9 {count inside ([1:MAX_RANDOM_SEQS]};}) else $fatal;
10
11 for (int i = 0; i < count; i++) begin
12 randcase
13 1: simpleO;
14 1: reactive _receiveO;
15 endcase
16 end
17 endtask: main

Task mainO (lines 7-16) is the start point of generation activity and is called when this
sequencer is activated (program 12.19). Variable count is used to decide how many packets
should be generated. The value of count is set to a random value if it is initially set to
OVM_UNDEF. Function seCcountO is also provided so that the value of count can be set by a
top level testcase component before the sequencer is activated. Task mainO uses a randcase
statement to randomly choose between functions simpleO and reactive_recelveO. These func-
tions, shown in the next program, generate packets and pass the generated packet to the
driver through interface driver_if.
The implementation of packet generation methods are shown in the following program:

'Program 12.18: XBar receive sequencer packet generation functions


1 xbar_packet template_pkt;
2
3 function void override_template_item(xbar_packet tpkt);
4 $cast(template_pkt, tpktclone());
5 endfunction
6
7 task automatic simple();
8 xbar_packet rpkt;
9 $cast(rpkt, template_pkt.clone());
10 assert (rpkt.randomizeO with {
11 rpkt.src_addr ::= port_num;
12 rpktport == port_num;}) else $fatal;
13 driveUf.put(rpkt);
14 endtask: simple
15
16 task automatic reactive_receive():
17 xbar_packet rpkt;
18 xbar_packet mpkt;
19 $cast(rpkt, template_pkt.clone());
20 assert (rpkt.randomize() with {
21 rpkt.src_addr==port_num;
Sequencer 303

22 rpkt.port==port_num;}) else $fatal;


23
24 @(event]duv_valid_output_observed);
25 mpkt=evenUf.xbaucv_pkt;
26 rpkt.wait_cycle=mpkt.data;
27 driveUf.put(rpkt);
28 endtask: reactive_receive

The above implementation is included in the module block that implements the receive
sequencer (program 12.16 line 11). This means that the above declarations exist in the body
of the sequencer module block.
This implementation includes field template_pkt which is an instance of the default item
type xbar,J)8cket that is to be generated by this sequencer (line 1). This default item will be
initialized before the sequencer starts and only if it has not already been initialized by the tes-
tcase that starts this sequencer (program 12.19 line 4). Overriding the default behavior of this
sequencer is achieved by assigning template_Item to a class object derived from the default
type xbar_packet. For example, class xbar_packeCnew can be derived from xbar_packet con-
taining a new constraint block. Because of polymorphism, assigning template_item to an
object of type xbar_packeCnew and randomizing templateJtem produces results matching the
additional constraint block specified for class xbar_packeCnew. Note that in this implementa-
tion approach, the default sequence item type for this sequencer is indicated through the
object type that is stored in template_item, and losing this object means losing information of
what sequence type should be generated. This consideration plays a role in the how sequence
generation methods are implemented.
Function override_template_itemO (lines 3-5) is provided as a means of overriding the
default item type through class polymorphism (section 4.7.4). The use of this function is
described in section 12.15.
Function simp leO (lines 7-14) operates by first creating a clone of template item
template_pkt (line 9). Working on a cloned copy of template_pkt is mandatory since a
sequencer may have multiple concurrently running threads that use template_pkt as a template
item and as such, template_pkt should not be used to carry out operations specific to anyone
thread. Function cloneO used in this step is a predefined function of class ovm_transaction
which is the parent class of xbar_packet. The use of this function guarantees that the cloned
object created on line 9 and assigned to rpkt, has the true type of object stored in template_pkt
even when this type is one derived from xbar_packet. Note that because of polymorphism,
randomizing rpkt produces a result that satisfies any constraint specified for the true type of
template_pkt object. Function putO overwrites the value of its argument, therefore after return-
ing from call to put(), the object pointed to by rpkt is the object returned by the consumer.
Function reactive_receiveO (lines 16-28) interacts with the receive monitor to decide the
value of walt_cycle depending on the source address of the packet that is currently arriving at
the DUV transmit port connected to this receive agent. The implementation of this function
is similar to function simpleO, except that after randomizing rpkt, the sequencer waits for
event duv_valid_outpuCobserved in event interface evenUf to be emitted by the monitor, and
then sets the yalue ofwalCcycle to the data field of the packet currently being received on the
DUV pon (line 26). It then passes rpkt to the consumer by calling function put() of driver_if
(line 28).
304 Module-Based VE Implementation

The following program shows the activation mechanism for this sequencer:

'Program 12.19: XBarreceive sequencer activation


1 always @(evenUf.start_all) begin
2 $display("RCV sequencer started on port ", PORT_NUM);
3 if(template_pkt == nUll)
4 $cast(template_pkt, ovm_factory::create_object("xbar_packet"»;
5 fork
6 begin
7 mainO;
8 $display("RCV sequencer completed on port", PORT_NUM);
9 end
10 begin
11 @(evenUf.stop_all);
12 $display("RCV sequencer stopped on port ", PORT_NUM);
13 end
14 join_any
15 template-pkt = null;
16 end

Start and stop of this sequencer is controlled by events start_all and stop_all defined in its
event interface event_if. The always loop is entered when event start_all is emitted (line 1).
This component first creates an object for template item template_pkt if it is not already
assigned explicitly by calling function override_template_item() (program 12.19 line 3). Note
that the DVM factory is used to create this object, and therefore, type override mechanisms
(section 6.3.1) can be used to modify the default behavior of this sequencer by changing the
object type that is created in this step. This component then executes task mainO, the
sequencer main loop (lines 6-9) in parallel with a thread waiting the occurrence of event
stop_all (lines 10-13) by using afork-join_any statement. If event stop_all is emitted before
task mainO is completed, then the thread of task mainO is killed. Before completing this
always block, field template_pkt is se to null so that factory override can take effect for the
next activation of this sequencer. The always block is restarted at the next occurrence of
event start_all.
Sequence generation in a module-based implementation has limited flexibility. Adding
new behaviors requires that new functions be added to this module, and the weight of rand-
case statement in task reactive_receiveO to be changed. Generating multi-sided scenarios (sec-
tion 8.1) is also challenging when using modules to implement sequencers. Chapter 14
presents the use of the OVM class library for implementing sequencers using class objects
while addressing these sequence generation challenges.

Scoreboards check for correct data transfer between DUV ports. The following program
shows the scoreboard interface methods for the XBar verification environment:
r=:---
Program 12.20: XBar scoreboard implementation
1 module xbar_scoreboardO;
2 import xbar_pkg::*;
3 parameter int NUM_PORTS = 1;
4
Scoreboardlng 305

5 function void insert(xbar_packet pkt);


6 //implementation not shown
7 endfunction
B
9 function void match(xbar_packet pkt);
10 I/implementation not shown
11 endfunction
12 endmodule

The internal implementation of the scoreboard is not shown. The scoreboard, however,
creates an array of scoreboards, one for each port. Inserting a packet into the scoreboard
places the packet in the scoreboard for its destination address. Matching a packet to the
scoreboard, matches that packet against its destination address as well.
The scoreboard for XBar design is instantiated in xbar_sve (program 12.12 line 5). This
is the scope that contains agent instances for all four ports of the design and as such, monitor
components for all four agents can be accessed. The following program shows the content of
file included in program 12.12 at line 6 for connecting the scoreboard to the receive and
transmit monitors inside each agent:

'Program 12.21: Connecting XBar scoreboard to receive and transmit monitors


1 always @(xbarjvc_envO.rcv_agent.evenUf.output_from_duv_collected)
2 sb.match(xbarjvc_envO.rcv_agent.evenUf.xbar_rcv_pkt);
3 always @(xbarjvc_env1.rcv_agent.evenUf.outpuCfrom_duv_coliected)
4 sb.match(xbarjvc_env1. rcv_agent.evenUf.xbar_rcv_pkt);
5 always @(xbaUvc_env2.rcv_agent.evenUf.outpuUrom_duv_coliected)
6 sb.match(xbarjvc_env2.rcv_agent.evenUf.xbar_rcv_pkt);
7 always @(xbarjvc_env3.rcv_agent.evenUf.outpuUrom_duv_coliected)
B sb.match(xbarjvc_env3.rcv_agent.evenUf.xbar_rcv_pkt);
9
10 always @(xbaUvc_envO.xmt_agent.evenUf.input_to_duv_coliected)
11 sb.insert(xbarjvc_envO.xmt_agent.evenUf.xbar_xmCpkt);
12 always @(xbarjvc_env1.xmCagent.evenUf.input_to_duv_coliected)
13 sb.insert(xbarjvc_env1.xmt_agent.evenUf.xbar_xmt-pkt);
14 always @(xbarjvc_env2.xmt_agent.evenUf.input_to_duv_coliected)
15 sb.insert(xbarjvc_env2.xmt_agent.evenUf.xbar_xmt_pkt);
16 always @(xbarjvc_env3.xmt_agent.evenUf.inpuUo_duv_coliected)
17 sb.insert(xbarjvc_env3.xmt_agent.evenUf.xbar_xmt_pkt);

As shown in this program, events in event interfaces of receive and transmit agents are
tracked and their collected packet is either inserted into the scoreboard (for transmit agents)
or matched against the scoreboard (for receive agents).
The implementation of score boarding in this design is such that the same scoreboarding
implementation continues to check for correct packet transfer when the agent components
are changed from active to passive and the packets are generated by other components in the
DUV and not by the verification environment. The reason is that this scoreboarding depends
on only the monitor and event interface implementations which are present in passive agents.
306 Module-Based VE Implementation

12.14 Test Component


A test component is used to carry out specific verification scenarios. The following example
shows a simple test component making use of the default behavior of the XBar verification
environment:

:program 12.22: XBar test component using default behavior


1 module xbaUestO;
2 import xbar_pkg::*;
3
4 initial begin
5 @(xbaUestbench.reset== 1'bO);
6 xbar_testbench.xbar_sve.startO:
7 #10000;
8 $finish;
9 end
10 endmodule

Module xbaUest shown above is instantiated in the top level testbench. Upon entering
the initial block, the test component waits for the global reset signal to get deactivated (line
5). It then calls function startO of the xbar_sve component which in turn emits event start_all
in the event interface blocks of all agents. All sequencers are activated after this function is
called. Transmit sequencers generate a random number of packets since their count variable
is initialized to OVM_UNDEF, and receive sequencers receive up to 1,000,000 packets. The test
component shown above waits for 10,000 time units after starting the environment and then
ends the simulation by calling function $finish (line 8).
A test component can also be used to create a directed test. A directed-test is shown in
the following example:

'Program 12.23: XBar test component for a directed test


1 module xbaUest();
2 import xbar_pkg::*;
3
4 initial begin
5 xbar_testbench .xbar_ sve.disable_ a11_xmt_agent_sequencers();
6 @(xbaUestbench.reset == 1'bO);
7 xbar_testbench.xbar_sve.start();
8
9 xbaUestbench.xbar_sve.xmt_directed_packet(O, 1 ,2);
10 repeat (2) wait_for_packet_collecled_on_port(1);
11 xbaUeslbench.xbar_sve.xml_direcled_packet(3,1,5);
12 repeat (5) waiUor_packel_coliected_on_port(1);
13 #1000;
14 $finish;
15 end
16 endmodule
L - -_ _ __

In tl1e first step. the above directed test disables the sequencers in all transmit agents
(line 5) by calling function disable_all_xmCagent_sequencersO of xbar_sve. This function sets
\"ariable count of all transmit sequencers to 0 by calling the seCcountO function of all transmit
sequencers (program l2.l7). The test then \\'aits for the global reset to be deactivated (line 6)
and then starts the verification environment (line 7). Next. this test sends two packets from
XBar port 0 to XBar port 1 by calling function xmt_directed_packetO of xbar_sve component.
Modifying Sequencer Default Behavior 307

This function in tum calls a function in the transmit sequencer for port 0 to send 2 packets to
destination port 1.

Function waIUor_packet_coliected_on_port() waits for event output_from_duv_collected in


the event interface block of xbar_agent on port 1 to be emitted (line 10). The same process is
then repeated for sending five packets from XBar port 3 to XBar port 1 (line 12) and then
waiting for these packets to be received at XBar port 1.

The default behavior of a sequencer can be changed through one of these mechanisms:
• Adding new functions for generating specific sequences of items .
• Calling function overrlde_template_ltem() of the sequencer to explicitly override its
default sequence item type before sequencer is started.
• Using type override for the OVM factory to implicitly modify the object type created
by the OVM factory for template_pkt when the sequencer is being started.
Functions slmple() and reactive_receive() (section 12.12) show examples of how new
packet generation capabilities can be added to this sequencer. The same approach can be
used for adding new functions that add new generation behaviors to the sequencer.
Both explicit and implicit modification of the sequencer default item require that a new
class type be derived from the default sequence item of a sequencer. In the explicit approach,
the default sequence item of a sequencer is replaced with an instance of the new class type by
calling function override_template_itemO. In the implicit approach, a type override from the
default sequence item to the newly defined class type is specified for the DVM factory
before the sequencer is activated.
The following program shows the implementation of a testcase that explicitly and
implicitly modifies the sequencers in the XBar verification environment:

I Program12.24: XBar test component for a directed test


1 module xbaUestO;
2 import xbar_pkg::*;
3
4 class xbar_packet_wait_5 extends xbar_packet;
5 constraint nowait {wait_cycle == 5;}
6 'ovm_objecCutils(xbar_packet_wait_5)
7 endc\ass
B
9 class xbar_packet_wait_B extends xbar_packet;
10 constraint nowait {wait_cycle == S;}
11 'ovm_objecCutils(xbar_packet_wait_B)
12 endc\ass
13
14 initial begin
15 xbar_packet_wait_B pktS;
16 =
pktB new;
17
1B @(xbaUestbench.reset == 1'bO);
19 xbaUestbench.xbar_sve.ivc_override_rcv_template-,tem(2, pktS);
20 xbaUestbench.xbar_sve.ivc_override_rcv_template_item(3, pktS);
308 Module-Based VE Implementation

21 ovm_factory::seUype_ override("xbar_packet", "xbar_packet_wait_5");


22 xbar_testbench.xbar_sve. startO;
23 #10000;
24 $finish;
25 end
26 endmodule

The above implementation shows the definition of class xbar_packeCwait_5 (lines 4-7)
and class xbar_packet_waiCB (lines 9-12) which are derived from class xbar_packet. Note that
macro ovm_objecCutils() is specified for both these classes so that they are handled correctly
by the GVM factory and the predefined functions provided by the GVM class library (e.g.,
clone()). The test is then implemented by first creating class object pkta of type
xbar_packet_waiC8 (line 16), and then using function ivc_overrideJcv_template_item() to
explicitly set the default sequence item for receive sequencers at ports 2 and 3 to pkta. Func-
tion ivc_override_rcv_template_item() is implemented as part of module xbar_sve and in tum
calls function override_template_item() of the sequencer at the port identified by its first argu-
ment. A type override is specified for the GVM factory to generate an object of type
xbar_packeCwait_5 anytime an object of type xbar_packet is requested (line 21). This override
causes the default sequence item for all sequencers whose default sequence types are not
explicitly initialized to be set to an object oftype xbar_packet_wait_5 (see program 12.19 lines
3--4 for the implementation that achieves this behavior). With this configuration, receive
sequencers at ports 2 and 3 use type xbar_packeCwait_B as their default sequence item type,
and receive sequencers at ports 0 and 1 and all transmit sequencers use class type
xbar_packeCwaiC5 as their default sequence item type. All sequencers are then started by
calling function xbar_sve.start() of xbar_sve which in tum starts the sequencers in all receive
and transmit agents.
CHAPTER 13 Class-Based VE
Implementation

Class-based implementation of a verification environment uses classes to model verification


environments and their hierarchies. Using classes to model the verification environment and
hierarchy leads to great flexibility in how the verification environment can be created and
manipulated.
The following tasks must be handled in creating a verification environment:
• Creating the components
• Creating the environment hierarchy
• Interconnecting
• Generating sequences of transactions
Verification environments and their hierarchy can naturally be modeled using classes.
In this approach, a class object represents an environment component and class object con-
tainment represents the environment hierarchy. Creation of transaction interfaces and trans-
action sequences is not a concept native to classes and, therefore, requires· extensive
infrastructure supporting the implementation of these concepts using class objects. The
OVM class library provides the full set of classes and utilities required to create a class-based
implementation of a verification environment. Features of OVM for implementing these
aspects of a verification environment are presented in detail in chapters 6 through 8.
This chapter illustrates the use of a class-based implementation approach for building a
complete verification environment. First, an example of a small class-based implementation
is provided in section 13.1 to provide a general feel for this implementation approach. The
remainder of this chapter provides an in-depth view of a class-based implementation of the
XBar design (introduced in section 12.2). Generation of transaction sequences is presented in
chapter 14.

13.1 Class-Based
- - -
Implementation
-- ... _
. -- ---------_. . ..
...... -
Overview---.
...

This section provides an o\'erview of the class-based implementation approach by showing a


class-based implementation for the DFF design first introduced in section 12.1. The focus in
310 Class-Based VE Implementation

providing this example is not on following correct methodology but on providing a clear
overview of elements involved in a class-based implementation. The class-based implemen-
tation shown in this example should be contrasted with the module-based implementation of
the same example (section 12.1). The implementation of a verification environment follow-
ing OVM guidelines is described in section 13.6.
The following program shows class-based implementation of the verification environ-
ment shown in figure 12.1:

'Program 13.1: Class-based verification environment for a OFF


1 module dfCelass_based_test(dfUf phy-if, input bit elk);
2 typedef class xmt_agent;
3 class transaction;
4 rand byte unsigned A;
5 endclass
6 class xmCdriver;
7 xmt_agent parent;
8 task drive(transaction tr);
9 @(negedge parent.pif.clk);
10 parent.pif.D = tr.A;
11 endtask
12 function new(xmt_agent p=null); parent =p; endfunction
13 endclass
14 class xmt_monitor;
15 xmt_agent parent;
16 task collect();
17 forever @(negedge parent.pif.elk) $display(parent.pif.Q);
18 endtask
19 function new(xmt_agent p=null); parent =p; endfunction
20 endelass
21 class xmt_sequencer;
22 xmt_agent parent;
23 transaction tr;
24 task gen_and_drive(input int count);
25 =
tr new;
26 for (int ;=0; i<count; i++) begin
27 assert(tr.randomize(»;
28 parent.xmt_ drvr.drive(tr);
29 end
30 endtask
31 function new(xmt_agent p=null); parent =p; endfunction
32 endclass
33 class xmt_agent;
34 virtual dfUf pif;
35 xmt_ driver xmt_ drvr;
36 xmt_sequencer xmt_sqnsr;
37 xmt monitor xmt mntr;
38 function r,ewO; -
39 xmt_drvr = new(this); xmt_sqnsr = new(this); xmt_mntr = new(this);
40 endfunct;on
41 endclass
42
43 xmt_agent veJmt_agent;
44 initial begin
45 veJmt_agent = new:
46 ve_xmt_agenLpif = phLif;
47 fork
48 ve_xmt_agent.xmt_sqnsr.gen_and_drive(1 0);
49 .. ve_xmt_agent.xmt_mntr.collect();
50 JOin_any
51 $finish;
-XBar Data Packet 311

52 end
53 endmodule
~---------------------

In a class-based implementation of the verification environment, all components are


modeled as class objects instantiated inside a top level module block. The class object repre-
senting the agent (lines 33-41) is composed of other class objects representing the driver
(lines 6-13), monitor (lines 14-20), and the sequencer (lines 21-32). The agent is then
instantiated in a module block (line 43) and activated in an initial block (lines 48, 49) to
drive traffic to and collect traffic from the design.
An important step in this implementation is the creation of a pointer to the physical
interface in class xmCagent (line 34) and initializing it with the actual interface to the design
(line 46). After initializing the physical interface, methods in the class hierarchy can access
the design through this field in the class hierarchy.
The top level testbench for class-based implementation is shown below.

'Program 13.2: DFF top level testbench


1 module dfUestbenchO;
2 bit elk;
3
4 dfUf dffjf(clk);
5
6 dff_wrapper dff_wrapper(dff_if, elk);
7 dfCelass_based_test dff_test(dfUf, elk);
8
9 always #5 elk = -elk;
10 endmodule

The major difference between this top level environment and the one for module-based
implementation is that in class-based implementation, the verification environment hierarchy
is modeled as a class hierarchy, where in a module-based implementation, the verification
environment hierarchy is modeled as a module hierarchy.
It should be noted that the class-based implementation shown above is only meant to
highlight the general structure of a class-based implementation, and this implementation
does not follow the recommended guidelines for implementing a verification environment.
For example, this implementation does not follow the component self-containment guideline
since in this example, each component makes method calls into other components in the
environment (e.g., the sequencer directly calling the drive() method of the driver).
The remainder of this chapter focuses on showing the implementation of the verifica-
tion environment for the XBar design using the OVM class library, and by following the
class-based implementation approach.

13.2 XBar Data Packet


The first step in building the \uification environment for the XBar design is to define the
data object that will be used in the em ironment. The class declaration for this data model is
shown below:
312 Class-Based VE Implementation

'Program 13.3: XBar Packet model


1 class xbar_packet extends ovm_sequenceJtem:
2 rand bit [3:0] data;
3 rand bit [3:0] src_addr;
4 rand bit [3:0] dest_addr:
5 rand int unsigned wait_cycle;
6
7. 'ovm_object_utils_begin(xbar_packet)
8 'ovm_fieldJnt(data, OVM_ALL_ON)
9 'ovm_fleldJnt(src_addr,OVM_ALL_ON)
10 'ovm_fleldJnt(dest_addr,OVM_ALL_ON)
11 'ovm_fleldJnt(wait_cycle,OVM_ALL_ONIOVM_NOCOMPARE)
12 'ovm_object_utils_end
13
14 constraint c10 {src_addr < 4; dest_addr < 4; desCaddr!= src_addr;}
15 constraint c12 {waiCcycle < 300;}
16
17 function new(string name="xbar_packeUnst");
18 super.new(name);
19 endfunction: new
20 endclass: xbar_packet

It is expected that data objects are generated by sequences. As such, classes represent-
ing a data model should be inherited from ovm_sequence_ltem (line 1). The implementation
above shows the use of begin/end variation of macro ovm_objecCutlls() to register class
xbar_packet with the OVM factory (line 7). In addition, field automation macros (section 6.4)
are specified to allow members of this data model to be managed by the OVM facilities
(lines 8-11). The constructor for this model allows for specifying a logical name for an
instance, if necessary.

13.3 Physical Interfaces


_ ,._--,."----_.. -._-_.,,. -- .._" ....__ -_ _.._----_._-_ .._---------",--------.. ,-_...
-. .. .•. .. .., ,.",- . ---_.._-----, ..... _-_._---------

Class-based implementation of a verification environment uses the System Veri log interface
construct to communicate between the verification environment and the DUV. In class-based
implementations a virtual interface· is used as a means of allowing classes to access a Sys-
temVerilog interface. This means that classes representing components in the verification
environment have a pointer to the physical interface to the DUV. This pointer is initialized to
point to the DUV interface when the verification environment hierarchy is built. The follow-
ing small program shows the use of a virtual interface as a field of a class object. This physi-
cal interface can then be accessed through this pointer:

'Program 13.4: Virtual interface use model


1 interface duvJfO;
2 bit elk;
3 endinterface
4
5 module duv(duvjf dif):
6 initial forever #1 dif.clk = -dif.clk;
7 endmodule
8
9 module test(duvjf dif);
10 class my_class:
11 virtual duvjf virtual_dif:
Transaction Ports and Interfaces 313

12 task runO;
13 repeat (10) @(virtual_dif.clk) $display($time. virtual_dif.clk);
14 $finish;
15 endtask
16 end class
17
18 my-class sve;
19 initial begin
20 sve = newO;
21 sve. virtual dif = dif;
22 sve.run(); -
23 end
24 end module
25
26 module testbenchO;
27 duv_if mdifO;
28 duv duvi(mdif);
29 test test(mdif);
30 endmodule

The above example shows the declaration of class my_class containing virtual interface
virtual_dif (line 11). Once this virtual interface is initialized (line 21) to point to an instance of
an interface (line 27), it can be used in a class object for accessing the physical signals inside
that physical interface (lIne 13).

13.4 Transaction Ports and Interfaces


The connection between two components depends on the communication requirements of
the components being connected. The OVM class library provides the following types of
transaction-based connections:
• Transaction interfaces (introduced in chapter 9.3 and 9.4)
• Transaction channels (introduced in section 9.7)
• Analysis ports and Channels (introduced in sections 9.5 and 9.7.2)
• Sequence item Interfaces (introduced in section 8.7)
• Sequence interfaces (introduced in section 8.8.1)
In the XBar verification environment, transaction interfaces are used for connecting
sequencers and monitors (section 13.6). Analysis ports are used with monitors to broadcast
collected packets (section 13.6.1.1), and then used by scoreboards (section 13.10) to check
correct flow of packets between design ports. Sequence item interfaces are used for connect-
ing sequencers with drivers in both the receive and transmit agents (section 13.6.1.4).
Sequence interfaces are used for connecting virtual sequencers to downstream sequencers
(section 14.7).
314 Class·Based VE Implementation

Event objects are used as a means of event-based synchronization between verification envi-
ronment components. OVM provides class ovm_event which can be used to exchange data
objects derived from ovm_object. The provider of the data object triggers the event while pro-
viding the data to be exchanged, and the consumer of the data object waits for the event to be
triggered and then collects the data object by calling a predefined method of the event object.
Event-based synchronization is similar to a non-blocking put and a blocking get operation by
the producer and consumer, respectively. As such, event-based synchronization provides a
very simple form of transaction-based synchronization. OVM also provides the predefined
class ovm_event_callback that can be used to initiate methods calls when an event is triggered
instead of passing data objects.
The use of event-based synchronization is shown in the following example program.
Note that this example does not strictly follow the OVM guidelines for creating an environ-
ment hierarchy (section 7.2) since the focus here is on how an event object are used for syn-
chronization purposes. The implementation of a verification environment following OVM
guidelines is described in section 13.6.

'Program 13.5: Use model for class ovm_event


1 module top;
2 'include "ovm.svh"
3
4 class packet extends ovm_object;
5 'ovm_object_utils(packet)
6 int data;
7 endclass
8
9 class monitor extends ovm monitor;
10 .ovm_component_utilS(monitor)
11 ovm_event packet_ready;
12
13 function new (string name. ovm_component parent=null);
14 super.new(name.parent);
15 packet_ready = new("packet_ready");
16 endfunction
17
18 virtual task run 0;
19 packet collected_packet;
20 coliected_packet=newO;
21 collected_packet.data = 123;
22 packeUeady.trigger(coliected_packet);
23 endtask
24 endclass
25
26 class target extends ovm_env;
27 'ovm_component_utils(target)
28 ovm_event monitor_got_pkt;
29
30 function new (string name. ovm_component parent=null);
31 super.new(name.parent);
32 monitor_got_pkt = new("monitor_got_pkt");
33 endfunction
34
35 virtual task run ():
36 packet collected_packet;
37 monitor_got_pkt.waiCptrigger();
38 Seast( collected _packet. monitor_got_pkt.geUrigger_dataO):

-----------_.--------------_._--
Building the Hierarchy 315

39 $display("got packet with data=%Od",coliected_packet.data);


40 endtask
41 end class
42
43 monitor mntr = new("mntr");
44 target tgt = new("tgt");
45
46 initial begin
47 tgt.monitor_got_pkt = mntr.packet_ready;
48 run_testO;
49 end
50 end module

The above example defines classes monitor and target. Class monitor contains event
packeUeady of type ovm_event (line 11). The monitor triggers this event by passing
collected_packet, the packet currently collected in the monitor and derived from ovm_object,
to function trigger() of event object packeCready (line 22). Event packet_ready is allocated in
the constructor for class monitor (line 15). Class target contains event monitor_got_pkt of type
ovm_event. This event is used by first waiting for it to be triggered (line 37) and then collect-
ing the data from this event object (lines 38-39). Note that event monitor_got_pkt is allocated
inside the constructor for class target (line 32) even though it will be set to point to the event
object in the monitor during hierarchy construction.

The top level of this example instantiates monitor mntr (line 43) and target tgt (line 44).
It then overrides event pointer monitoUloCpkt in tgt by setting it to event packet_ready in mntr
(line 47) before running the test (line 48).

The implementation of target and monitor classes shown in this example are specially
defined to allow each component to operate even if the other component is not yet available,
hence achieving self-containment. This is accomplished by creating the event object in each
component in its constructor even though it is understood that the event pointer in the target
will be changed to point to the event object in the monitor when the hierarchy is being built.

Events provide an easy-to-implement approach for synchronizing the activities of mul-


tiple components. The use of events can however lead to problems when a verification envi-
ronment is moved from a simulation-based platform to a hybrid platform (e.g., simulation
and emulation). For example, in moving a verification environment to an emulation plat-
fonn, the monitor may be evaluated by the emulation platform while the sequencer may be
evaluated in the simulator. For such cases, it is best to use transaction interfaces (chapter 9)
in order to avoid issues that may arise when accessing event objects across verification plat-
forms. As such, the implementation of the verification environment for the XBar design uses
transaction interfaces to synchronize between interacting components (section 13.6).

13.6 Building the Hierarchy


Building a class-based verification em' ironment using the OYM class library consists of the
following steps:
• For each environment component. create a custom class derived from an OYM pre-
defined class appropriate for that component (section 7.3).
316 Class-Based VE Implementation

• Customize the new class by adding fields specific to that component.


• Use appropriate macros to register each class and its fields with the OVM factory.
• Instantiate interface connector objects as needed by the communication requirements
of environment components.
• Define the constructor for each component (function newO).
• Define the builder for each component (task buildO).
The architecture of a class-based implementation of the XBar verification environment
a£cording to the guidelines of the OVM is described in this section starting from the compo-
mints in the lowest layers of the verification environment ending with the top level compo-
nent. This section is focused on creating the hierarchy and the connectivity between
components. The implementation of each component is then described in the following sec-
tions.

13.6.1 XBar Receive Agent


Figure 13.1 shows the architecture of the XBar receive agent. This agent consists of a moni-
tor, a driver, and a sequencer. The implementation of components in this hierarchy are shown
in the following subsections.

XBar Receive Agent (xbar rcv agent)

Monitor
7-l
~
pkt_bdcst_port
analysis interface
b pkt_bdcst_port

I PhY_ifl

Sequencer 4- blocking peek inlorfaco n duv_out_validjmp

dW_,"'_~'"j><>' ~~ D"~, ~

seq item cons ifCf- .. seqjtem_prod_if


- - - sequence Item Interface

Figure 13.1 XBar Receive Agent Architecture

13.6.1.1 Receive Monitor


The monitor component (figure 13.1) contains analysis port pkt_bdcsCport used for broad-
casting the latest packet collected by the monitor, and an event object duv_outpuUs_valid
which is emitted when the monitor detects the start of a valid packet on the DUV interface.
The monitor also contains virtual interface phLif which is initialized to point to the top level
interface block that connects with the transmit port of the DUV.
The implementation of this monitor is shown in the following program:
Building the Hierarchy 317

Iprogram 13.6: Receive Monitor class declaration


1 class xbar rcv monitor extends ovm monitor;
2 virtual XbElr_duv_xmUf phyjf;-
3 int unsigned port_num;
4 xbar_packet rcv_pkt;
5
6 event duv out is valid event;
7 event cov=pk(collected;
8
9 ovm_analysis_port #(xbar_packet) pkt_bdcst_port;
10 ovm_blocking_peekjmp#(xbar_packet,xbar_rcv_monitor) duv_out_validjmp;
11
12 'ovm_component_utils_begin(xbar_rcv_monitor)
13 'ovm_field_int(port_num,OVM_ALL_ON)
14 'ovm_field_object(rcv_pkt,OVM_ALL_ON)
15 'ovm_component_utils_end
16
17 covergroup cov_packet@cov_pkt_collected;
18 packet_src_addr: coverpoint rcv_pkt.src_addr;
19 packet_dest_addr: coverpoint rcv_pkt.dest_addr;
20 packet_data: coverpoint rcv_pkt.data;
21 endgroup
22
23 function new (string name="", ovm_component parent=nu/l);
24 super.new(name, parent);
25 coy_packet = new();
26 pkt_bdcst.....port = new("pkt_bdcst_port", this);
27 duv_out_validjmp = new("duv_out_validjmp", this);
28 endfunction
29
30 task peek(output xbar_packet pkt);
31 @duv_ouUs_valid_event;
32 $cast(pkt, rcv_pkt.clone());
33 endtask
34
35 . include "program-13.17" /I implementation of method runO shown in section 13.7
36 endclass: xbar_rcv_monitor

This program defines class xbar_rcv_monitor derived from class ovm_monitor (line 1).
Field phy-if (line 2) will be initialized to point to the top level interface block that connects
with the transmit port of the DUV Field port_num (line 3) identifies DUV port to which this
monitor is attached. Field rcv_pkt (line 4) holds the latest packet collected from the DUV
transmit interface. Analysis port connector object pkt_bdcst_port (declared on line 9 and ini-
tialized on line 26) is used to broadcast packets collected from the DUV interface. Event
cov_pkCcollected (line 7) activates coverage group coy_packet (declared on lines 17-21 and
initialized on line 25).
Blocking peek interface duv_ouCvalid_imp (declared on line 10 and initialized on line
27) is provided to allow other components to synchronize with the beginning time of when a
new packet is placed by the DUV on its transmit port. Function peekO is defined to block
until event duv_ouUs_valid_event is emitted, and then to return a copy of rcv_pkt. Event
duv_ouUs_valid_event is emitted in task runO after the packet placed by the DUV on its trans-
mit POt1 is collected and stored in rcv_pkt (section 13.7). This interface is implemented as a
blocking interface so that outside components can synchronize by waiting for task peekO to
complete. Also, because of llsing a peek interface, multiple outside components can use this
interface to synchronize with this monitor.
318 Class-Based VE Implementation

Macros are added to this class declaration (lines 12-15) in order to register this class
and its field with the OVM factory. include predefined methods of OVM classes (line 12)
and to also allow fields in this class to participate in OVM field automation. No hierarchy
exists below this monitor component. As such, task bulldO of this class is not required to be
defined.
Class ovm_monltor does not implement protocol-specific behavior, and therefore, the
run phase behavior of this monitor should be implemented in the predefined task runO of
class ovm_monltor. The statement on line 35 includes that implementation (program 13.17) in
this class.

13.6.1.2 Receive Sequencer


The sequencer component (figure 13.1) contains the blocking peek port connector object
duv_ouCvalid_port connected to imp connector object duv_out_valid_imp in the monitor com-
ponent. This transaction interface is used by sequences running in the sequencer to synchro-
nize to the arrival of a new packet on a DUV output, as detected by the monitor. The
sequencer also contains sequence item interface seq_item_cons_if that is used by the driver to
receive the packets generated by the sequencer.
The implementation of receive sequencer is shown in the following:

'Program 13.7: Receive sequencer


1 class xbar rcv sequencer extends ovm sequencer:
2 int unsigned port_num: -
3 virtual xbar_duv_xmUf phLif:
4
5 ovm_blocking_peek_port#(xbar_packet) duv_out_valid_port:
6
7 'ovm_sequencer_utils_begin(xbar_rcv_sequencer)
8 'ovm_fieldJnt(port_num,OVM_ALL_ON)
9 'ovm_sequencer_utils_end
10
11 function new (string name='''', ovm_component parent=null):
12 super.new(name, parent):
13 . ovm _update_sequence _lib_andJtem(xbar_rcv_packet)
14 duv_out_valid_port = new("duv_out_valid_port", this):
15 endfunction: new
16 endclass: xbar_rcv_sequencer

This program defines class xbar_rcv_sequencer derived from class ovm_sequencer (line
1). Field port_num (line 2) identifies the DUV port to which this sequencer is attached. Vir-
tual interface phy_if is included to allow sequences running in this sequencer to access the
DUV physical interface (line 3). Blocking peek interface duv_out_valid_port (declared on line
5 and initialized on line 14) will be connected to the blocking peek interface of the receive
monitor (program 13.6 line 10) when building the agent containing this sequencer (program
13.10). This interface is used with reactive sequences (section 14.6) to synchronize to the
time when a new packet appears on the DUV transmit pOli. No hierarchy exists below this
sequencer, therefore, task build() of this class is not defined in this program.
The begin/end variation of OVM macro ovm_sequencer_utils() (section 8.2.2) is used to
register class xbar_rcv_sequencer with the OVl\1 factory. and to also create the sequence
library container for this sequencer (line 7-9). Any sequence added to this sequencer by
Building the Hierarchy 319

using macro ovm_sequence_utils() (e.g., program 13.19 line 2) is placed in the sequence
I ibrary created in this step.
DVM macro ovm_update_sequence)ib_and_item() is used (line 13) to specify the default
sequence item for this sequencer and to also initialize the sequencer with the needed
sequencing infrastructure. The use of this macro is mandatory.
Note that the implementation of class xbar_rcv_sequencer does not include the sequence
item interface port seq_item_cons_if since this object is included by default in class
ovm_sequencer.

The transaction type generated by the sequencer has a type of xbar_packet. In the receive
direction, this packet contains parameter waiCcycle which instructs the slave driver on how
many cycles to wait before accepting a packet from the DUV. In the transmit direction, the
packet produced by the sequencer is transmitted by the driver into a DUV receive port.
The default behavior of the receive and transmit sequencers is to execute sequence
ovm_simple_sequence a number of times defined by field count, where each execution of this
sequence generates one sequence item whose type is given by the default sequence type of
the sequencer. This means that class xbar_packet cannot be used directly as the default
sequence item type for either the receive or the transmit sequencers if the default behavior of
the sequencers is to be used. The reason is that the default implementation of xbar_packet
(program 13.3) does not include information on which port it is being generated on. It is,
however, required that the source address of a packet generated by the transmit sequencer be
set to the local port number, and the destination address of a packet generated by the receive
sequencer to be set to the local port number. Because of this requirement, the default
sequence item type for the receive sequencer is set to class xbar_rcv_packet shown below:

iprogram 13.8: Receive sequencer transaction type


1 class xbar_rcv_packet extends xbar_packet;
2 rand int port_num;
3
4 'ovm_object_utils(xbar_rcv_packet)
5
6 constraint valid_des,-addr {dest_addr == port_num;}
7
8 function new(string name="xbar_rcv_packet");
9 super.new(name);
10 endfunction: new
11
12 function void pre_randomize();
13 xbar_rcv_sequencer sqnsr;
14 $cast(sqnsr, get_sequencer(});
15 port_num = sqnsr.port_num;
16 endfunction
17 endclass: xbar_rcv_packet

Class xbar_rcv_packet is derived from xbar_packet and includes field por,-num. The goal
is to have the value of port_num set to the local port number before randomization of this
packet is started so that this field can be used to constrain the destination address of this
packet to the current port during randomization (line 6). Function pre_randomize() (section
10.5.3) is used to set the \'alue of field port_num before randomization is started. Function
geCsequencer() of class ovm_sequence_item (the parent class of xbar_packet) is used in
320 Class-Based VE Implementation

pre_randomlze() (line 14) to set field porcnum to the port number of the sequencer that is gen-
erating this packet type.
In the transmit direction, the default sequence item type is set to xbar_xmCpacket which
is implemented similar to xbarJcv_packet except that constraint on line 6 is replaced with a
constraint setting the value of src_addr to field porCnum.

13.6.1.3 XBar Driver


The default implementation of class ovm_driver contains sequence item interface
seq_ltem_prod_1f which should be connected to the sequencer so that the driver can receive
sequence items from the sequencer. The driver component (figure 13.1) also contains vir-
tual interface phy_if which during hierarchy construction is initialized to point to the top level
interface block that connects with the transmit port of the DUV.
The implementation of receive driver is shown below:

iprogram 13.9: Receive driver


1 class xbar rcv driver extends ovm driver;
2 int unsigned port_num; -
3
4 virtual xbar_duv_xmUf phyjf;
5
6 'ovm_component_utils_begin(xbar_rcv_driver)
7 'ovm_fieldjnt(port_num,OVM_ALL_ON)
8 'ovm_component_utils_end
9
10 function new (string name="", ovm_component parent=null);
11 super.new(name, parent);
12 endfunction: new
13
14 . include "program-13.18" /I implementation of method runO shown in section 13.8
15 end class: xbar_rcv_driver

This program defines class xbarJcv_driver derived from class ovm_driver (line 1). Field
port_num (line 2) identifies the DUV port to which this driver is attached. Field phyjf (line 4)
will be set to point to the top level interface block that connects with the transmit port of the
DUV. Sequence item producer interface seq_item_prod_if which is a default field of class
ovm_driver is used to receive sequence items from the sequencer.

Macros are added to this class declaration (lines 6-8) to include predefined methods of
the OVM classes (line 6) and to also allow fields in this class to participate in OVM field
automation (line 7). No hierarchy exists below this monitor component. As such, task bulidO
of this class is not redefined.
The run phase behavior of this monitor is implemented in the predefined task run() of
class ovm_drlver. The statement on line 35 includes that implementation (program 13.18) in
this class.
Building the Hierarchy 321

13.6.1.4 XBar Agent Top Level Component


The agent component (figure 13.1) is the container for the driver, sequencer, and the monitor,
and also has an analysis port pkt_bdcst_port that is used to expose the monitor analysis port
pkCbdcst_port to the higher layer components.

The implementation of receive agent is shown below:

'Program 13.10: XBar receive agent


1 class xbar_rcv_agent extends ovm_agent;
2 protected ovm_active_passive_enum is_active = OVM_ACTIVE;
3 protected int porCnum = 0;
4
5 xbar rcv monitor monitor;
6 xbar=rcv=driver driver;
7 xbarJcv_sequencer sequencer;
8
9 'ovm_component_utils_begin(xbar_rcv_agent)
10 'ovm_field_enum(ovm_active_passive_enum, is_active, OVM_ALL_ON)
11 'ovm_field_int(port_num,OVM_ALL_ON)
12 'ovm_component_utils3nd
13
14 function new (string name="", ovm_component parent=nu/l);
15 super.new(name, parent);
16 endfunction: new
17
18 virtual function void build();
19 super.build();
20
21 set_config_int("monitor", "port_num", port_num);
22 $cast(monitor, create_component("xbar_rcv_monitor", "monitor"));
23 monitor.build();
24
25 if(is_active == OVM_ACTIVE) begin
26 seCconfig_int("sequencer", "port_num", port_num);
27 $cast(sequencer, create_component("xbar_rcv_sequencer", "sequencer"));
28 sequencer.buildO;
29
30 /I connect peek port between sequencer and monitor
31 sequencer.duv_out_valid_port.connect(monitor.d uv_ out_valid jmp);
32
33 set_config_int("driver", "porCnum", port_num);
34 $cast(driver, create_component("xbar_rcv_driver", "driver");
35 driver.buildO;
36
37 driver.seq_item_prod_if.connecUf{sequencer.seqjtem_cons_if);
38 end
39 endfunction: build
40
41 function void assign_vi{virtual interface xbar_duv_xmUf phy_if);
42 monitor.phyjf = phyJf;
43 if (is_active == OVM_ACTIVE) begin
44 driver.phyJf = phyjf;
45 sequencer.phLif = phyj;
46 end
47 endfunclion: assign_vi
48 endclass: xbar _rcv _agent
~------------

This program detines class xbar_rcv_agent derived from class ovm_agent (line 1). Field
is_active (line 2) indicates whether this is an acti\e or passive agent. Field port_num (line 3)
identifies DUV port to \\hich this monitor is attached.
322 Class-Based VE Implementation

The purpose of this class is to instantiate the monitor, driver, and the sequencer, and to
make the necessary connections between these components. Task build() is redefined in this
implementation (lines 18-39) to build the hierarchy rooted at this component.
The creation of each component follows a three step process:
• Specifying configuration settings for the component
• Creating an instance of the component
• Calling task build() of component to build its sub-hierarchy
The monitor is created first. Before creating an instance of the monitor (line 22), config-
uration function set_config_intO is used to set field port_num of the monitor to field port_num
of the agent. Task buildO of the monitor is called after the monitor is created (line 23). Note
that this task is called even though there is no hierarchy below the monitor component. This
is recommended, since later changes may require adding sUb-components to the monitor.
A passive agent does not contain the driver and the sequencer. As such, these compo-
nents are created only if the agent is active (line 25). The sequencer is created (lines 26-28)
with the same flow as that of the monitor. Blocking peek interface used for synchronization
between the sequencer and monitor is initialized by connecting blocking peek port object
duv_ouCvalid_port of the sequencer to the blocking peek imp object duv_ouCvalid_imp of the
monitor (line 31).
Driver is created (lines 33-35) with the same steps as that of the monitor. The sequence
item interface connector objects in the driver and sequencer are connected after both are cre-
ated (line 37).
This class also defines function assign_vi() which is used by higher layer components to
initialize the virtual interfaces in this agent (lines 41-47). As such, field phy-if of driver and
monitor are not set when building the agent, since these fields will be initialized procedurally
after the environment hierarchy is built.

13.6.2 XBar Transmit Agent


The implementation of XBar transmit agent xbar_xmt_agent, is similar to the implementation
for XBar receive agent discussed in the previous section, and is not shown in this text.

13.6.3 XBar Interface VC


Figure 13.2 shows the architecture of an XBar interface verification component. This com-
ponent contains an instance of XBar receive agent and an instance of XBar transmit agent.
The implementation of XBar interface verification component is shown below:
~--------------------
~rogram 13.11: XBar interface verification component
1 class xbaUvc extends ovm env:
2 protected int unsigned-port_num = 0:
3 protected ovm_active_passive_enum is_active =OVM_ACTIVE:
4
5 xbar_xmt_agent xmt_agent:
6 xbar_rcv_agent rcv_agent:
7
Building the Hierarchy 323

XBar Interface VC (xbaUvc)

XBar RCV Agent (xbar rcv agent)

{JP"--~ I

XBar XMT Agent (xbar_xmt agent)

~ p"-",,,,-,,,,,
Figure l3.2 XBar Interface VC Architecture

8 'ovm_component_utils_begin(xbaUvc)
9 'ovm_field_int(port_num,OVM_ALL_ON)
10 'ovm_field_enum(ovm_active_passive_enum, is_active, OVM_ALL_ON)
11 'ovm_component_utils_end
12
13 function new(string name="", ovm_component parent = null);
14 super.new(name, parent);
15 endfunction: new
16
17 function void buildO;
18 super.buildO;
19
20 set_config_int("xbar_xmt_agentO", "port_num", port_num);
21 set_config_int("xbar_xmt_agento", "is_active", is_active);
22 $cast(xmt_agent, create_ component("xbar_xmt_agent". "xba r_xmt_agentO"»;
23 xmCagent.buildO;
24
25 set_config_int("xbar_rcv_agentO", "port_num", port_num);
26 set_config_int("xbar_rcv_ agento" , "is_active" , is_active);
27 $cast(rcv_agent, create _ component("xbar_rcv_agent", "xbar_rcv_ agentO"»;
28 rcv_agent.buildO;
29 endfunction: build
30
31 function void assign_vi(virtual interface xbar_duv_xmUf xmt_phyj
32 virtual interface xbar_duv_rcv_if rcv_phLif);
33 xmt_agent.assign_vi(rcv_phyJf);
34 rcv_agent.assign_vi(xmt_phyJf);
35 endfunction: assign_vi
36 endclass: xbaUvc

This program defines class xbar_ivc derived from class ovm_env (line 1). Field is_active
(line 2) indicates whether this is an active or passive verification component. Field porCnum
(line 3) identifies the DUV port to which this component is attached.
The creation of each component follows the same three-step process outlined for creat-
ing the components in the receive agent (section 13.6.1.4). Component xmt_agent is created
(line 22) after its relevant configuration settings are made (lines 20, 21), followed by calling
task build() of this component. The receive agent is created using the same process (lines
25-28).
324 Class-Based VE Implementation

This class also defines function asslgn_vlO which is used by higher layer components to
initialize the virtual interfaces in this verification component (lines 31-35). This function is
implemented by calling function asslgn_vl() of receive and transmit agents.

13.6.4 XBar Verification Environment


Figure 13.3 shows the architecture of the top level component of the XBar verification envi-
ronment. This component contains four xbar_lvc components, each interacting with one port
of the XBar design. This component also contains four xbar_scoreboard components. Each
scoreboard listens to packets broadcasted by all transmit monitors and places packets des-
tined to its port address on its queue. Each scoreboard listens to packets broadcasted by the
receive monitor attached to its local port and matches incoming packets against packets in its
queue (section 13.10).

Figure 13.3 XBar Verification Environment Architecture

The implementation of the XBar verification environment top level is shown below:
I
.Program 13.12: XBar verification environment top level
1 class xbar sve extends ovm env'
2 'ovm~component_utils(xba;_sve)
3 int unsigned num_ports=4:
4
5 xbaUvc ivcs[):
6 xbar_scoreboard1 sbs [):
7
8 function new (string name='''', ovm_component parent=null):
9 super.new(name, parent):
Building the Hierarchy 325

10 endfunction
11
12 virtual function void buildO;
13 string inst_name;
14 super.buildO;
15
16 ivcs = new[num_portsJ;
17 sbs = new[num_portsJ;
18 for (int i=O; i< num_ports; i++) begin
19 $sformat(inst_name, "xbaUvc[%Oq]", i);
20 set_config_int(insCname, "port_num", i);
21 $cast(ivcs[iJ, create_component("xbaUvc", inst_name»;
22 ivcs[iJ.build();
23
24 ivcs[iJ.assign _ vi(xbaUestbench.xbar_ duv_ xmti,
25 xbaUestbench.xbar_duv_rcvi);
26
27 $sformat(inst_name, "xbar_sb[%Od]", i);
28 set_config_int(inst_name, "port_num", i);
29 $cast(sbs[i], create_ component("xbar_scoreboard 1", inst_name»;
30 sbs[iJ.buildO;
31 end
32 for (int i=O; i< numJ)orts; i++) begin
33 ivcs[iJ.rcv_ agent.monitor.pkt_bdcsCport.connect( sbs[iJ.rcv_listeneUmp);
34 for (intj=O; j< 4; j++)
35 ivcs[iJ.xmt_agent.monitor.pkCbdcst_port.connect(sbs[j].xmUisteneUmp);
36 end
37 endfunction: build
38 endclass: xbar_sve

This program defines class xbar_sve derived from class ovm_env (line 1). This compo-
nent contains field nurn_ports (line 3) which controls the number of ports supported by this
environment. Dynamic arrays ivcs and sbs (lines 5, 6) hold objects corresponding to
instances of xbar_ivc and xbar_scoreboard components; one for each port.
The :virtual interface in the receive and transmit agents of each ivcs are initialized by
calling function assign_vi() of the interface verification component with the appropriate inter-
face block as argument (line 24).
Each scoreboard contains two listener ports that should be connected to the broadcast
analysis ports of each ivcs component according to the diagram in figure 13.3. These connec-
tions are made by calling function connect() of the analysis port objects (lines 32-36). The
implementation of the scoreboard components is shown in section 13.10.

13.6.5 XBar Tests


A testcase represents a specific customization of a verification environment for a desired
behavior. In the DVM class library, class ovm_test is intended to represent a testcase. Differ-
ent testcases are modeled as classes derived from ovm_test. A library oftestcases can be cre-
ated by defining multiple classes, each containing an instance of the verification
environment with specific configurations required for carrying out the desired verification
scenario.
The following program shows the implementation of a testcase for the XBar design:
326 Class-Based VE Implementation

Iprogram 13.13: XBar default testcase


1 class xbar test default extends ovm test;
2 'ovm-='component_utils(xbaUesCdefault)
3
4 xbar_sve xbar_sve;
5
6 function new(strlng name = "xbar_base_test", ovm_component parent=null);
7 super.new(name, parent);
8 endfunction: new
9
10 virtual function void buildO;
11 super.buildO;
12 $cast(xbar_sve, create_component("xbar_sve", "xbar_sveD"));
13 xbar_sve.buildO;
14 endfunction: build
15
16 task runO;
17 #200000;
18 globaLstop_requestO;
19 endtask: run
20 endclass: xbar_test_default

This example shows the implementation oftestcase xbar_test_default that only creates an
instance ofxbar_sve, the top level component of the verification environment, without modi-
fying any of its default settings. Task run() of this test provides a simple stop mechanism
where the test is terminated after a fixed length of time (line 16-19). Section 7.4.2 describes
the infrastructure provided by the OVM class library for supporting more elaborate require-
ments for ending the run phase ofthe simulation.

The following example shows the implementation of a new testcase derived from class
xbaUesCdefault that creates an environment with passive transmit agents and active receive
agents:

Iprogram 13.14: XBar testcase with passive transmit agents


1 class xbar_test_passive_xmt extends xbar_test_default;
2 .ovm _component_utils(xbar_test_passive_ xmt)
3
4 function new (string name = "xbar_base_test", ovm_component parent=null);
5 super.new(name, parent);
6 endfunction: new
7
8 virtual function void buildO;
9 set_configjnt ("*.xbar_xmt_agentO","is_active",OVM_PASSIVE):
10 super.buildO;
11 endfunction: build
12 endclass: xbar_tesCpassive_xmt

Testcase xbaUest_passive_xmt is derived from class xbaUesCdefault (line 1). This


derived class does no redefine task runO of xbaUesCdefault, and therefore, the run behavior
for this test is the same as class xbaUest_default. Task build() of this class is, however, rede-
fined to re-configure all transmit agents in the sub-hierarchy rooted at this component before
calling task buildO of its parent class (i.e., task buildO of class xbaUest_default). The configu-
ration setting is done by calling function set_config_intO to set to OVM_PASSIVE field
agenUs_active of all components in the sub hierarchy rooted at this component whose names
end with string" .xbar_xmt_agentO". The priority scheme for applying configuration settings
Building the Hierarchy 327

(section 7.2) guarantees that the configuration setting specified at this level overrides any
settings made in the lower layers of the hierarchy.

The use of global function run_testO to start a previously defined testcase is shown in
the next section.

13.6.6 XBar Testbench


Figure 13.4 shows the architecture of the XBar testbench. This architecture consists of top
level module block xbaUestbench. This top level component contains the following objects:
• An instance ofxbar_wrapper
• An instance ofxbar_tesUauncher
• Clock or reset generation facilities
Module block xbar_wrapper contains the XBar design and its interfaces (section 12.4). A
module block is used for implementing xbaUesUauncher, which is used to create an instance
of a testcase and start the execution of simulation phases. An example is shown below:
I
.Program 13.15: XBar testlauncher block
1 module xbaUesUauncherO;
2 /I first include all class defined so far
3 'include "top.svh"
4
5 1/ use function run_testO to allocate and start a previously defined testcase
6 initial begin
7 run _test("xbar_test_passive_xmt");
8 end
9 endmodule

In the above example, the implementation of all classes is first included (line 3). An ini-
tial block is then used to call global function run_testO with the name of a previously defined
testcase. Section 7.4.1 describes the infrastructure provided by the OVM class library for
selecting a test and starting the run phase of the simulation.
The implementation of xbaUestbench is shown below:

IProgram 13.16: XBar testbench


1 module xl>aUestbeneh();
2 reg elk, reset;
3
4 xbar_duv_xmUf xbar_duv_xmti(reset, elk);
5 xbar_duv_rcv_if xbar_duv_revi(reset, elk);
6 xbar_duvjnternaUf xbar_duvjnti(reset, elk);
7
8 xbar_duv_wrapper duvw(.*);
9 xbaUestJauncher mt();
10
11 always #5 elk = -elk;
12
13 initial begin
14 reset = 1'bO; #0 reset = 1'b1; elk = 1'b1: #5~ reset = 1'bO;
15 end
16 endmodule
328 Class-Based VE Implementation

xbaUestbench (module block)

xbauesUauncher (module block)

Initial block
Irun_Iest(xbauesIO)

Figure 13.4 XBar Testbench

The implementation of xbar_testbench contains instances of xbar_duv_wrapper, inter-


faces to be connected to this instance (lines 4-6), and an instance of xbar_tesUauncher. This
block also includes the necessary code for clock generation (line 11) and reset signal genera-
tion (line 14).

13.7 Monitor Run Phase


The implementation of the monitor run phase in class-based implementation is shown in the
following program:

'Program 13.17: XBar receive monitor Run Phase (included in program 13.6)
1 task runO;
2 forever begin
3 II wait for duv to indicate output is valid
4 @(posedge phy_if.clk iff phyjf.outv[port_numj === 1);
5 =
rcv_pkt newO;
6 phLif.get_data_addr(port_num, rcv_pkt.data, rcv_pkt.src_addr);
7 rcv_pkt.dest_addr = port_num;
8
9 lIaliow function peekO (program 13.6) to complete when valid packet appears
10 -> duv_ouUs_valid_event;
11
12 II wait for outside agent to indicate packet was read
13 if(phyjf.outr[port_numj !== 1'b1)
14 @(posedge phyJoutr[porCnumj):
15 @(negedge phy3.clk);
16
17 lIactivate coverage group COy _packet
18 -> cov_pkt_collected;
19
20 /1 broadcast packet after DUV receives acknowledgement that it was accepted
21 pkt_bdcst_port.write(rcv_pkt);
Driver Run Phase 329

22 end
23 : endtask: run
~--------------------
This implementation emits event duv_ouUs_valid_event when the DUV places a new
packet on its transmit port. Emitting this event allows task peek() of the monitor (program
13.6) to complete, therefore allowing all components blocked on this task to synchronize to
this condition detected by the monitor. Note that the monitor is unaware components, if any,
that are blocked and waiting for this condition to be detected.
Event cov_pkCcoliected is emitted (line 18) after the monitor detects that the DUV has
received acknowledgement that the packet placed by the DUV on its transmit interface was
accepted by the outside agent (i.e., the driver in the receive agent in active mode, and a
design component in passive mode), thereby activating coverage group coy_packet. After
emitting this event, the monitor uses analysis port pkt_bdcsCport to broadcast the packet it
collected from the DUV transmit port (line 21). The monitor is unaware of components, if
any, that use the packets it broadcasts.
The implementation for the transmit agent monitor is similar to the one for receive
agent monitor.

13.8 Driver Run Phase


The core function of receive and transmit agent drivers are defined using tasks. The proto-
types for these tasks in the agent receive driver were shown in the class declaration of the
driver (program 13.9). Task run() of class xbar_rcv_driver is automatically called as part of
executing the OVM predefined simulation phases (section 7.4) and as such, provides an
entry for the driver function to get started. The implementation for the receive agent driver is
shown below:

"Program 13.18: XBar Driver Run Phase (included in program 13.9)


1 task runO;
2 fork
3 get_packet_from_sequencer_and_drive(port_num):
4 reset_signalsO;
5 join
6 endtask: run
7
8 task geCpackeUrom_sequencer(output xbar_packet pkt);
9 ovm_sequenceJtem item;
10 seqJtem_prod_if.get_nexUtem(item);
11 $cast(pkt, item);
12 endtask: get_packeCfrom_sequencer
13
14 task get_packeUrom_sequencer_and_drive(int port);
15 @(negedge phyJf.reset)
16 forever begin
17 xbar_packet rcv_pkt;
18 getJlackeUrom_sequencer(rcv_pkt);
19 collect_packet(port, rcv_pkt);
20 seqJtem_prod_if.item_done(rcv_pkt);
21 end
22 endtask: get_packeUrom_sequencer_and_drive
330 Class-Based VE Implementation

23
24 task collect_packet(int port, xbar_packet pkt);
25 assert(phy-if.outr[port] == 1'bO);
26 @(posedge phLif.clk iff phLif.outv[port] === 1'b1);
27 repeat (pkt.wait_cycle) @(posedge phLif.clk);
28 phy].outr[port] <= 1'b1;
29 pkt.dest_addr = port;
3D phy_if.get_data_addr(port, pkt.data, pkt.src_addr);
31 @(negedge phyJclk);
32 phy].outr[port] <= 1'bO;
33 endtask: collect_packet
34
35 task xbar_rcv_driver::reset_signalsO;
36 forever @(negedge phLif.reset) phLif.outr =1'bO;
37 endtask: reset_signals

An important aspect of the above implementation is the mechanism for this driver to
receive packets from the receive sequencer. The driver uses field wait_cycle of the packet it
receives from the sequencer to decide how many cycles to wait before collecting the next
packet from the DUV transmit port (line 27). The interaction between the driver and the
sequencer is shown in function geCpacket_from_sequencer (lines 8-12). The driver first calls
task geCnexUtem() of its sequence item interface connector secl.Jtem_prod_if (line 10). Once
the driver has completed driving the next packet from the DUV transmit port, it informs the
sequencer that it has completed processing the previous item by calling function item_doneO
of seq_item_prod_if (line 20). Sequence item interface connector seq_item_prod_if is linked
with the sequencer when building the receive agent component (program 13.1 0 line 42).

13.9 Sequencer
The implementation of sequencers, their interaction with the driver, and their associated fea-
tures are described in detail in chapter 14. However, a simple example is shown in this sec-
tion on how to override the default behavior of sequencer for the receive agent sequencer.
This implementation is shown below:
I
Program 13.19: Overriding default behavior for receive sequencer
1 class xbar_rcv_seq_idle extends ovm_sequence;
2 'ovm_sequence_utils(xbar_rcv_seqjdle, xbar_rcv_sequencer)
3
4 function new(string name="", ovm_object parent=null);
5 super.new(name);
6 endfunction
7
8 virtual task body();
9 'message(OVM_INFO, ("idle rcv sequence"»
10 endtask
11 endclass: xbaucv_seqjdle
12
13 class xbar_tesUdle_rcv extends xbar test default;
14 .ovm_component_utils(xbar_test_idl9_rcv)
15
16 function new(strif1g name = "xbar_base_test", ovm_component parent=null);
17 super.new(name, parent);
18 endfunction: new
19
Scoreboarding 331

20 virtual function void buildO:


21 set_config_string("· .xbar_rcv_agentO.rcv_sqnsr",
22 "default_sequence", "xbar_rcv_seq_idle");
23 super.build();
24 endfunction: build
25 end class: xbaUest_passive_xmt

In this example, a new sequence xbar3cv_seq_idle is first defined where the body of the
sequence (lines 8-10) is defined so that no sequence item is generated. A new testcase
xbaUesUdle3cv is created by deriving from xbaUesCdefault (line 13). The default sequence
started by sequencers in receive agents is then changed using function secconfig_stringO to
set field default_sequence of any sequencer whose instance name matches string
..·.xbar_rcv_agentO.rcv_sqnsr.. to xbar3cv_seq_main (lines 21-22). The buildO method of parent
class is then called (line 23) which creates the verification environment as described in
xbaUesCdefault (program 13.13). Testcase xbaUesUdle_rcv can then be run using global
function run_test(xbaUesUdleJcv) (program 13.15 line 7).

13.10 Scoreboarding
The xbar scoreboard, as described in figure 13.3, is defined to have two analysis imp connec-
tor objects for listening to packets broadcasted by transmit and receive monitors. The com-
plication with requiring two analysis imp connector objects in the same class object is that a
single class declaration cannot contain two standard analysis imp objects since a class decla-
ration can contain only one function writeO, and each standard analysis imp connector object
requires a dedicated function write() (section 9.5).
Approaches for implementing this behavior include:
• Defining new analysis imp types thereby allowing two analysis imp objects to coexist
in the same component
• Using a subscriber component as a subcomponent of the scoreboard, that listens on
each of the incoming analysis interfaces of the scoreboard
These approaches are described in the following sections.

13.10.1 Implementation with User Defined Analysis Imp Types


The OVM class library provides the ovm_iF_decIO macros (table 9.5 on page 228) for defin-
ing new imp connector object types that use a modified method name, thereby allowing mul-
tiple imp connector objects to be instantiated in the same component (section 9.6). The
implementation of xbar_scoreboard, shown below, makes use of this approach for including
two imp connector objects in the scoreboard that listen to packets broadcasted by receive and
transmit agents in the interface verification components.

I Program13.20: XBar scoreboard


1 . ovm_analysis_imp_declLxmt)
2 'ovm_analysis_imp_decILrcv)
3
332 Class·Based VE Implementation

4 class xbar scoreboard extends ovm scoreboard;


5 int unsigned port_num; - .
6 xbar_packet pkCqueue[$];
7
8 ovm_analysis_imp_xmt #(xbar_packet, xbar_scoreboard) xmUistenerJmp;
9 ovm_analysisJmp_rcv #(xbar_packet, xbar_scoreboard) rcv_listenerJmp;
10
11 'ovm_component_utils_begin(xbar_scoreboard)
12 'ovm_field_int(port_num, OVM_ALL_ON)
13 'ovm_field_queue_object(pkl_queue,OVM_ALL_ON)
14 'ovm_componenCutils_end
15
16 function new (string name, ovm_component parent);
17 super.new(name, parent);
18 =
xmUistenerjmp new("xmUistenerjmp", this);
19 =
rcv_listener_imp new("rcvJistenerJmp", this);
20 endfunction
~1
22 virtual function void write_xmt(inpul xbar-packet t);
23 ==
if (t.dest_addr port_num) insert(I);
24 endfunction
25
26 virtual function void write_rcv(input xbar_packet t);
27 ==
If (t.dest_addr port_num)
28 match(t);
29 . else
30 'message(OVM_INFO, ("rcv packet dest_addr does not match port_num"»
31 endfunction
32
33 function void insert(xbar_packet pkt);
34 II Implementation not shown
35 endfunction
36
37 function void match(xbar_packet pkt);
38 II Implementation not shown
39 endfunction
40 end class

In this implementation, macro ovm_analysis_imp_decIO is used to declare analysis imp


connector object types ovm_analysis_imp_xmt and ovm_analysls_impJcv (lines 1, 2). These
new types are then used to create imp connector objects xmUlstener_imp and rev_listener_imp
(lines 8, 9), Functions write_xmtO (lines 22-24) and write_revO (lines 26-31) are then defined
for imp connector objects xmUisteneUmp and rev_listenerJmp respectively.
With this implementation, a call to function write() of an analysis port connection object
connected to xmUistenerJmp results in calling function write_xmt(), and a call to function
wrlteO of an analysis port connection object connected to rev_listener_imp results in calling
fUllction writeJev(). With this behavior, and given the connectivity shown in figure 133,
packets broadcasted by the receive agent are passed to function wrlte_rcv() and packets broad-
casted by the transmit agent are passed to function write_xmt(). The implementation of these
functions then uses functions insertO (lines 33-35) and matchO (lines 37-39) of the score-
board to carry out the necessary scoreboarding activities.

13.10.2 Implementation with Listener Subcomponents


The predefined class ovm_subscriber to create subcomponents inside the scoreboard that lis-
ten on the incoming analysis interfaces. Class ovm_subserlber is derived from class
Scoreboardlng 333

ovm_component, and contains a predefined analysis imp object. In this approach, two compo-
nents of type ovm_subscrlber are placed inside class ovm_scoreboard. Each ovm_subscriber
object contains a reference to its parent scoreboard, and function write() in each
ovm_subscriber object can be redefined to perform the required operation on the scoreboard
in the parent component. Since a single class object can contain multiple export connector
objects, the analysis imp connector in each ovm_subscrlber object can then be routed to com-
ponents outside xbar_scoreboard through analysis export connectors placed inside the
xbar_scoreboard object. .

xbar_scoreboard

MATCH xbar rev listener


analysis_export ~c---<

Queue
xbar xmt listener
INSERT analysis_export ~,-----< xmUistenerJmp
'--'--'-----1-

Figure 13.5 XBar Scoreboard Architecture

Figure 13.5 shows the internal architecture of the XBar scoreboard component. The
implementation of this scoreboard contains two analysis export connectors rcv_listener_imp
and xmUisteneUmp of type ovm_analysis_export (note that these objects are export objects
but are named as imp objects since they appear as imp objects to outside components). These
connector objects are connected to the analysis imp objects inside ovm_subscriber compo-
nents. Function wrlte() of xbar_xmUlstener block derived from ovm_subscriber is defined to
place the incoming packet in the scoreboard queue if the packet destination address matches
the port number for this scoreboard. Function write() of xbarJcv_listener component derived
from ovm_subscriber is defined to match the incoming packet against the contents of the
scoreboard queue. Connector objects rcv_listeneUmp and xmUisteneUmp of xbar_scoreboard
are named as imp objects even though these are in fact export connector objects. The reason
is that these connector objects appear as analysis imp connectors to components outside the
scoreboard component.
The implementation ofxbar_xmUlstener component is shown in the following:

I Program 13.21: XBar xmt listener component


1 class xbar_xmUistener extends ovm_subscriber #(xbar_packet);
2 xbar_scoreboard sb;
3
4 'ovm_component_utils_begin(xbar_xmUistener)
5 'ovm_field_object(sb,OVM_ALL_ON)
6 'ovm_component_ulils_end
7
8 function new(string name, ovm_component parent);
9 super.new(name,parent);
10 endfunction
11
12 virtual function void write(input xbar_packet t);
334 Class-Based VE Implementation

13 if (t.desCaddr == sb.port_num) sb.insert{t);


14 endfunction
15 endclass

Class xbar_xmCIIstener is derived from ovm_subscriber and is specified to be listening to


transactions of type xbar_packet (line 1). Field sb is a reference to the scoreboard that con-
tains this component and is initialized by the scoreboard when the scoreboard hierarchy is
being built. Function writeO is defined to call function insertO of the scoreboard if the destina-
tion address of the incoming packet matches the port number for this scoreboard (line 13).
This function places the incoming transaction in the ordered list of transactions that are
expected to be received at this port. Note that it is not required to create an instance of an
analysis imp connector since class ovm_subscriber by default contains connector
analysis_export of type ovm_analysis_imp.

The implementation of the xbar_rcv_iistener is similar to the implementation of


xbar_xmUistener except that its function write() is defined to call function match() of the
scoreboard with the incoming packet.
The implementation ofxbar_scoreboard is shown in the following:

'Program 13.22: XBar scoreboard


1 class xbar scoreboard extends ovm scoreboard;
2 int unsigned porCnum; -
3 xbar_packet pkt_queue[$l;
4
5 .ovm_ componenCutils_ begin(xbar_scoreboard)
6 'ovm_field_int(port_num, OVM_ALL_ON)
7 'ovm_field_queue_objeet(pkt_queue,OVM_ALL_ON)
8 'ovm_component_utils_end
9
10 ovm_analysis_export #(xbar_packet) xmUisteneUmp;
11 ovm_analysis_export #(xbar_packet) rcvJisteneUmp:
12
13 xbar_xmUistener xmIJistener;
14 xbar_rcvJistener rev_listener;
15
16 function new (string name, ovm_component parent);
17 super.new{name, parent);
18 endfunction
19
20 virtual function void build{);
21 super.build();
22
23 xmUisteneUmp = new("xmUisteneUmp", this);
24 rcv_listeneUmp = new{"rcvJisteneUmp", this);
25
26 set_confi9_object{"xmUistener". "sb", this, 0);
27 $cast{xmUistener, create_component{"xbar_xmUistener", "xmUistener"));
28 xmUistener.build{);
29
30 set_confi9_object{"rcvJistener", "sb", this, 0);
31 $cast{ rcv _listener, create _ component("xbar_rcv_listener", "rcvJistener"));
32 revJistener.build();
33
34 xmUistener _imp.connect(xmt_listener.analysis_export);
35 rcvJisteneUmp.connect(rcvJistener.analysis_export);
36 endfunetion: build
37
38 extern function void insert(xbar_packet pkt);
Scoreboarding 335

39 extern function void match(xbar_packet pkt);


40 . end class
~------------------------
This implementation contains components xmUistener and reV_listener (lines 10, 11).
Task build() of xbar_seoreboard is defined to first create connector objects xmUisteneOmp
and rev_listener_imp (lines 23, 24). It then creates object xmUistener (line 27) after using
function seCeonfi9_objeetO to set field sb of xmUistener to point to this scoreboard object
(line 26). Object rev_listener is created using the same process (lines 30-32). In the last step,
connector objects for xbar_seoreboard are each connected to their corresponding listener
component (lines 34, 35). With this implementation, any packet arriving at xmUisteneOmp
is routed to the analysis imp connector of xmUistener, causing function write() of xmUistener
to be called with the incoming packet, which in tum inserts the packet into the scoreboard.
Similarly, any packet arriving at rev_listener_imp is routed to the analysis imp connector of
rev_listener, which in turns matches the incoming packet against the scoreboard.
336 Class-Based VE Implementation
CHAPTER 14 Verification Scenario
Generation

.The DVM class library provides powerful constructs and concepts for modeling verification
scenarios as a sequence of transactions. The concepts and constructs for implementing this
model of a verification scenario is described in detail in chapter 8.
Chapter 13 introduces the XBar crossbar switch as a sample design and showed the
implementation of its verification environment containing only interface verification compo-
nents. This chapter introduces the XBar communication protocol and describes in detail the
implementation of a complete verification environment that uses transaction sequences gen-
erated across multiple layers of interface, module, and system verification components to
verify the features of this communication protocol. Techniques and features of generating
sequences using the DVM class library are described through the presentation of this sample
environment.

14.1._XBar Communication
_ _ _ ·__________ m_. ___· ___ _ Protocol
--------_._----_._----

The XBar communication protocol is based on the single-cycle packet routed through the
XBar crossbar switch (section 12.2).
The following data encapsulations are used in constructing the XBar communication
protocol:
• The XBar Packet is the fundamental item of communication, consisting of:
• Source address (src_addr)
• Destination address (desCaddr)
• Payload (data)
• The XBar Frame defines an abstraction over the XBar packet where values of field
data are mapped into the following object kinds: SOF, BEACON_REQ, BEACON_REPLY,
DATA_REQ_A. DATA_REQ_B. DATA_REPLY_A, and DATA_REPLY_B.
• The XBar Transfer is composed of XBar frames and supports the following types of
transfers across XBar ports:
• Beacon request
338 Verification Scenario Generation

• Beacon reply, which is sent in response to receiving a beacon request


• Data request in either protocol A or B
• Data reply which is sent in response to receiving a data request
The composition of XBar transfers, as well as the data encoding for each XBar frame
kind, are shown in table 14.1. The protocol semantics are as follows:
• Beacon request and beacon reply transfers consist of a single XBar frame.
• Data request and data reply transfers consist of two XBar frames.
• Sending a data request or data reply transfer at a given port must be completed before
another data transfer is started by that port's agent.
• Sending of a beacon request or beacon reply transfer at a port may interleave the two
frames composing a data request or data reply transfer.

Transaction Frame Kinds Packet Data Field Notes


Beacon Request I: BEACON_REQ 4'bOOOO
Beacon Reply I: BEACON_REPLY 4'bOOOI
I: SOF 4'bOO1O
Data Request/Protocol A
2: DATA_REQ A 4'blOO· bit 0 holds the payload
I:SOF 4'bOO1O
I Data Request/Protocol B
2: DATA_REQ_B 4'blOl· bit 0 holds the payload
I:SOF 4'bOOIO
Data Reply/Protocol A
2: DATA_REPLY_A 4'bllO- bit 0 holds the payload
l:SOF 4'bOOIO
Data Reply/Protocol B
2: DATA_REPLY_8 4'blll- bit 0 holds the payload

Table 14.1: XBar Communication Protocol

The XBar communication protocol is defined to facilitate the illustration of the variety
of protocol handling requirements that may exist in real designs, while including as few
low-level details as possible (e.g., the reader may note that the XBar protocol supports only
the transfer of a single bit in each data transfer). The changes required to tum this protocol
into a real-life protocol, however, do not affect the techniques described for verification
related handling of this protocol. For example, in a real protocol, the data field size may be
increased arbitrarily to support larger payloads or the packet definition may be redefined to
occur over multiple cycles, allowing for more complex data exchange scenarios. This added
protocol complexity can be handled by the driver component that drives the packet at the
XBar physical interface.

The XBar communication protocol highlights the following features of a generalized


communication protocol:
XBar packet routing through source and destination addressing
XBar frame assigning protocol-related meanings to packet data values
Composite transfers consisting of multiple frames
Support for multiple protocols A and B. through the same device port
The need for a multi-layer stack for handling (i.e .. generating and collecting) the pro-
tocol. The implementation of the environment supporting the XBar communication
protocol requires a physical layer for driving packets into the DUV ports. a link layer
XBar Transfer and Frame Models 339

for managing beacon request and replies and for abstracting packets into transfers,
and an application layer handling the generation of data request transfers and
responding to data request transfers with data reply transfers.

XBar frames generated by the verification environment and arriving at a DUV receive
port must follow the ordering requirements described for the XBar protocol (e.g., two frames
belonging to a transfer must appear before a frame belonging to a new transfer appears). On
the other hand, XBar frames arriving at a DUV transmit port and collected by the verification
environment may come from any of the other DUV ports. As such, XBar frames arriving at a
DUV transmit port may appear in any order (two SOF frames, coming from two separate
DUV ports, appearing back to back). This behavior affects the monitoring requirements at
the transmit and receive ports of the XBar design.

14.2 XBar Transfer and Frame Models


•• - ••••••••• _ , • • • _ _ _ _ . _ ••• _ _ _ _ _ _ " _ _ , • • • _ _ _ _ _ • • • _ •• _ _ _ _ • • • • • , . _ _ . _ • • • • • _ _ _ ••• _ . _ . _ _ _ _ _ . ' _ , _ 0 •• ". __ • _ _ _ _ _ •••• __ • _ •• , • • • • • • • , , _ _ _ • • • • • _ . _ _ .,.. • • • • • • _, . _ • • _ . . . . . _ _ • _ _ _ • _ _ _. _ . '

The implementation of class xbaUrame is shown in the following program. An XBar frame
is used to provide protocol related meaning to fields of an XBar packet. This class, therefore,
is derived from class xbar_packet:

I Program 14.1: XBar Frame


1 typedef enum {INVALID, SOF, BEACON_REO, BEACON_REPLY, DATA_REO_A,
2 DATA_REO_B, DATA_REPLY_A, DATA_REPLY_B} xbar_packet_klnd;
3
4 class xbar_frame extends xbar_packet;
5 1/ logical fields
6 rand xbar_packe,-kind kind;
7 rand bit payload;
8
9 'ovm_object_utils_begin(xbaUrame)
10 . ovm_field_enum(xbar_packe,-kind,kind,OVM_ALL_ON)
11 'ovm_field_int(payload,OVM_ALL_ON)
12 'ovm_object_utils_end
13
14 constraint c1 {(data[3:1]==3'bOOO) == (kind==SOF);}
15 constraint c2 {(date[3: 1]==3'b001) == (kind==BEACON_REO);}
16 constraint c3 {(data[3:1]==3'b010) == (kind==INVALlD);}
17 constraint c4 {(data[3:1]"=3'b011) == (kind==BEACON_REPLY);}
18 constraint c5 {(data[3:1]"=3'b100) == (kind==DATA_REQ_A);}
19 constraint c6 {(data[3: 11==3'b1 01) == (kind==DATA_REO_B);}
20 constraint c7 {(data[3:1]==3'b11 0) == (kind==DATA_REPLY_A);}
21 constraint c8 {(data[3: 1]==3'b111) == (kind"=DATA_REPLY_B);}
22 constraint c9 {(data[O:O]==payload);}
23
24 I/optionally allow this object to be initialized from the content of an xbar_packet
25 function new(string name="xbar_frameJnst", xbar_packet pkt=null);
26 super.new(name);
27 if(pkt== nUll) return;
28 src_addr = pkt.src_addr; dest_addr = pkt.dest_addr;
29 data = pkl.data; wait_cycle=pkt.wait_cycle;
30 void'(randomize(kind. payload));
31 endfunction: new
32 endclass: xbar_frame
340 Verification Scenario Generation

Class xbaUrame includes logical fields kind and payload that provide a view of this
object as an XBar frame. This class also includes fields data, src_addr, dest_addr, and
wait_cycle since it is derived from class xbar_packet. This class should contain the set of con-
straints necessary for generating random values that are consistent across the set of logical
and physical fields, regardless of which field is constrained. For example, if kind is con-
strained to DATA_REQ_B and payload is constrained to 1, then randomization should set field
data to 4'b1011. Alternatively, if field data is constrained to value 4'b1011, then randomization
should set kind and payload to DATA_REQ_B and 1, respectively (section 11.2.2). Constraints
defined for this class (lines 14-22) implement this behavior. These constraints are used for
initializing this class to the contents of an xbar_packet object. In the constructor for
xbar_frame (lines 24-31), fields src_addr, desCaddr, data, and waiCcycle are initialized from
argument pkt, and then fields kind and payload are assigned by randomizing their value.
Given the simple description of the XBar protocol, no special class is defined for an
XBar transfer. In this implementation, class xbar_frame and its field kind is used to indicate
the type of transfer that is to be sent.

14.3 XBar Sequence Generation Architecture


----.---~-----------

The XBar communication protocol, as outlined in section 14.1, reflects functionality beyond
that described for the XBar crossbar switch introduced in section 12.2. The XBar switch has
the simple function of routing single-cycle packets through XBar ports, subject to handshak-
ing at both the source and destination ports. The XBar communication protocol, however,
reflects the existence of additional hardware layers that depend or act on beacon and data
trans fers.

Application
} - Layer

XBar Module
XBar System

Figure 14.1 XBar Block to XBar System Evolution

Figure 14.1 shows a possible evolution of the XBar design from a block to a system that
supports the XBar communication protocol. XBar block contains only the XBar crossbar
switch. -YBar .modl/le is created by adding a component that requires traffic passing through
the XBar SWitch to f~llo\\" transfer ordering requirements. and beacon request and reply
transfers to be appropriately sent by all ports . ."-Bar sl'sfem is created by adding a component
that requires data reply transfers to be sent back from ports that receiYe data request trans-
XBar Sequence Generation Architecture 341

fers. It should be noted that the integration flow for moving from an XBar block to an XBar
system, as described here, is decided specifically to motivate the need for different sequence
generation techniques described in this chapter.

To other 3 Interface VCs

P3 P2 P1
XBar Block
Figure 14.2 XBar System Verification Environment Architecture

The verification environment architecture for the XBar system level design is shown in
figure 14.2. This architecture reflects the multi-layer implementation of the XBar system. In
full deployment of this environment during system level verification, each verification com-
ponent perfonns the following activities:
• XBar Interface VC:
• Implements the abstraction ofxbar_packet for interacting with the XBar design
through XBar packets.
• During block level verification, randomly generates XBar packets to be trans-
mitted and generates random packets indicating the latency with which packets
should be collected from DUV ports.
• During module and system level verification, acts as the driver of traffic gener-
ated by the module and system VC components.
• Has the necessary scoreboarding structure in place to check for correct transmis-
sion of packets across the XBar switch.
• Can remain in place in passive mode as the real application layer is attached to
the system and continues to scoreboard for packets traveling across the XBar
design.
• XBar Module VC
• Is not present during block level verification.
• Implements the abstraction of XBar transfers composed of XBar frames, and
interacts with interface VC components using the abstraction of XBar frames.
• During module level verification, generates random beacon request transfers,
replies to incoming beacon request transfers, and generates random data request
and reply transfers to any of the interface VC components. Data request and
response frame traffic generated at this level does not comply with the
request/reply requirements for data transfers.
• During system level verification, continues to generate beacon request and bea-
con reply transfers, making beacon transfer handling invisible to system VC
342 Verification Scenario Generation

components. Also provides to system VC component the facilities for sending


data transfers through any of the downstream interface VC components.
• Monitors that incoming and outgoing XBar frames follow the ordering require-
ments of the XBar communication protocol.
• Can remain in place in passive mode when the real application layer is attached
to monitor to check for correct ordering ofXBar frames injected into the DUV
port by the application layer and XBar frames received by the application layer
from the DUV ports.
• XBar System YC:
• Is not present during block and module level verification.
• During system level verification, generates data reply transfers in response to
incoming data request transfers, and generates data request transfers.
• Can remain in place in passive mode when the real application layer is con-
nected to monitor that data reply transfers are received in reply to data request
transfers generated by the application layer.
An advantage of this verification environment architecture is that it provides a clear
migration path from block to module to system level verification. During block level verifi-
cation, only the interface YCs are present and are attached to all ports. In this configuration,
each interface VC randomly generates all types of packets and in any order since at this level
the focus is only on verifying the XBar crossbar switch. During module level verification,
interface and module verification components are present. In this configuration, interface
ves take on a supporting role where they provide the infrastructure necessary for higher
level VCs to transmit their generated data. During system level verification, both interface
and module VCs take on a supporting role where interface VCs provide the infrastructure
needed by module VCs and in tum, module VCs provide the infrastructure needed by system
VCs.
There is great flexibility in deciding the detailed architecture of a verification environ-
ment. This means that the final form of an architecture will ultimately be decided by the spe-
cific requirements of a project. One requirement for deciding the architecture presented in
this section is to help illustrate different sequence generation techniques. These techniques
include:
• Reactive sequences responding to environment conditions.
• Interaction between a flat sequence and a sequencer.
• Interaction between a hierarchical sequence and a sequencer.
• Interaction between multiple sequences and a single sequencer, and techniques for:
• Allowing the sequencer to choose the sequence that has the item relevant to cur-
rent environment conditions.
• Allowing one of the sequences to grab the sequencer, thereby preventing that
sequencer from interacting with any of the other sequences until it is released.
• Using virtual sequencers for interaction between one sequence and multiple sequenc-
ers, thereby allowing a sequence to interact with multiple DUV interfaces.
• Use of transaction ports to communicate between VCs at different layers of the veri-
fication environment (e.g .. between system VC and module VC), and conditions that
make this use model preferred over other means.
• Reuse of infrastructure developed during block level verification in the module level
verification em-ironment. and from module to system le\'e\ \'eritication environment.
Flat Sequences 343

These techniques will be illustrated in the following sections where the implementation
for each layer of the verification environment is described.

1.1.~. ~. F'J(1 t s~(l1!.~.~~.~~ ___ ___ ._. __ ._. _. ._. _ .________.__ .___._. _. _...... _. ______ ._____._. ._._ . ___ . . _ _
The interaction between a flat sequence and a sequencer is described in section 8.8.1. Flat
sequences produce only sequence items which are then passed to a driver through synchro-
nized interaction with a sequencer. Figure 14.3 shows the structural relationship between
these components. As shown, a sequencer may contain one or more sequences that interact
with the sequencer to pass their generated items to the driver. Sequences may require status
information from the environment in order to generate their next item (see reactive
sequences in section 14.5). The architecture is defined so that all such information is pro-
vided by only the monitor. A sequencer can interact with mUltiple sequences, in which case,
the next available item by a relevant sequence is selected by the sequencer. In addition,
sequences are defined to allow the sequencer to choose from the next relevant verification
item available (see conditional sequences in section 14_10).
Monitor
~
~

Seauencer Driver

ISequence 1 I.... y

Sequencer
I Sequence 2 Arbiter ~
1
.... ...
I Sequence 3 L.. ..
y

I
I '---

Figure 14.3 Sequencer and Driver Interaction

The simplest form of a sequence consists of a non-reactive flat sequence being passed to
the driver through the pull-mode interaction. A non-reactive sequence does not need envi-
ronment status information to generate the next item. The generation of this simple form of
sequence is described in this section. The discussion in this section is used to introduce more
advanced sequence types in the following sections.
The first step in creating a sequencer is to define a default sequence item. This sequence
item is used in generating the default behavior for a sequencer as described later in this sec-
tion. The following program shows the declaration for a class object that will be used as the
default sequence item for the transmit direction of the sequencer for XBar interface verifica-
tion em-ironment:

~m 14.2: default sequence item xbar_xmt_packet


1 class xbarJmt_packet extends xbar_packet:
2 ·ovm_object_ulils(xbar_xmt_packet)
3
344 Verification Scenario Generation

4 intport_num;
5 constraint valid_src_addr {src_addr == port_num;}
6 constraint valid_dest_addr {dest_addr != src_addr;}
7
8 function new(string name="xbar_xmtJ>acket");
9 super.new(name);
10 endfunction: new
11
12 function void pre_randomizeO;
13 xbar_xmCsequencer sqnsr;
14 $cast(sqnsr, geCsequencer());
15 if (sqnsr != nUll) port_num = sqnsr.port_num;
16 endfunction
17 endclass: xbar_xmt_packet
L---_____________________

Class xbar_packet used as base class for xbar_xmt_packet is derived from


ovm_sequence_item (see program 13.3). Even though it is possible to use class xbar_packet as
the default sequence item for xbar_xmCsequencer, it is recommended that a dedicated class
type be defined for each sequencer type so that it can be customized for the specific require-
ments of its sequencer. In this example, declaration of class xbar_xmCpacket includes the nec-
essary code to initialize its port_num field to that of the port that is generating this sequence
item. Additionally, this class includes a constraint for setting the source address of this
packet to the correct port number before it is passed to the driver.
The default sequence item defined in the previous program is used in declaration of a
sequencer. This setting is shown in the following program segment:

'Program 14.3: Setting default sequence item for XBar transmit sequencer
class xbar_xmt_sequencer extends ovm_sequencer;

2 function new (string name="", ovm_component parent=null);


3 super.new(name, parent);
4 'ovm_update_sequenceJib_and_item(xbar_xmt_packet)
5 endfunction: new
6 endclass: xbar_xmt_driver

In this example, class xbar_xmt_packet is defined as the default sequence item for driver
xbar_xmCsequencer (line 4). The full implementation of this sequencer is similar to that of
xbarJcv_sequencer shown in program 13.7.

In an interface verification component, sequence items are generated by the sequencer


and passed to the driver. The following program shows the implementation of a task in a
driver that interacts with the sequencer to get the next generated sequence item:

,Program 14.4: XBar transmit driver interaction with sequencer


1 task xbar_xmt_driver::get_packet_drive_and_respond(int port);
2 xbar_packet xmt_pkt;
3 ovm_sequencejtem item;
4
5 seqjtemJ)rod_if.get_nexUtem(item);
6 cast(xmt_pkt. item);
7 drive_packet(port, xmt_pkt):
8 seq_item_prod_if.item_done(xmt_pkt):
9 endtask: get_packet_drive_and_respond
Flat Sequences 345

This interaction consists of two main steps: 1) driver requesting and receiving the
sequence item from the sequencer (line 5), and 2) driver informing the sequencer that the
item has been processed so that the sequencer can complete the generation steps for this
sequence item (line 8). This example shows a pull-mode implementation where the driver
requests the next item from the sequencer when it is ready to process that item, and blocks if
necessary, until the sequencer can produce an item. Instantiation and connection of sequence
item producer interface seq_item_prod_if is similar to that for the receive driver (programs
13.9 and 13.10).

In the implementation of the XBar verification environment, reactive sequences receive


their required information through the monitor and the sequencer. In some cases, however,
the information required by a sequence may be available from only the driver (e.g., driver's
intemal status on the result of executing an item). In such cases, the driver can return an item
to the sequencer when calling function item_doneO (line 8). This returned item can then be
accessed by the sequence that provided the item currently being executed.

The GVM class library provides the predefined sequence type ovmJeqJsp_sequence to
automate the handling of separate request and response item types.

14.4.1 Sequencer Default Behavior


In the absence of any user defined sequences, the default behavior of a sequencer is defined
by the following aspects (section 8.5.1):
• Field default_sequence of sequencer (default is ovmJandom_sequence)
• Fields count and max_random_count of sequencer (default is OVM_UNDEF and 10)
• Predefined sequence ovm_random_sequence
• Predefined sequence ovm_exhaustive_sequence
• Predefined sequence ovm_simple_sequence
The first step, before sequence execution starts, is to decide the value of field count. In
this step, if the value of count is the default value ofOVM_UNDEF, then it is assigned a positive
random value less than maxJandom_count. If the user has changed the value of count by
using function set_confi9_intO to a value equal to or larger than 0 (e.g., line 9 in program
14.6), then its value is not changed.
Next, the value of field default_sequence is used to decide which sequences to execute.
If default_sequence is set to its default setting of ovmJandom_sequence, then the sequencer
executes count sequences selected randomly among user defined sequences and the pre-
defined sequence ovm_simple_sequence. Sequence ovm_simple_sequence is defined to exe-
cute the default sequence item of the sequencer (i.e., xbar_xmt_packet) once.
If no user defined sequences are defined, and given that the default values of fields
default_sequence and count are ovm_random_sequence and OVM_UNDEF respectively, the
sequencer by default generates a random number of sequence items (less than value given by
field max_random_count) of type xbar_xmt_sequence when started.
The implementation of the default sequence item xbar_xmt_sequence shows how its
source address is set to the port number of its parent sequencer. Any other constraints needed
346 Verification Scenario Generation

for this default behavior of the sequencer can also be added to the implementation of
xbar_xmCpacket in a similar fashion.

14.4.2 Adding Sequences to the Sequence Library


The sequence generation architecture allows for user defined sequences to be defined and
added to the sequence library ofa sequencer. Since the default behavior of the sequencer is to
randomly select among sequence ovm_slmple_sequence and user defined sequences, simply
defining a new sequence brings this new sequence into the mix of sequences that are exe-
cuted.
The following example shows the declaration of a sequence xbar_xmt_seCLsend_beacon
for sequencer xbar_xmCsequencer:

Iprogram 14.5: User defined sequence for xbar_xmt_sequencer


1 class xbar_xmt_seq_send_3_packets extends ovm_sequence;
2 'ovm_sequence_utils(xbar_xmCseq_send_3_packets, xbar_xmCsequencer)
3
4 function new(string name="", ovm_object parent=null);
5 super.new(name);
6 endfunction
7
8 virtual task bod yO;
9 xbar_xmt_packet this_transfer;
10 for (int i=O; i< 4; i++)
11 if (i!=p_sequencer.port_num)
12 'ovm_do_with(this_transfer, {dest_addr == I;})
13 endtask
14 endclass: xbar_xmt_seq_send_3_packets

Sequence xbar_xmCseq_send_3_packets is derived from class ovm_sequence (line 1) and


is specifically defined to belong to sequencer xbar_xmt_sequencer (line 2). This sequence is a
flat sequence and sends one XBar packet to each of the other ports of the XBar design. A
for-loop (line 10) is used to loop over al1 destination addresses. An if-statement (line 11) is
used to skip a destination address if it is the same as the port number for the generating
sequencer, since a constraint involving this destination address would conflict with the con-
straint set in the declaration of xbar_xmt_packet requiring the destination address to be differ-
ent than the current port number. OVM macro ovm_do_withO is used to execute the sequence
item and to define the constraint for setting the destination address (line 12).
It is especially important to note that any field values in sequence item this_transfer are
lost before the item is executed (line 12). The reason is that item execution with macro
ovm_do_with() allocates a new object for this item at the beginning. This means that values of
fields of this_transfer before calling ovm_do_withO cannot be used for any purposes, and any
customization should be performed through constraints specified with the macro executing
this item.
Adding sequence xbar_xmt_seq_send_3_packets to the sequence library of sequence
xbar_xmt_sequencer has the effect of including this sequence in the list of randomly selected
sequences when executing the predetlned sequence ovm_random_sequence.
Flat Sequences 347

14.4.3 Modifying the Default Behavior


Sequencer default behavior can be modified in a number of ways:
• Changing the value of field count in the sequencer
• Adding new sequences to the sequence library
• Changing the value of field defaulCsequence in the sequencer
• Changing task run() of the sequencer

The addition, and effect, of adding a new sequence to the sequence library is shown in
the previous section. The following example shows how the value of fields count and
default_sequence can be customized as part of building the verification environment:

Iprogram 14.6: XBar testcase with passive transmit agents


1 class xbar test modifed behavior extends xbar lest default:
2 . ovm-_componenCutils(xbauescmodifed=behavior)
3
4 function new(string name = "xbar_base_test", ovm_component parent=null):
5 super.new(name, parent):
6 endfunction: new
7
8 virtual function void buildO:
9 set_configJnt("·.xbar_xmt_agentO.xmt_sqnsr", "count", 0):
10 set_config_int ('" .xbaUvc[O].xbar_xmt_agentD.xmt_sqnsr"."count", 1):
11 set_config_string ("*.xbaUvc[O].xbar_xmt_agentO.xmt_sqnsr",
12 "default_sequence", "xbar_xmt_seq_send_3_packets"):
13 super.buildO:
14 endfunction: build
15 endclass: xbar_test_modifed_behavior

The above program defines new test xbar_test_modifed_behavior that can be executed by
passing its name to the global function run_test() (section 13.6.6), and follows the procedure
described for defining new testcases for the XBar verification environment (section 13.6.5).
In this implementation, configuration function set_config_int{) is first used to set field count of
all transmit sequencers to value 0 (line 9). Functions set_conUnt{) and seCconfi9_string{) are
then used to redefine the values for fields count and default_count of the sequencer at port 0
(lines 10-12), thereby overriding the setting for port 0 made on line 9. These settings will
take effect while the environment hierarchy is being built by calling function build() (line 11).
With these configuration settings, the running of this testcase results in one execution of
sequence xbar_xmt_seq_send_3_packets by the sequencer at port 0, and no sequences in the
sequencer of any of the other ports.

Most behaviors can be implemented by adding a new sequence to the sequence library,
redefining the default sequence, and changing field count of the sequencer. For behaviors that
cannot be implemented through these steps, the default behavior of a sequencer can be mod-
ified by redefining its predefined task runO. The following example shows this approach:

IProgram 14.7: Modifying default definition of task runO of a sequencer


1 task xbar_xmt_sequencer::run ():
2 fork
3 super.runO:
4 start_sequence(get_sequence(get_ seq_kind ("xbar_ xmt_ seq_send _ 3_packets"»):
5 join
6 endtask
~.. --.
348 Verification Scenario Generation

[n this example, an instance of sequence xbar_xmCseCLsend_3_packets is started as a


root sequence (section 8.6) by using functions get_seq_kind() and get_sequence() of the
sequencer (line 4). Note that the predefined task run() of the sequencer must be started in a
parallel thread in order for the sequencer operation to begin (line 3).

14.5 Hierarchical Sequences


Hierarchical sequences allow a sequence to be defined in terms of other previously defined
sequences. As such, hierarchical sequences allow complex sequences to be described more
efficiently and more clearly. A hierarchical sequence generates a single stream of sequence
items and as such appears to the sequencer as a single sequence.
A sequence that can be called as a subsequence is described in terms of its:
• Action block
• Configuration fields
The action block for a sequence describes the steps performed in the sequence for gen-
erating the target scenario. The action block may execute sequence items or subsequences.
Configuration fields of a sequence allow it to be customized when executed by a parent
sequence. An example of a hierarchical sequence executing a configurable subsequence is
shown in this section.
A subsequence that is executed by another sequence is created and randomized similar
to how sequence items are executed. This means that configurable fields of a subsequence
can be modeled using randomizable fields of a sequence class. The following program shows
the implementation of a sequence that contains fields that can be configured by a calling
sequence:

'Program 14.8: Sequence definition containing configurable fields


1 class xbarJmt_seq_send_special extends ovm_sequence;
2 .ovm_sequence_ utils(xbar_xmt_seq_send _special, xbar_xmt_ sequencer)
3
4 rand xbar_packet pkt;
5 rand int seq_param1;
6 rand int seCLparam2;
7
8 constraint valid_dest_addr {pkt.dest_addr != p_sequencer.port_num;}
9 constraint valid_src_addr {pkt.src_addr == p_sequencer.port_num;}
10 constraint param1 {seq_param1 < 1000;}
11 constraint param2 {seq_param2 < 2000:}
12
13 function new(string name="", ovm_object parent=null);
14 super.new(name):
15 $cast(pkt, ovm_factory::create_object("xbar_packet", '''', "pkt"));
16 endfunction
17
18 virtual task bod yO;
19 xbar_xmt_packet this_transfer:
20 'message(OVM_INFO. ("Sequence Parameter 1 randomized to:", seq_param1))
21 'message(OVM_INFO, ("Sequence Parameter 2 randomized to:", seq_param2))
22 'ovm_do_with(this_transfer, {dest_addr == pkt.dest_addr;
23 data == pkt.data; wait_cycle == pkt.wait_cycle:})
Hierarchical Sequences 349

24 endtask
25 endclass: xbar_xmt_seq_send_special

This example shows sequence xbar_xmCseq_send_speclal (line 1) belonging to


sequencer xbar_xmt_sequencer (line 2). Randomizable fields pkt, seq_paramh and seq_param2
are also defined in this class (lines 4-6). Note that these fields are marked as rand so they
can be assigned by the randomization engine when a subsequence is randomized as part of its
execution flow. If this sequence is executed as a subsequence, then any constraints specified
in the calling macro (e.g., ovm_do_wlth()) is considered along with the constraints in the local
scope (lines 8-11). If this sequence is executed as a root sequence, then only constraints in
the local scope are considered. SystemVerilog randomizes only non-nuB objects. Therefore,
random fields that are object classes must be allocated in the constructor for this sequence
(line 15) so that they have non-null value during randomization.
It can be assumed that by the time the action block of a subsequence is executed, its ran-
dom fields have already been assigned a value meeting any randomization constraints speci-
fied in its local and the calling scopes. These values can then be used to customize the flow
of the action block (lines 20, 21) or provide further constraints for sequence items or subse-
quences being executed in the local scope (lines 22, 23).
The following program shows the implementation of a hierarchical sequence:

'Program 14.9: Hierarchical sequence


1 class xbar_xmt_seq_send_multiple_packets extends ovm_sequence;
2 .ovm_sequence_utils(xbar_xmt_seq_send_multiple_packets, xbar_xmt_sequencer)
3
4 function new(string name="", ovm_object parent=null);
5 super.new(name);
6 endfunction
7
8 rand bit [3:0] seq_dest_addr;
9 constraint valid_addr {seq_dest_addr < 4;seq_dest_addr != p_sequencer.port_num;}
10
11 virtual task body();
12 xbar_xmt_seq_send_special send_special_seq;
13 xbar_xmt_packet xmUtem;
14 'ovm_do_with(send_special_seq, {
15 sequence_param1 == 20;
16 sequence_param2 == 20;
17 pkt.wait_cycle == 20;
18 pkt.data == 4'b1111;
19 pkt.desCaddr == seq_dest_addr;})
20 'ovm_do_with(send_special_seq, {
21 pkt.data == 4'b01 01;
22 pkt.dest_addr == seq_dest_addr;})
23 repeat (10)
24 'ovm_do(xmUtem)
25 endtask
26 endclass: xbar_xmt_seq_send_multiple_packets

This example shows sequence xbar_xmt_seq_send_multiple_packets (line 1) belonging to


sequencer xbar_xmt_sequencer (line 2). Contiguration tield seq_dest_addr and its related con-
straints (lines 8, 9) contains the destination address of all packets generated by this sequence.
This sequence executes subsequence send_special_seq two times (lines 14-22) while pro\id-
ing constraints for its configurable fields. Note that fields within configuration field pkt of
this subsequence can be constrained directly when using the execution macros (e.g., lines 18.
350 Verification Scenario Generation

19). It then executes sequence item xmUtem a total of ten times without providing any con-
straints.

14. 6 Re~ctiv~ SeC{uen~f!..~ ____________.______. . ______.___.____._


Only in rare conditions is a sequence completely independent of its outside environment.
Most sequences are reactive sequences that either follow the current configuration settings of
the verification environment or produce stimulus in reply to current state values in the verifi-
cation environment and the DUV. It is, therefore, necessary to have a clear strategy for mod-
eling the interaction between verification environment components that provide a sequence
with the information it needs to generate reactive scenarios.
A reactive sequence receives two types of information from the environment:
• Configuration settings related to its location in the environment hierarchy (e.g., the
port number for the agent containing the sequencer).
• Live data being generated by a monitor (e.g., generating a read-reply transfer in
response to a read-request transfer arriving at the DUV port).
The sequencer is the focal point of all sequence generation activity since it mediates the
generation of items before they are sent into the environment. As such, a sequencer is the
obvious choice for localizing information needed by a sequence or a sequence item. When
building the environment, special care should be taken to include in the sequencer, all config-
uration fields that sequences and sequence items may need to access. An example of this
approach is adding parameter porCnum to the i111plementations of xbar_xmt_sequencer and
xbar_rcv_sequencer (program 13.7) which was initialized when building the environment
hierarchy (program 13.10). This parameter was consequently used for specifying a constraint
for the source address of xbar_xmCpacket, the default sequence item for xbar_xmCsequencer
(line 5 in program 14.2).
Reacting to live data during sequence generation is somewhat more challenging since
the changing nature of data values requires careful timing and ordering of sequence genera-
tion activity with value changes in the environment. XBar packets generated in the transmit
path of the XBar verification environment do not depend on live data generated by the envi-
ronment, since both the content of the packet as well as the wait cycle for the packet are
decided in the sequence. The receive channel of the XBar verification environment may,
however, need to generate a wait cycle value that depends on the source port of the packet
being received on the XBar transmit port. This wait cycle value is used by the receive path
slave driver to decide how many cycles to wait before accepting the incoming packet from
the DUV. This requirement is a clear example of a strict reactive sequence where both the
timing and the value of the generated sequence item depends on observations made at the
design transmit port. In this example, timing is important since the sequence in the recehe
sequencer should produce a wait cycle value as soon as a ne\\' packet has been produced by
the DUV. The remainder of this section shows the implementation of a sequencer that imple-
ments this requirement. The implementation approach shown here can easily be applied to
cases requiring similar types of interactions between a sequencer and a DUV interface.
Examples include generating a read-reply transfer in response to receiYing a read-request
Reactive Sequences 351

transfer, producing an error condition at a DUV port upon a specific value observed at the
DUV port, etc. A sequence that requires infonnation from mUltiple design ports should be
placed in module or system VCs where conditions at all DUV interfaces can be tracked
through the monitors in interface VCs.
In deciding the best implementation for a reactive sequence, the following issues must
be considered:
• A sequence is not aware of the driver and the driver is not aware of the sequence.
They interact only through the sequencer. As such, a sequence cannot read from a
driver and a driver cannot write to a sequence.
• A monitor is not aware of any ofthe other components in the environment. A monitor
provides status about the current state of the environment. Other components in the
environment subscribe to this status information as needed. This behavior is neces-
sary to allow a monitor to be used in passive mode where the driver and the
sequencer are removed.
Given this independence requirement between verification environment components,
the following approach is used for implementing a reactive sequence:
• IdentifY all environment status information needed by a reactive sequence to take the
next step in its flow.
• Implement the monitor to provide this information to the sequencer, either through
broadcasting on an analysis port or through transaction ports.
• Add to the sequencer the necessary fields that allow access to this status information
in the monitor. By using this approach for reading status information produced by the
monitor, it is possible to remove the sequencer in passive mode without needing to
change the monitor implementation.
• In a reactive sequence, use transaction interfaces made available in its sequencer to
detect and react to relevant environment conditions.
The approach outlined above allows the component independence requirement to be
satisfied while providing a reactive sequence with the information it requires. The following
example shows the implementation of a reactive sequence that generates wait-cycle values
based on the source address of the incoming packet on the receive channel.

IProgram 14.10: Reactive Sequence/receiving packets from XBar transmit port


1 class xbar_rcv_seq_rand_wait_cycle extends ovm_sequence;
2 ·ovm_sequence_utils(xbar_rcv_seq_rand_wait_cycie. xbar_rcv_sequencer)
3
4 function new(string name= ..... ovm_object parent=null);
5 super.new(name);
6 endfunction
7
8 xbar_rcv_packet this_transfer;
9 rand int unsigned seq_wait_cycle;
10 constraint reasonable_wait {seq_wait_cycle < 200;}
11
12 virtual task body();
13 xbar_packet partiaUcved_pkt:
14 p_sequencer.duv_out_valid_port.peek(partiaUcved_pkt);
15
16 case (partiaUcved_pkt.src_addr)
17 0: ·ovm_do_with(this_transfer. {wait_cycle == 10;})
18 1: ·ovm_do_with(this_transfer. {wait_cycle == 20;})
352 Verification Scenario Generation

19 2: 'ovm_do_with(this_transfer, {waiCcycie == seq_wait_cycle;})


20 3: 'ovm_do_with(this_transfer, {wail_cycle == seq_wail_cycle;})
21 endcase
22 endtask
23 endclass: xbar_rcv_seq_rand_wait_cycle

The definition for xbarJcv_packet is not shown, but is similar to the one for
xbar_xmCpacket (program 14.2). The declaration of blocking peek port connector object
duv_ouCvalld_port in sequencer xbarJcv_sequencer, and connecting it to the blocking peek
imp connector object in xbar_rcv_monltor is shown in section 13.6. The implementation of
sequence xbar_rcv_selLrand_wait_cycle contains configuration field seq_waiCcycle (line 9,
10). Execution of this sequence starts by calling function peekO of transaction interface
duv_out_valid_port (line 14). This function call returns when the monitor detects that the
DUV has placed a new packet on its transmit port. The sequence then generates an item with
a wait cycle value that depends on the source address of the incoming packet (lines 16-21).
Note that in this implementation, no direct interaction with the receive slave driver takes
place. In this flow, the receive slave driver waits to receive the first item from its sequencer
providing information on how many cycles to wait before collecting an incoming packet
from the port, while the monitor is independently looking for the arrival of the first packet.
Once this packet arrives, the monitor allows function peekO to complete allowing the
sequence to proceed, thereby generating the next item. This item is then passed to the receive
slave driver, which will in tum use this item to decide how long to wait before collecting the
incoming packet.
The mechanism shown in the above example can be used multiple times in a row to
generate a complex sequence of handshaking with a DUV port. The sequence shown in the
above example must replace the default behavior for the xbarJcv_sequencer. The mecha-
nisms described in section 14.4.3 can be used to carry out this task.

14. 7 Virtual Sequences


Figure 14.4 shows the block diagram corresponding to interface and module ves for the
XBar design. The XBar verification environment as shown in figure 13.3, is limited to sin-
gle-sided verification scenarios since interface ves can interact with only one DUV inter-
face. Additionally, interface VCs are not aware of the XBar communication protocol and
work only with xbar_packet items. However, module ves in the XBar verification environ-
ment:
• Are aware of the Xbar frame abstraction defined in table 14.1
• Can interact with multiple interface ves to generate multi-sided scenarios
• Automatically generate beacon request transfers
• Automatically generate beacon reply transfers in response to incoming beacon
request transfers
• Provide the abstraction of multi-frame data request and reply transfers, and can gen-
erate random data request and reply transfers
• Do not differentiate between data request and reply transfers, and leave it to the sys-
tem VC to generate data request transfers and reply to data reply transfers
Virtual Sequences 353

Module VC (xbar_mvc)

DUV

To sequence producer interfaces


of the other 3 xbarjvc blocks

Figure 14.4 Virtual Sequences Connecting Interface and Module VCs

A virtual sequencer allows a single sequence to interact with multiple sequencers and
hence interact with multiple drivers. A virtual sequence can only execute subsequences,
either in the local virtual sequencer or in a downstream sequencer. This means that a virtual
sequence can execute a subsequence on any sequencer in the verification environment as
long as that subsequence is in the sequence library of the target sequencer. The sequencer
belonging to the XBar module VC is implemented using a virtual sequencer so that each
instance of the module level sequencer can interact with transmit sequencers in all of the
interface VCs. Note that the virtual sequencer for the module VC is placed in the top level
environment so that its implementation can be changed if needed without needing to modify
the implementation of the module vc.
The virtual sequencer of the module VC is implemented through the following steps:
• Implementing the virtual sequencer (mxbar_sequencer in figure 14.4)
• For each virtual sequencer, adding one sequence consumer interface (section 8.9) for
each downstream sequencer, and connecting it with the sequence producer interface
in the downstream sequencer (sci connectors in figure 14.4)
• Implement sequences in the sequence library of downstream sequencers that will be
executed by local virtual sequences (sequence seq3 in figure 14.4)
• Implement virtual sequences that execute local sequences and sequences on down-
stream sequencers through appropriately connected sequence consumer interfaces
(sequence VirtSeq1 in figure 14.4)
These steps are described in the following sections:
354 Verification Scenario Generation

14.7.1 Virtual Sequencer


The implementation ofXBar module VC sequencer is shown in the following program:

IProgram 14.11: XBar module VC sequencer


1 class mxbar_sequencer extends ovm_virtual_sequencer;
2 int unsigned port_num = 0;
3 int unsigned num_duv_ports = 4;
4
5 ovm_blocking_peek_port#(xbar_frame ) beacon_requested _port:
6 ovm_seq_item_prodJf sve_pkt_prod_if;
7
8 . ovm_sequencer_utils _begin(mxbar_sequencer)
9 'ovm_fieldjnt(port_num,OVM_ALL_ON)
10 'ovm_field_int(num_duv-.ports,OVM_ALL_ON)
11 'ovm_sequencer_utils_end
12
13 function new (string name='''', ovm_component parent=null);
14 super.new(name, parent);
15 'ovm_update_sequenceJib
16 beacon_requested_port = new("beacon_requested_port", this);
17 $cast(sve_pkt_prod_if,
18 create_componenl("ovm _seq_item_prod_if', "sve_pkt-'prodjf'»;
19 endfunction: new
20
21 function ovm_seq_consJf get_seq_cons_if(int index):
22 string seq_if_name;
23 $sformat(seq_iCname. "mxbar_seq_if(%Od]", index);
24 if(seq_cons_if.exists(seqJf_name)==O)
25 add_seq_cons_if(seqjCname);
. "26 return seq_consjf[seqjCname};
27 endfunction: get_seCLcons_if
28 : endclass: mxbar_sequencer
L-.-

Sequencer mxbar_sequencer is derived from class ovm_virtual_sequencer, and contains


blocking peek port object beacon_requested_port (line 5), and sequence item producer inter-
face sve_pkt_prod_if (line 6) used for receiving sequence items from the higher layer
sequencer (see layered sequences in section 14.9).
A virtual sequencer can only execute subsequences, and cannot generate sequence item.
This means that it is not necessary to define a default sequence item for a virtual sequencer.
Therefore, instead of using macro ovm_update_sequenceJib_andJtem() which is used with
real sequencers to specify a default sequence item, macro ovm_update_sequece_lib is used in
the constructor (line 15) to initialize sequence generation facilities of this virtual sequencer
without specifying a default sequence item.
A virtual sequencer by default contains associative array seq_cons_if used for storing
interface objects connecting to downstream sequencers. Function geCseq_cons_ifO is defined
(line 21-27) to return a connector for an index value. This function adds a new connector in
array seq_cons_if if one for that index does not already exist. This function provides an sim-
ple-to-use mechanism for both the creation and access to these interface objects through an
index value.

Building the hierarchy for the XBar module VC is similar to that defined for the XBar
interface VC and is not shown in this text. The next section shows how each virtual
Virtual Sequences 3SS

sequencer in a module ve is connected to each of the downstream sequencers in the inter-


face yes.

14.7.2 Sequence Interface Connectivity


The following program shows the implementation of XBar verification environment top
level that includes both the interface and module yes. Note that the implementation of this
verification environment is derived from that of the implementation for the verification envi-
ronment containing only the interface ves (program 13.12).

I Program 14.12: Module level XBar verification environment


1 class mxbar_sve extends xbar_sve;
2 'ovm_component_utils(mxbar_sve)
3
4 xbar_mvc mvcsO;
5
6 function new (string name="", ovm_component parent=null);
7 super.new(name, parent);
8 endfunction
9
10 virtual function void buildO;
11 string insCname;
12 super.buildO;
13
14 mvcs = new[num_portsj;
15 for (int i=O; i< num-ports; i++) begin
16 $sformat(insCname, "xbar_mvc[%Od]", i);
17 set_config_int(insCname. "port_num". i);
18 set_config_int(inst_name, "num_duv_ports". num_ports);
19 $cast(mvcs[ij. create_component("xbar_mvc", inst_name));
20 mvcs[ij.buildO;
21 end
22
23 for (int i=O; i< num_ports; i++) begin
24 ivcs[ij. rcv _pkt_bdcst_port.connect(mvcs[ij.agent.rcv_ monitor.lstn jmp);
25 ivcs[ij.xmCpkt_bdcst_port.connect(mvcs[ij.agent.xm!_monitor.lstn_imp);
26 for (in! j=O; j< num_ports; j++) begin
27 ovm_seq_consjf sci;
28 sci = mvcs[i].agent.sqnsr.get_seq_consjfU);
29 sci.connecUf(ivcsUl.xmt_agent.xmt_sqnsr.seq_prod_if);
30 end
31 end
32 endfunction: build
33 endclass: mxbar_sve

The top level environment mxbar_sve is derived from class xbar_sve (line I). This envi-
ronment instantiates a dynamic array of type xbar_mvc type objects (line 4). Task buildO of
this class is implemented to first call task buildO of its parent class (line 12), thereby building
the environment shown in figure 13.3. It then creates objects in array mvcs (line 14) and
builds the module ve at each index (lines 15-21).
In the next part, connections are made between each module VC and downstream inter-
face yes. For the module ve at port i, packet listeners in recei\'e and transmit monitors
(implemented as analysis imp objects) are connected with the corresponding packet broad-
casters (implemented as analysis port objects) of the interface ve at port i (lines 24, 25).
Next, a loop is used to create four sequence consumer interface objects for the virtual
356 Verification Scenario Generation

sequencer at port I (line 28), and then connecting each with the sequence producer interface
in its corresponding downstream sequencer (line 29). Note that each new sequence consumer
interface is created by calling function get_seq_cons_lfO (line 28), since this function creates
an object if one doesn't exist.
Note that each sequence consumer interface is connected to four different sequence pro-
ducer interfaces in the downstream sequencers. And each sequence producer interface in the
downstream sequencers receives connections from four different virtual sequencers. This
structure shows an example of virtual sequencers executing sequences on different down-
stream sequencers, and downstream sequencers allowing multiple virtual sequencers to exe-
cute sequences on them.

14.7.3 Downstream Sequences


Virtual sequencers can only execute sequences and not sequence items. As such, a special
sequence must be defined in a downstream sequencer in order to provide a mechanism for
virtual sequencers to execute its sequence item type. The following program shows the
implementation of this downstream sequence:

.Iprogram 14.13: Defining a sequence on a downstream sequencer


1 class xbar_xmt_seq_send_packet extends ovm_sequence;
2 'ovm_sequence_utils(xbar_xmt_seq_send_packet, xbar_xmt_sequencer)
3
4 rand xbar_packet pkt;
5 constraint valid_desCaddr {pkt.dest_addr 1= p_sequencer.porCnum;}
6 constraint valid_src_addr {pkt.src_addr == p_sequencer.porCnum;}
7
8 function new(string name="", ovm_object parent=null);
9 super.new(name);
10 pkt = newO;
11 endfunction
12
13 virtual task bod yO;
14 xbar_xmt_packet this_transfer;
15 'ovm_do_with(this_transfer, (dest_addr == pkt.dest_addr;
16 data == pkt.data; wait_cycle == pkt.wait_cycle;})
17 endtask
18 endclass: xbar_xmt_seCLsend_packet

This program shows the implementation of sequence xbar_xmt_seq_send_packet derived


from ovm_sequence and placed in the sequence library for xbar_xmt_sequencer. This sequence
includes configurable field pkt that allows sequences executing this sequence as a subse-
quence to execute items on xbar_xmCsequencer.

14.7.4 Virtual Sequence Library


Vittual sequences are placed in the sequence library of a virtual sequencer, and can execute
either local sequences, sequences in downstream sequencers, or virtual sequences in down-
stream \·irtual sequencers. Downstream sequencers may optionally be virtual sequencers that
drive a downstream sequencer.
Virtual Sequences 357

The following program shows the implementation of a configurable virtual sequence


that allows each module VC to send a packet through any of the downstream sequencers:

'Program 14.14: Virtual Sequence using macro ovm_so_seq_withO


1 class mxbar_seq_send_frame extends ovm_sequence;
2 ·ovm_sequence_utils(mxbar_seq_send_frame. mxbar_sequencer)
3
4 rand xbar frame xfr;
5 constraintwaitcycles {xfr.wait_cycle < 10;}
6
7 function new(string name="". ovm_object parent=null);
8 super.new(name);
9 $cast(xfr. ovm_factory::create_object("xbar_frame" ...... "xfr"));
10 endfunction
11
12 virtual task bodyO;
13 xbar_xmCseq_send_packet seq_send_pkt;
14 ovm_seq_cons_if sci;
15
16 sci = p_sequencer.get_seq_consJf(xfr.src_addr);
17 ·ovm_do_seq_with(seq_send_pkt. sci. {
18 pkt.dest_addr==xfr.dest_addr;
19 pkt.data == xfr.data;
20 pkt.wait_cycle == xfr.wait_cycle;})
21 endtask
22 end class: mxbar_seq_send_frame

This implementation defines class mxbar_seq_send_frame derived from ovm_sequence


(line 1) and belonging to virtual sequencer mxbar_sequencer (line 2). This sequence contains
configurable field xfr which provides the XBar frame abstraction implemented in class
xbar_frame (section 14.2), allowing it to be constrained based on frame kind. The source and
destination of xfr may be set to any port number. Therefore, the action block for this virtual
sequence first uses xfr.src_addr to identify sci, the sequenct! consumer interface on which its
subsequence should be executed (line 16), and then uses macro ovm_do_seq_withO, along
with sci to execute sequence seq_send_pkt (section 14.7.3) on the sequencer connected to
sequence consumer interface sci (lines 17-20).
Note that by the time the action block for this sequence is called, field xfr is randomized,
and therefore, if any constraint is set for xfr.kind, then the value of xfr.data is set to a value
consistent with this constraint. This data value is then used to constrain the configuration
field of subsequence seq_send_pkt which executes on the downstream sequencers (line 19).
Once virtual sequence mxbar_seq_send_frame is defined, a virtual sequence in any of the
module VCs can use this sequence to execute XBar frames on any of the downstream
sequencers. This is shown in the following example:

'Program 14.15: Hierarchical virtual sequence sending a beacon request frame


1 class mxbar_seq_send_beacon_request extends ovm_sequence;
2 .ovm_sequence _ utils(mxbar_ seq_send _beacon_request. mxbac sequencer)
3
4 function new(string name=·"'. ovm_object parent=null);
5 super.new(name);
6 endfunction
7
8 rand int pkt_dest;
9 constraint valid_dest_addr {pkt_dest < 3; pkt_dest != p_sequencer.port_num;}
10
358 Verification Scenario Generation

11 virtual task bod yO;


12 mxbar_seq_send_frame seq_send_frame;
13
14 'ovm_do_with(seq_send_frame, {
15 xfr.kind==BEACON_REQ;
16 xfr.dest_addr==pkLdest;
17 xfr.src_addr==p_sequencer.port_num;})
18 #1000; /I wait some time. Can be a configurable field
19 endtask
20 endclass: mxbacseq_send_beacon_request

This example shows virtual sequence mxbar_setLsend_beaconJequest which is used to


send a beacon request transfer to any of the downstream sequencers. This sequence has con-
figuration field pkt_dest. It uses macro ovm_do_withO (line 14) to execute the local virtual
sequence mxbar_seq_send_frame to send a frame with kind BEACON_REQ (line IS) through
the downstream sequencer at local port (line 17) to destination port pkt_dest (line 16). This
virtual sequence can in twn be used as a subsequence in any other local sequence by simply
constraining its field pkt_dest to send a beacon request transfer to any destination port. The
same approach can be used to send a data request transfer by sending two frames in the
action block of a sequence. This implementation is described in the next section when grab-
ber sequences are introduced.

14.8 Grabber Sequences


The main difficulty when multiple sequences are supplying packets to a single sequencer is
that the order in which packets are processed by the sequencer is dynamically decided
depending on the current state of the environment. As such, the sequence of items produced
by one sequence can be interleaved by items produced by another sequence passing items to
the same sequencer. Consider the structure shown in figure 14.5. In this configuration, the
module VC for each port may generate data request frames and pass them to the interface
verification components at any of the ports. The XBar communication protocol states that
the two frames corresponding to a transfer generated at a port can be interleaved only by a
beacon request or beacon reply frame. Consider a case when the module VCs at ports 0 and 3
are both sending a transfer to the driver at port I. Given that the order of interaction between
each sequence and the sequencer in interface VC at port 1 is dynamically decided, it is possi-
ble that the sequencer may send the SOF frame for each transfer first before sending the sec-
ond frame in each transfer. This behavior violates the protocol requirement that frames
corresponding to a transfer be interrupted only by a beacon request or reply frame.
The solution to this problem is to provide a mechanism for a sequence to grab the
sequencer that it is interacting with, thereby giving that sequence exclusive access to that
sequencer. The grabbing mechanism provided by the OVM class library is described in sec-
tion 8.8.2. This section provides an example of how the grabbing constructs are used to
resoh'e the issue raised in tlgure 14.5.
The following program shows the implementation ofthe default sequence that is started
for each module VC:
Grabber Sequences 359

Module Virtual
Sequencers

~
Datoe SOF

Interface ves
"----I 1,-,-_

Figure 14.5 Multiple Sequences per Sequencer Needing Exclusive Access

I
Program 14.16: Default sequence for XBar module VC
1 class mxbar_seq_main extends ovm_sequence;
2 'ovm_sequence_utils(rrixbar_seq_main, mxbar_sequencer)
3
4 function new(string name="", ovm_object parent=null);
5 super.new(name);
6 endfunction
7
8 virtual task bodyO;
9 mxbar_seq_send_beacon_request send_beacon_req_seq;
10 mxbar_seq_send_beacon_reply send_beacon_repILseq;
11 mxbar_seq_send_data_transfer send_data_seq;
12
13 fork
14 forever
15 'ovm_do(send_beacon_req_seq)
16 forever
17 'ovm_do(send_beacon_reply_seq)
18 repeat(p_sequencer.count)
19 'ovm_do(send_data_seq)
20 join
21 endtask
22 endclass: mxbar_seq_main

This program shows the implementation of virtual sequence mxbar_seq_maln (line 1)


belonging to virtual sequencer mxbar_sequencer (line 2). Field default_sequence of virtual
sequencer mxbar_sequencer is configured during hierarchy construction to run this sequence
by default. The action block of this sequence starts three threads using ajork-join statement
(lines 13-20). The first thread (line 14) uses ajorever loop to continuously execute sequence
send_beaconJeq_seq of type mxbar_seCLsend_beacon_request (defined in program 14.15).
Each execution of this sequence sends a beacon request transfer, and can optionally include a
randomized or user defined wait time after the request is sent. The second thread (line 16)
continuously executes sequence send_beacon_reply-seq of type mxbar_seq_send_beaconJeply
which waits for the receive monitor to indicate that a beacon request transfer has been
received, and then sends a beacon reply transfer from the local port to the source port of the
incoming beacon request transfer (implementation not shown in this text). The third thread
(line 18), executes sequence send_data_seq oftypemxbar_seq_send_data_transfer a number of
times given by the field count of this sequence's parent sequencer. Note that the default
360 Verification Scenario Generation

sequence of a sequencer is not started at all if field count of the sequencer is set to zero. As
such, if field count is used as a controlling field in a newly defined sequence (line 18), care
should be taken so that it is never set to zero. Sequences started in all these threads are
treated as subsequences and have this sequence as their parent sequence. This distinction is
important because of how it affects the behavior of grabbing sequence. The implementation
of grabbing sequence mxbar_seq_send_data_transfer is described next.

A data transfer requires two frames to be transmitted. The XBar communication proto-
col indicates that these two frames can be interrupted only by beacon request or reply frames.
The implementation of the sequence implementing this behavior is shown next:

'Program 14.17: Virtual sequence sending an XBar transfer


1 class mXbar_seq_send_data_transfer extends ovm_sequence;
2 'ovm_sequence_utils(mxbar_seCLsend_data_transfer, mxbar_sequencer)
3
4 rand xbar_data_frame dxfr;
5
6 function new(string name="", ovm_obJect parent=null);
7 super.new(name);
8 $cast(dxfr, ovm_factory::create_object("xbar_data_frame", "", "dxfr"));
9 endfunction
10
11 virtual task body();
12 mXbar_seq_send_frame seq_send_frame;
13 ovm_seq_cons_lf sci;
14
15 sci = p_sequencer.geCseq_consJf(dxfr.src_addr);
16
17 sci.grab(geCparent_seqO);
18 'ovm_do_with(seq_send_frame, {xfr.kind==SOF;
19 xfr.desCaddr==dxfr.dest_addr; xfr.src_addr==dxfr.src_addr;})
20 'ovm_do_wlth(seCLsend_frame, {xfr.kind==dxfr.kind;
21 xfr.dest_addr==dxfr.dest_addr; xfr.src_addr==dxfr.src_addr;})
22 sci.ungrab(get_parent_seq());
23 #1000; /I wait for some time, optionally randomize by using a field instead
24 endtask
25 endclass: mxbar_seq_send_data_transfer
L ______________________ _

The implementation of sequence mxbar_seq_send_data_transfer is similar to the imple-


mentation of mxbar_seq_send_frame (program 14.14). The main difference is that this
sequence first generates an SOF frame (line 18), and then sends a frame whose kind is con-
strained to the value given by its configuration field dxfr (line 20). Field kind of dxfr is guar-
anteed to be a data transfer kind since dxfr has type xbar_data_frame (line 4). Therefore, this
sequence can generate only one of the four data transfer types. Also note that the use of vir-
tual sequence mxbacseq_send_frame allows this data transfer to be sent through any of the
DUV ports, and not just the local port. The source port of this transfer is identified by field
src_addr of dxfr which is used to get the sequence consumer interface connected to that port
(line 15).

Functions grab() and ungrab() of the sequence consumer interface are used to grab and
ungrab the downstream sequencer before sequence execution starts and after it ends (lines 17
and 22).

An important aspect of this example is that the grabbing is performed on the parent
sequence of this sequence (i.e., mxbar_seq_main in program 14.16). The parent sequence is
Layered Sequences 361

obtained by calling the predefined function get_parent_seq() of ovm_sequence (line 17, 22).
The reason is that by grabbing the parent sequence, any subsequence started by the parent
sequence can still access the downstream sequencer that is being grabbed by this sequence,
therefore allowing beacon request and reply frames started by the root sequence to interleave
the two frames generated by this sequence. Note that this interleaving is allowed if both the
data transfer and beacon requesUreply transfers need to use same downstream sequencer to
execute. In other words, a local beacon request transfer cannot interleave the frames being
sent to the local downstream sequencer by the virtual sequencer in another module VC.

14.? Layered Sequences


A layered seguence consists of multiple layers of sequencers where a sequence in one layer
receives sequence items from the higher layer sequencers. This architecture is ideally suited
for implementing layered protocols. Figure 14.6 shows a view of this relationship between
the system and module VCs in the XBar verification environment. In this implementation,
the sequence in module VC gets the next sequence item from the sequencer in the system VC
and then uses this item to decide what action to take next. The connection between the
sequence in the module VC and the sequencer in the system VC is implemented using
sequence item producer and consumer interfaces (section 8.7). Note that in this architecture,
the implementation of the sequencer in the system VC would be the same, regardless of
whether its sequences are being received by a downstream sequence or a driver.

From Interface
VC Monitor

To Interface
VC ""~'u,,'",'''
VirtseQ2 1-+-+-------1~

Module Virtual Sequencer

Figure 14.6 Layered Sequences Connecting Module and System VCs

Implementation of the architecture shown in figure 14.6 follows these steps:


• Defining a default sequence item produced by the sequencer in system VC
• Implementing the system VC including its sequencer
• Building the verification environment top level to include the system VC component,
and connecting the module virtual sequencer with system sequencer
362 Verification Scenario Generation

• Implementing the module virtual sequence that receives sequence items from the
sequencer in the system VC
• Modifying the default behavior of the system VC sequencer by implementing new
sequences in its sequence library
These implementation steps are described in the remainder of this section.
The first step is to define a default sequence item type for the system VC sequencer. In
this implementation, class xbaUrame (program 14.1) can be used to model the type of trans-
fers that the system VC sequencer may need to generate. Given, however, that system VC is
aware only of data transfers, a new class must be defined so that the default behavior of the
system VC sequencer produces only data transfer items. This implementation is shown in the
following program:

·Program 14.18: Class xbar~data_frame, default sequence item for system VC


1 class xbar data frame extends xbar frame;
2 .ovm-='object_utils(xbar_data_frame)
3 constraint data_kind {kind
4 inside {OATA_REQ_A,DATA_REQ_B,DATA_REPLY_A,DATA_REPLY_B};}
5
6 function new(string name="xbar_frame_inst", xbar_packet pkt=nu/l);
'7 super.new(name);
8 endfunction: new
9 endclass: xbar_data_frame

Class xbar_data_frame is derived from class xbaUrame and only adds a constraint that
limits the kind of transfers that can be represented by this object. Note that the definition of
this new class is necessary for the default behavior of the system VC sequencer to be useful.
Another option is to use class xbar_frame as the default sequence item for the system VC
sequencer, but in that case, the default sequence of this sequencer must be modified to run a
sequence that executes an item whose kind is limited to data transfers.
The implementation of the system VC sequencer is the same as the one for interface VC
sequencer (program 13.7), with the difference that sequence item xbar_data_frame is used as
the default sequence item tyPe for the system VC sequencer. With this implementation, the
system VC sequencer will start generating random data transfer frames at the start of the run
phase of the verification.
The implementation of module VC sequencer was shown to include sequence item pro-
ducer interface sve_pkt_prod_if (program 14.11, line 6). The implementation sxbar_sve, XBar
environment top level that includes system VC components xbar_svc, is similar to the imple-
mentation of mxbar_sve (program 14.12), In this implementation, class sxbar_sve is derived
from class mxbar_sve and adds the system VC components to the environment while making
the necessary connections between system VC and module VC components. The connection
between the sequence item producer interface sve_pkCprod_if of the module VC sequencer
and system VC sequencer is carried out in this step and follows the same syntax shown for
connecting XBar receive driver xbar_rcv_driver to XBar transmit sequencer
xbar_rcv_sequencer (program 13.10 line 44).

The default implementation of system VC sequencer starts by generating a series of


data transfer frames which are passed to the sequence item producer interface in the module
VC sequencer. The next step is to write a sequence belonging to the sequence library of the
Layered Sequences 363

module VC sequencer that uses this interface to receive the next data frame it sends to down-
stream sequencers. The implementation of this sequence is shown in the following program:

I Program 14.19: Sequence in Module VC receiving items from System VC


1 class mxbaUayered_seq_send_data_transfer extends ovm_sequence;
2 'ovm_sequence_utils(mxbaUayered_seq_send_data_transfer, mxbar_sequencer)
3
4 function new(string name="", ovm_object parent=null);
5 super.new(name);
6 endfunction
7
8 task geUeq(output xbar_data_frame dfr);
9 ovm_sequencejtem item;
10 P_sequencer.sve_pkCprodjf.get_nexUtem(item);
11 $cast(dfr, item);
12 endtask
13
14 task puUsp(input xbar_data_frame dfr);
15 p_sequencer.sve_pkt_prodjf.item_done( dfr);
16 endtask
17
18 virtual task body();
19 mxbar_seq_send_frame seq_send_frame;
20 ovm_seq_cons_if sci;
21 xbar_data_frame dxfr;
22
23 geUeq(dxfr);
24 sci = p_sequencer.get_seq_consjf(dxfr.src_addr);
25 sci.grab(get_parent_seq());
26
27 'ovm_do_with(seq_send_frame, {xfr.kind==SOF;
28 xfr.desCaddr==dxfr.dest_addr; xfr.src_addr==dxfr.src_addr;})
29 'ovm_do_with(seq_send_frame, {xfr.kind==dxfr.kind;
30 xfr.dest_addr==dxfr.dest_addr; xfr.src_addr==dxfr.src_addr;})
31
32 sci.ungrab(get_parent_seq());
33 puUsp(dxfr);
34 #1000; /I wait for some time, optionally randomize by using a field instead
35 endtask
36 endclass: mxbaUayered_seq_send_data_transfer

The implementation of sequence mxbaUayered_seq_send_data_transfer (line 1), belong-


ing to the sequence library of virtual sequencer mxbar_sequencer (line 2) is shown in the
above example. This sequence defines tasks geUeq() (lines 8-12) and puUsp() (lines 14--16)
used for interacting with the sequence item producer interface in its parent sequencer. Task
geUeq() blocks until it receives the next sequence item (line 10) from the sequencer con-
nected to sve_pkt_prod_if (i.e., system VC sequencer), and task puCrsp() indicates the com-
pletion of sequence item execution to the sequencer connected to sve_pkt_prod_if (line 15).
The sequence action block (lines 18-35) starts by getting the next sequence item from the
upstream sequencer (line 23), identifying the downstream sequencer through which this
packet should be sent (line 24), grabbing that sequencer while identifying itself as the parent
sequence that started this sequence (line 25), and then using subsequence seq_send_frame of
sequence type mxbar_seq_send_frame (program 14.14) to send the two frames that compose
the requested data transfer (lines 27-30). After sending the frames, the sequence ungrabs the
downstream sequencer (line 32). and indicates to the upstream sequencer that it is finished
executing its sequence item (line 33). Note that grabbing the downstream sequencer as the
parent of this sequence (line 25) allows other subsequences started by the parent sequence
364 Verification Scenario Generation

(i.e., sequences to send beacon request and reply frames) to have access to this downstream
sequencer. This approach allows the transfer of beacon request and reply to interleave a data
transfer, but prevents other data transfers to interleave another data transfer.
One possible configuration for making use of this layered sequence is as follows:
• Implement a sequence in module sequencer that starts three parallel subsequences for
sending beacon request transfers, sending beacon reply transfers, and executing
sequence mxbaUayered_seq_send_data_transfer a given number of times controlled
by field count of the module VC sequencer.
• Configure the system VC sequencer to continuously generate sequence items. The
actual number of data transfers injected into the DUV is controlled by the number of
sequence items that the module virtual sequencer is configured to receive from the
sequencer in system VC. The system VC sequencer blocks if the module VC
sequencer stops accepting the items it is producing.
Generation of data transfer sequences, as described in this section, does not differentiate
between data request and reply transfers. The extension of this environment to comply with
data transfer requirements of the XBar communication protocol is described in the next sec-
tion.

14.10 Conditional Sequences


_--,.. ---_._-----
,..

Multiple sequences can pass their generated items to their sequencer. A sequencer picks the
next sequence item in a first-in-first-out (FIFO) order from those sequence items that are rel-
evant to the current verification context. The default implementation is to assume all
sequence items produced by all sequences are relevant. This default behavior, however, must
be changed in order to support sequence generation schemes where not all sequence items
are relevant at all times.

Consider a requirement that the XBar system VC sequencer should reply immediately
to an incoming data request transfer even if an outgoing data request transfer is pending. One
approach to support this requirement is to create two concurrently running sequences in the
XBar system VC sequencer: one for replying to incoming data request transfers, and one for
generating outgoing data request transfers. The required behavior for the sequencer is then to
pick the next sequence item from the sequence generating data reply transfers if an incoming
data request transfer is detected.

Conditional sequences provide a mechanism for specifying when a sequence is relevant


to the current verification context. In turn, a sequencer selects its next sequence item from
those sequences that are marked as relevant at the time the next item is being selected.
Conditional sequences are used in the XBar verification environment to allow the
sequencer in XBar system VC to reply to data request transfers first. even if an outgoing data
request transfer is also a\'ailable for transmission. This implementation follows these steps:
• XBar system VC monitor detects all incoming data request transfers by listening to
the analysis port of the module VC recei,'e monitor.
Conditional Sequences 365
-------------------------------------------------------------------------
• The sequencer in system VC continuously listens to the event port in the monitor and
adds any incoming data request frames to queue data_req_pkt_queue. Having the
sequencer extract this information from the monitor is necessary since the monitor is
unaware of all other blocks, so the sequencer has to extract the needed information
from the monitor.
• Each sequence has a predefined function is_relevant() whose return value is checked
by the sequencer to select sequences relevant to the current verification context:
• The sequence that generates data reply transfers returns true for this method
when queue dataJeq_pkt_queue is not empty.
• The sequence that generates data request transfers returns true for this method
when queue dataJeq_pkt_queue is empty.
• Each sequence has a predefined task wait_forJelevantO that completes only when the
sequence containing this task becomes relevant to the current verification context.
The completion of this task is used as a means of triggering the sequencer arbiter to
process sequence items as they become relevant. This task must be defined is func-
tion iSJelevantO is redefined. .

The following program shows the implementation of system VC sequencer:

'Program 14.20: system VC driver for computing relevant sequences


1 class sxbar_sequencer extends ovm_sequencer;
2 int unsigned port_num:
3
4 ovm_blocking_peek_port#(xbar_data_frame) data_req_collected_port;
5 xbaUrame data_req_pkt_queue[$];
6
7 'ovm_sequencer_utils_begin(sxbar_sequencer)
8 'ovm_field_int(port_num,OVM_ALL_ON)
9 'ovm_sequencer_utils_end
10
11 function new (string name="", ovm_component parent=null);
12 super.new(name, parent);
13 .ovm_update_sequence_lib_andjtem(xbar_data_frame)
14 dataJeq __coliected_port =new("data_req_collected_port", this);
15 endfunction: new
16
17 task update_data_req_queueO;
18 xbar_data_frame rcv_data_frame;
19 forever beg i n
20 data_req_collected _port.peek(rcv_data_frame);
21 rcv data frame = new rcv data frame;
22 data_req:':'pkt_queue. push:':'back(rcv_data_frame);
23 end
24 endtask
25
26 task runO;
27 fork
28 super.runO:
29 update_data_req_queueO;
30 join
31 endtask
32 endclass: sxbar_sequencer
~---------------------
Queue data_req_pkCqueue (line 5) is defined to hold incoming data request frames
detected by the monitor. Task update_data_req_queueO (lines 17-24) is defined to receive the
next data request transfer recei\"ed by the system VC monitor by calling task peekO of block-
366 Verification Scenario Generation

ing peek transaction port data_req_collected_port, and add the received data request to the
queue. The definition for the run() method of the sequencer is also modified to start this
method in parallel with the default behavior of the driver (lines 26-31). Given the above
implementation, queue data_req_pkCqueue will contain all unprocessed data request transfers
in the order of their arrival.

The following program shows the implementation of function ISJelevant() and task
waiCforJelevant() for sequences that generate and reply to a data request transfers:

'Program 14.21: Function is_relevant{) of data req and reply sequences


1 class sxbar_send_data_reply extends ovm_sequence;
2 'ovm_sequence_utils(sxbar_send_data_reply, sxbar_sequencer)

3 function bit is_relevant();


4 return (p_sequencer.data_req_pkCqueue.sizeO > 0);
5 endfunction
6
7 task wait_for_relevant();
8 if (IiSJelevant()) @(iUelevant());
9 endtask
10 endclass: sxbar_send_data_reply
11
12 class sxbar_send_data_request extends ovm_sequence;
13 'ovm_sequence_utils(sxbar_send_data_request, sxbar_sequencer)

14 function bit iSJelevant{);


15 return (p_sequencer.data_req_pkt_queue.size() == 0);
16 endfunction
17
18 task wait_for_relevantO;
19 while (!is_relevant()) @(p_sequencer.data_req_pkt_queue.sizeO == 0);
20 endtask
21 endclass: sxbar_send_data_request

Sequence sxbar_send_data_reply (lines 1-10) responds to data request transfers being


received at this port. Function iSJelevant() of this sequence is defined to return true if array
dataJeq_pkt_queue contained in its parent sequencer p_sequencer has any entries (line 4).
Task waiUouelevant() of this sequence is defined to return when function Is_relevant() suc-
ceeds. The If-statement allows this task to return immediately if function Is_relevant() suc-
ceeds at the current time.

Sequence sXbar_send_data_request (lines 12-21) generates data request transfers. Func-


tion ISJelevant() of this sequence returns false anytime there is a pending data request trans-
fer that has not yet been replied to (line 11). The definition of task waiUouelevantO for this
sequence is the same as that for sequence sxbar_send_dataJeply.

Note that function iSJelevantO returns true by default. As such, if a conditional


sequence scheme is implemented for a sequencer, then function is_relevant() of all new
sequences must be redefined to take into account the relevance scheme.

The above example provides a simple definition for when a sequence is relevant to the
verification context. The same approach can. however. be used to define any relevant criteria
as needed by the verification context.
Sequence Synchronization with End of Run 367

14.11 Sequence Synchronization


----_._--------------_...
with End of......Run
_--_ _----_.--- -------------_.----_
... __...._----------
Some verification scenarios may require that a chain of sequence items produced by a
sequence be fully processed by the driver before the simulation run phase is ended. In other
cases, verification scenarios may require that a reply to an outgoing sequence item be
received before the run phase is completed. The OVM class library provides a mechanism
for a component to raise an objection to ending the run phase of the hierarchy in which is
exists (section 7.4.2). This mechanism can be used to synchronize the end of run phase with
the operating state of sequences.
One example of this requirement is in the XBar module VC where a data transfer con-
sisting of two frames is generated. The goal is to prevent the run phase to end if a data trans-
fer is started but both frames have not yet been processed.
The following implementation shows the additional functions that are added to the
implementation of the module VC sequencer (program 14.11) to support this requirement:

IProgram 14.22: Implementing sequencer support for objection to end of run phase
class mxbar_sequencer extends ovm_virtual_sequencer:

2 function void raise_objectionO:


3 enable_stop_interrupt ++:
4 endfunction
5 function void drop_objectionO:
6 enable_stop_interrupt --;
7 endfunction
8
9 task stop(string ph_name="run");
10 while (enable_stop_interrupt) @(enable_stopJnterrupt):
11 endtask
12 endclass: mxbar_sequencer

Functions and drop_obJectionO increase and decrease the value of field


raise_objectionO
enable_stop_interrupt (a predefined member of class ovm_component, a parent class of
ovm_virtual_sequencer) respectively. The addition of these functions are necessary since field
enable_stop_interrupt is a protected field and as such, cannot directly be modified by
sequences in the sequence library of this sequencer. Function stopO is defined so that it com-
pletes when the value of enable_stop_interrupt is, or has reached, o.
The implementation sequence mxbar_seq_send_data_transfer (program 14.17) is modi-
fied as follows to prevent the run phase from ending after the generation of the first frame is
started and before the second frame is processed by the driver:

Iprogram 14.23: Sequence action block raising objection to end of run


class mxbar_seq_send_data_transfer extends ovm_sequence:

2 virtual task body():


3 mxbar_seq_send_frame seq_send_frame:
4 ovm_seq_consJf sci:
5
6 sci = p_sequencer.get_seq_consJf(dxfr.src_addr):
7 scLgrab(get_parent_seq()):
368 Verification Scenario Generation

8 p_sequencer.raise_objectionO;
9
10 'ovm_do_with(seq_send_frame, {xfr.kind==SOF;
11 xfr.dest_addr==dxfr.desCaddr; xfr.src_addr==dxfr.src_addr;})
12 'ovm_do_with(seq_send_frame, {xfr.klnd==dxfr.kind;
13 xfr.dest_addr==dxfr.desCaddr; xfr.src_addr==dxfr.src_addr;})
14 sci.ungrab(geCparent_seq());
15
16 p_sequencer.drop_objectionO;
17 #1000; /I walt for some time, optionally randomize by using a field instead
18 endtask
19 endclass: mxbar_seq_send_data_transfer

This behavior is achieved in the action block of this sequencer by calling function
raise_obJection() of its sequencer betore executing the first sequence item (line 8), and calling
function drop_objectlonO of its sequencer after executing the second sequence item (line 16).
Depending on the synchronization requirements of a sequence with the end of run
phase, the functions to raise or drop objections can be placed in the predefined callback
methods of a sequence. For example, if the run phase should not end while the action block
of a root sequence is executing, then functions to raise and drop objections can be placed in
tasks pre_bodyO and posCbodyO of that sequence. Figure 8.5 provides a view of the order in
which callback methods are called for root sequences, subsequences, and sequence items,
and can be used as a guide to deciding the best place for a sequence to raise and lower objec-
tions to ending the run phase.
369

PART 6
Assertion-Based Verification
370
CHAPTER 15 Property Specification
and Evaluation Engine

A major factor affecting verification productivity is how concisely verification intent can be
described. A concise description makes verification intent easier to specify and also easier to
understand. The need to describe a property, either in the design or the verification environ-
ment, is an inherent part of any verification activity. As such, the ability to write concise, yet
clear descriptions of design properties not only increases verification productivity, but also
improves verification environment reusability by allowing other engineers to better under-
stand the intent behind property descriptions.
The quality of a proprety specification language is measured in its expressiveness, a
measure that is meant to maximize the following competing priorities:
• The ability to model as complex a description as possible
• The ability to write as concise a description as possible
• The ability to write as readable a description as possible
As in any other natural or computer language, one's ability to reach a good balance
between these competing priorities is developed over time and through practice and experi-
ence.

To address the need for a powerful property specification mechanism, property specifi-
cation languages have been defined, either as independent standards (e.g., Property Specifi-
cation Language [PSLJ) or as part of hardware verification languages (e.g., temporal
expressions in the e language). SystemVerilog provides an extensive set of constructs for
writing property specifications.
Assertion-based verification (ABV) is a verification approach that makes extensive use
of property specifications. ABV methodology provides best practices for improving verifica-
tion quality by using assertions. A good understanding of how properties are specified is
essential for taking maximum advantage of the benefits provided by ABY. This chapter
describes in detail how properties are described in SystemVerilog. Assertion-based verifica-
tion, \,·here property specification is needed. is described in chapter 16.
372 Property Specification and Evaluation Engine

15.1 Property Specification Hierarchy


---------.-----~-----------.----------

A property, in its most abstract form, is a relationship between Boolean conditions across
multiple time steps. The simplest property is a single Boolean condition defined at one point
in time, and the most complex property is a relationship between multiple Boolean condi-
tions within one and across mUltiple time-steps. As such, a property specification language
shcmld provide language constructs for defining Boolean conditions based on design and ver-
ification environment signals, and for specifying relationships between these Boolean condi-
tions across multiple time-steps.
In addition to these constructs, a powerful property specification language should also
provide facilities for managing the following aspects of property specification:
• Ability to define a property based on Boolean conditions sampled (i.e., evaluated) at
multiple asynchronous (i.e., unrelated) clocks in order to facilitate properties defined
across multiple clock domains.
• Ability to define properties in a hierarchical fashion so that a complex property can
be constructed from simpler properties.
• Ability to write a property based on formal arguments so that a given specification
can be used in multiple places in the environment using different sets of actual sig-
nals replacing the formal arguments.
• Efficient ways to define when a property should hold (i.e., defining property sam-
pling event).
• Efficient ways to define when a property need not hold (i.e., disabling property).
SystemVerilog provides three abstraction layers for defining a property:
• Boolean Expressions
• Sequences
• Propeliies
Boolean expressions define conditions that are evaluated in zero time. In general, Sys-
temVerilog allows any expression that produces a Boolean result to be used as a Boolean
expression (section 15.2). In addition, SystemVerilog provides system functions (e.g.,
$rose()) to extract relevant Boolean conditions from signals in the environment (section
15.2.5). Sequences are composed of Boolean expressions and sequence operators, and are
used to define a relationship between Boolean expressions across multiple time-steps. Prop-
erties are composed of sequences and property operators, and provide a true or false result
indicating whether the given property was maintained throughout the simulation runtime.

Properties

Sequences

Boolean Expressions

Figure 15.1 Propel1y Specification Hierarchy


Property Specification Hierarchy 373

Sequences and properties have very different semantic meanings. The relationship
between sequences and properties is the same as the relationship between behaviors and
rules. Behaviors describe a manner of behaving or acting, while rules outline legal behaviors.
For example, a behavior for a reset signal may state that "the cpu reset signal stays active for
five clock cycles" while a rule about that reset signal may state that "the cpu reset signal must
stay active for at least five clock cycles." Rules often use some behaviors as qualifying con-
ditions for legal behaviors. For example, a rule may state that "if system reset becomes
active, then the cpu reset signal must stay active for at least five clock cycles." In this case,
the behavior "system reset becomes active" is used as the qualifying condition for the second
behavior.
Properties provide a mechanism for specifying rules about design behaviors described
with sequences. SystemVerilog sequences and sequence operators provide a powerful mech-
anism for specifying complex design behaviors, while SystemVerilog properties and prop-
erty operators allow legal relations to be specified between behaviors described by
sequences. In the cpu reset example, sequences are used to describe behaviors "system reset
becomes active" and "cpu reset stays active for five clock cycles," and properties are used to
specify the allowed relationship between these two behaviors.
In addition to this conceptual difference, SystemVerilog sequences and properties have
different evaluation models. For a given simulation trace, a sequence may match multiple
times in one or more evaluation cycles and fails only if it never matches. All the match con-
ditions computed for a sub-sequence are tracked as part of the evaluation and used to guide
the evaluation of the sequence containing that sub-sequence (section 15.3.2). Evaluation of a
property, however, produces only a true or false statement indicating whether the given prop-
erty was satisfied in any possible way. Consider a design having two reset signals. Assume
sequence 8 states that "either reset l or reset2 stays active for two to five cycles" and property
P states that "either resetl or reset2 must stay active for two to five cycles." Property P is sat-
isfied if either reset l or reset2 stay active for two to five cycles. This means that if one of
these conditions is observed, then the property can be considered as satisfied, and there is no
need to find out if the other condition is satisfied. However, if sequence 8 is used to describe
a more complex sequence, then all possible ways it can succeed must be found and consid-
ered.
The requirement to keep track of all possible matches for a sequence is illustrated in the
following example:
5f (Vl==10) ##1 [1:2] (Vl==20)
PI: 51
P2: 51 ##1 (Vl==30)
Trace Tr: (10,20,20,30)

Sequence 8 1 describes a behavior where variable VI is equal to 10 in the first evaluation


cycle, and equal to 20 in the following one or two evaluation cycles. As such, sequence 8 1
matches traces (10,20) and (10,20,20), thereby producing two matches for trace Tr at evaluation
cycles 2 and 3. Property PI is described as sequence 8 1 and succeeds for either trace (10,20) or
(10,20,20). Property P2 is described as sequence 8 1 followed in the next evaluation cycle with
variable VI being equal to 30, and therefore. succeeds for traces (10,20,30) or (10,20,20,30).
When evaluating property PI, only the first match of sequence 8 1 is suflicient for deciding
that the property succeeds. However. in evaluating property P2 • both matches of sequence 8 1
must be considered. since considering only the first match (i.e .. (10,20)) would result in miss-
374 Property Specification and Evaluation Engine

ing the actual success of this property for trace Tr. Clearly, if sequence 51 fails for any trace
(e.g., (10,50,20,30)), then neither property P1 nor P 2 will match for that trace.
Boolean expressions, sequences and properties are described in the following sections
of this chapter.

1~2!!.!!olea~_ Expre~~io1}s ___._.__._.__._. .__. . _________


Boolean expressions are the building blocks of sequences and property specifications. In
general, any expression that produces a Boolean result without any side effects I can be used
as a Boolean expression in a sequence or property specification. Such an expression is
treated in the same way as the expression in the ij-clause of an ij-statement, where a value of
1 is assumed to be true and values of 0, x, and z are assumed to be false.

Examples of valid Boolean expressions with different operand types are shown below:
( A && IB) /I A and B are bits
( cnt ==53) /I cnt is an integer
( arrayA == arrayB) /I arrayA and arrayB are 2-dimenslonal arrays
( A.X == B,Y) /I X and Yare integer members of structs A and B respectively

Boolean expressions, in the context of sequence and property specifications, are defined
by the following aspects:
• Allowed operand types
• Operators
• Sampling events
• Operand value sampling
• Sampled value functions
Sampled value functions are system provided functions that allow for writing condi-
tions based on changes in signal values, These topics are discussed in the following subsec-
tions.

15.2.1 Operand Types


Boolean expression operands can be one of literals (i.e., constants), variables, or function
calls. Literals with the following types are not allowed in Boolean expressions:
• Non-integer types (shortreal, real, realtime)
• string
• event
• chandle
• class
• Associative arrays
• Dynamic arrays.

I. A side effect, in this conte.\t. refers to a chang~ in the ,·alue of any data (l~ject in the em·ironment.
Boolean Expressions 375

Variables that return a valid operand type (i.e., variable types not in the previous list)
can be used in a Boolean expression. These variables, however, must be a static design vari-
able declared in programs, interfaces, clocking blocks, or tasks.
Function calls that return one of the valid types (i.e., types excluding those listed above)
can be used in a Boolean expression. However, the following semantic restrictions are
imposed on function calls used in a Boolean expression:
• Function arguments cannot be of type ref(const refis allowed)
• Functions should not contain any static variables
• Functions should not have any side effects (e.g., changing values in other scopes).
These restrictions are required in order to prevent side effects when a property is being
evaluated. Preventing side effects is needed since a property may get evaluated multiple
times in a single time-slot because of race conditions in its sampling event. And any side
effect in evaluation of a Boolean expression used in a property can, therefore, lead to unpre-
dictable behavior.

15.2.2 Operators
All operators that are valid for the operands types described in the previous section can be
used in Boolean expressions. However, operators that produce side effects when used in
expressions are not allowed. Disallowed operands are:
• Assignment operator (=)
• Increment operator ( ++)
• Decrement operator (--)

15.2.3 Sampling Events


The sampling event for a Boolean expression is the condition that causes the Boolean expres-
sion in a sequence or property specification to be evaluated. The sampling event for a Bool-
ean expression can either be explicitly specified or derived from the context in which it is
used. The discussion of the sampling event for a Boolean expression is the same as discus-
sion for sampling event for sequences (section 15.6).

15.2.4 Operand Value Sampling: Sampled Value vs. Current Value


Every simulation time-slot in the execution engine of a System Verilog simulator starts with
the preponed region, goes through at least one, and potentially multiple, passes from active
through to reactive regions, and ends by passing through the postponed region (section 5.1).
This flow implies that a signal value may change multiple times during a single time-slot.
In order to avoid race conditions in evaluating a property. and also to avoid evaluating a
property multiple times at the same simulation time, Boolean expressions in a sequence or
property are evaluated in the postponed region using the variable values sampled in the pre-
poned region This means that there is only one evaluation of a Boolean expression occurring
at a given simulation time-slot. and the values used are the stable values right before entering
376 Property Specification and Evaluation Engine

that simulation time-slot. It is, however, important to note that the current value of the sam-
pling event are used to decide when a Boolean expression is evaluated. This means that if the
sampling event of a property (and hence, Boolean expressions used in that property) makes
multiple transitions in a single simulation time-slot, it is possible to evaluate that Boolean
expression mUltiple times in a single time-slot.
Figure 15.2 shows how this approach provides a consistent behavior even when signals
used for evaluating a property change at the same time as the sampling event of a property. In
this figure, Boolean expression (@(posedge elk) elk) never evaluates to true. The reason is that
the current value of signal elk, which is set in the main scheduling loop of the current
time-slot, is used to detect the sampling event while the value of elk, sampled in the preponed
region before clock changed to 1, is used to evaluate the Boolean expression. Boolean
expression (@(posedge elk) lelk) evaluates to true for all sampling events because of the same
reason. The values of expression (@(posedge elk) A) at times 2, 3, 6, 7 and 8 show examples of
using variable values immediately before the sampling event to evaluate a property. This
example also shows the usefulness of the sampled values of variables used in a Boolean
I expressions, where this approach leads to predictable evaluations even when signals change
at the same time as the sampling event. Also, this approach is consistent with cycle-based
verification semantics where signal values are sampled before a clock edge (section 5.2).

11
elk
A

@(posedge elk) elk

Figure 15.2 Sampled Values in Property and Sequence Evaluation

It is important that the sampling event for all sequences and properties are glitch free
and change only once at each simulation time-slot. Using the current value for evaluating
sampling events means that a sampling event with race conditions (e.g., signal elk changing
multiple times in the same simulation time-slot) leads to multiple evaluations of a sequence
or property in the same time-slot. In this case, the sequence evaluation engine assumes the
next sampling event at a next simulation time to have arrived, when, in fact, simulation time
has not yet advanced. Given that properties are usually described in telms of consecutive
occurrences of sampling events, any such misunderstanding of the actual arrival of the sam-
pling event can lead to errors in correct evaluation of a property.

15.2.5 Sampled Value Functions


SystemVerilog provides special functions to help detect a change in sampled value, the cur-
rent sampled value, or the past sampled value of \'ariables. The use of these functions is not
Sequences 377

limited to sequence and property specifications, and can be used anywhere a function return-
ing a Boolean value can be used. These system functions and their syntax are:
$sampled( expr)
$rose(expr)
$fell(expr)
$stable(expr)
$past(expr [, number_oCsampling_events! [, gating_exprJ)

Function $sampled(expression) returns the sampled value of expression in the current


simulation time-slot. The value returned by this function is the value used for expr in evaluat-
ing a property in which expr is used.
Functions $rose(), Sfell(), and $stable() compare the sampled value of expr in the current
simulation time-slot to the sampled value of expr in the previous simulation time-slot when
the sampling event for the function occurred. Function $rose() returns true if the least signifi-
cant bit changed from zero to one. Function $fell() returns true if the least significant bit
changed from one to zero, and function $stable() returns true if the sampled value did not
change.
Function $past() returns the sampled value of expr at the time-slot for a previous sam-
pling event. Function $past() can be used to retrieve the sampled value of expr for any number
of sampling events in the past by specifying arguments number_oCsampllng_events and
gatlng_expr. The number of sampling events in the past is counted only for sampling events at
which gating_expr evaluates to true. Both these arguments are optional, with
number_oCsampllng_events having a default value of 1.

Figure 15.3 shows examples of values returned for these sampled value functions. Sig-
nal elk is used as the sampling event, and row 2 shows when this sampling event occurs. The
value for signal A (row 3) goes through all transitions (0 ..... 0, 0 ..... 1, 1 ..... o, 1 ..... 1) both at the ris-
ing edge of elk and at the falling edge of elk, thereby providing examples for all possible cor-
ner cases. Row 5 shows the value returned by function Ssample(). Note that the value returned
for the first sampling event is x since initially, all variables are initialized to x. Rows 6, 7, and
8 show transition values computed for signal A. Rows 9, 10, and 12 show values returned by
function $past(). Row 9 shows values returned for sampled value of signal A that is delayed
by one sampling-event. Row 10 shows values returned for sampled values of signal A that is
delayed by two sampling events. Row 12 shows the effect of using a gating expression to
decide which past sampled value of signal A to return. Note that the Boolean expression
shown in row 12 is still evaluated for every occurrence of the sampling event shown in row
2, but the number of sampling events counted into the past is taken from row II.
Boolean expressions are the building blocks of sequences and properties. The use of
Boolean expressions in writing sequences and properties are described in the following sec-
tions.

15.3 Sequences
----_..

Sequences specify behaviors that span zero or more simulation time. Sequences are con-
structed by using the ~lel1Ce delar opemtor (##) to specify what Boolean expressions must
378 Property Specification and Evaluation Engine

l_c\_k_ _ _ ._ _ _ _
2 @(posedge elk)
tl'] nnh nnnII
Sampling Event

3 A I -- I I

----_._-------_._-----
6 @(posedge ell<) $ros.(A)
.._.F__. _F.._-- _
F
....._--
T
.-.---.~-
F F
---.- F
_.._-- _._- .._-_. -'--
T F F

7 @(posedge elk) $tell(A) F F T F F F T

8 @(posadge elk) $.table(A) T F T F T F T F T F

_9~@~(~posed_:g~e_el~k)~$~P-_~~~~)______~·--~--·~-~----r--~--~--+_--r---r---r----

10 @(posedg.elk) $p.st(A, 2) x x x 0 0 1 1 0 0 1
-..--.. .----.-..-..---.-------.--.-.-.. .---.-..."--.--- .----.-.. . ..--.-- -.. ---.-- .-.--.. -.....-.. .- . --'-'--" -.---.-- --.. . -. r--- .--..-_..-
11 @(po ••dgeelkiffelk...!!at.)
--. ------r-.- _ . . 1--- ._--'--...- ...... 1---- .---- ..---..- ---.. '------ --.....-.-....-
12 @(posedge elk) Spa"t(A, 1, elk...!!_te)

Figure 15.3 Sampled Value System Functions

evaluate to true across consecutive sampling cycles. Evaluation cycles for a sequence are
defined by the sequence sampling event. For example, sequence (A ##1 e) indicates a condi-
tion where A is true in a given cycle and e is true in the following cycle.
A linear sequence is the simplest fom1 of a sequence and specifies Boolean conditions
that must evaluate to true in consecutive cycles (e.g., A ##1 B ##1 C). A linear sequence is said
to match when all Boolean expressions, as ordered by the unit delay operator, are true in their
corresponding cycles. Note that a sequence of type (A ##3 B) is also a linear sequence, since
this form indirectly states that in the three cycles from A to e, no Boolean condition is neces-
sary to be true in order for this sequence to match. In other words:
A ##3 B is equivalent to: A ##1 (1) ##1 (1) ##1 B where (1) matches any condition

All other sequence operators are essentially provided as efficient means of specifying
long linear sequences or composite sequences where a single expression represents a group
of linear sequences. For example, the delay repeat operator (e.g., A ##60 B) helps in defining
a very long linear sequence without explicitly writing the condition for each cycle. An "or"
sequence operator helps in writing one sequence expression that represents multiple
sequences. A composite sequence may represent zero, tinitely many, or infinitely many lin-
ear sequences. Table 15.1 shows examples of composite sequences each representing one or
more linear sequences.
The sequence operator that is used to create a composite sequence defines how the
results of individual sequences are combined in order to compute the match or fail condition
Sequences 379

Number of Linear
Composite Seq uence Linear Sequences Represented Sequences
Represented
A l·uJ ThIs IS an empty sequence. Doesn't match any trace. 0
A##I B A##I B 1
A##I B
A ##[1:3] B A ##1 (I) ##1 B 3
A ##1 (I) ##1 ('true) ##1 B
A##I B
A ##1 (I) ##1 B
A ##[1:$] B Infinite
A##1 (1)##1 ... ##1 (1)##1 B

Table 15.1: Composite Sequences Representing a Group of Linear Sequences

for the composite sequence. The evaluation model of sequences is described in section
15.3.2.

15.3.1 Sequence Declarations and Formal Arguments


Sequences can be written in-line as part of a property or assertion declaration. Sequences can
also be declared as a named sequence, which can then be instantiated in other sequences and
properties. Named sequences provide the following benefits:
• A declared sequence can be defined in terms of formal arguments, allowing declared
sequences to be reused for different sets of signals and repeat counts.
• A declared sequence can contain local variables, allowing a declared sequence's
matching conditions to be defined in tenns of state information stored in a local vari-
able.
• A declared sequence can be used to better organize the description of a complex sce-
nario, leading to more readable implementation of a sequence.
Sequences can be declared in the following blocks:
• Module block
• Interface block
• Program block
• Clocking block
• Package
Sequence instantiation refers to using the name of a declared sequence in writing other
sequences or properties. Such usage is equivalent to replacing the actual declaration of a
named sequence with the instance of that sequence, while replacing that sequence's formal
arguments with the actual argument provided in its instantiation.
The following program shows examples of sequence declarations and instantiations:

:Program 15.1: Named sequence declaration and instantiation


1 module top:
2 bit a, b, c, elk;
3 int datajn, data_out:
4
380 Property Specification and Evaluation Engine

5 initial begin for (int i = 0; i <= 10; i++) #1 clk = !clk ; end
6
7 sequence 51(AA);
8 AA ['2:4];
9 endsequence
10
11 sequence 52;
12 bit [7:0]local_var;
13 @(negedge clk) (51 (b), locaLvar = dataJn) ##5 (data_out == local_var);
14 endsequence
15
16 sequence 53 (5_CLK, AA, BB, CC, min1, max1, max2);
17 @(posedge 5_CLK) AA ##[min1 :max1] BB ##[1 :max2] CC;
18 endsequence
19
20 sequence 54;
21 53(clk, e, 51(a), (a==b), 1, 10, "$");
22 end sequence
23 end module

The following observations apply to the above examples. These topics are covered in
more detail in the following subsections.
• Sequences are specified by using sequence operators to combine Boolean expres-
sions and previously declared sequences (lines 8, 13, 17, 21).
• Sequence sampling events can either be specified explicitly or derived from the con-
text of where the sequence is used. In the above example, no sampling event is speci-
fied for sequence 8 1, As such, 51 derives its sampling event from its instantiation
context. The sampling event for sequence 8 1 is the negative edge of elk when 8 1 is
used in sequence 8 2 (line 13), positive edge of clk when 8 1 is used in sequence 8 3 (line
17), and positive edge of elk when 8 1 is passed to 8 3 as an actual argument to its
instance in sequence 8 4 (line 21).
• Sequence evaluation state can be one of "started", "matched", or "failed".
• Formal arguments can be defined for a sequence declaration in order to customize an
instance of a sequence to its instantiation context. Formal arguments of a declared
sequence can replace the following elements of a sequence declaration:
• Identifier: Identifier a as actual for formal argument AA of 53
• Sequence: Sequence 8 1(a) as actual for formal argument BB of 53
• Expression: (a==b) as actual for formal argument cc of 83
• Event control expression: elk as actual for formal argument 5_CLK of8 3
• Repeat range: Passing 1, 10, and "$" for formal arguments of 53
• Sequence declarations can include procedural code (including function calls) that
will be invoked when a given part of sequence matches (e.g., invoking (loeal_var =
data_in) when sub-sequence (8 1(b) on line 13 matches)).
• Local variables which persist throughout evaluation of a sequence can be used in a
sequence specification. These local variables simplify the transfer of information
from one part of a sequence to another (storing the value of data_In to be later used in
a Boolean expression on line 13).
• Any identifier used in a sequence declaration that is not a local \'ariable or a formal
argument is resolved according to the scoping rules of the block in which the
sequence declaration is placed. For example, variables data_in and data_out are not
local variables of sequence 52' As such, the variables used are the ones in the scope
Sequences 381

where sequence 52 is declared (line 3). This means that even if this sequence is used
in a property in a ditferent module (using its hierarchical name), variables data_in and
data_out from this module (line 3) are used.
• Formal arguments are type independent. As such, a sequence instance actual argu-
ment can have any type as long as the resulting sequence definition is legal.
Details of sequences are described in the following subsections.

15.3.2 Sequence Evaluation Model


Figure 15.4 shows the abstract evaluation model for a linear sequence. In this modeL an eval-
uation thread is started in the first cycle to check for the Boolean condition for the first sam-
pling period. Boolean expressions for each cycle are checked in consecutive cycles until
either one of the Boolean expressions is detected as false, in which case the sequence fails
and evaluation thread is terminated, or until all Boolean expressions in their corresponding
cycles are detected to be true and sequence matches. A linear sequence will match or fail
only one time.

time
~O~~1~~2~~3~-=4~__5~~6__~7__~~9
f'Aatq,ed ':;:valIJatiqn Tt"(reaq

time
7 :8 9
Fai1e:d Ev;aluation Threfld
Figure 15.4 Abstract Evaluation Model of Linear Sequence

Evaluating a composite sequence is more involved. Figure 15.5 shows the abstract eval-
uation model of a composite sequence. The sampling event cycles are shown on top, along
with their corresponding cycles. The overall result of sequence evaluation is shown on top,
where evaluation is started at cycle 0 (first cycle this evaluation is started) and in this exam-
ple ends at time 10. The evaluation result of a composite sequence is computed from the
results of evaluation threads for its sub-sequences. The operator used to create the composite
sequence from its subsequences defines how the overall evaluation of the sequence is
derived from the results produced by its evaluation sub-threads. For example, composite
sequence (5 = 51 or 52) has two evaluation sub-threads corresponding to sequences 51 and 52'
Sequence 5 is then said to match any time one of its sub-sequences matches.
In figure 15.5, the match or fail status of the overall sequence (shown on top) does not
depend directly on the match or fail condition of its sub-threads, since the actual results will
depend on the operator used to combine the sub-threads. The presentation of sequence oper-
ators in the following section wiIl describe each operator based on hO\\' it combines the
results of its evaluation sub-threads.
The following aspects define the abstract evaluation model of a composite sequence:
382 Property Specification and Evaluation Engine

Composite Sequence
Evaluation Result ------;?----\--~-~-L-~---.:b_~:____l,:__~-~~ time
Sampling cycle. - - - F-----T:..---,::....-....;::.--+--+-,:;--i---,.=----,::..-....;.=..j

Evaluation sub·thread 1 - -

Evaluation sub·thread 2 - -
~~~~~~~~=4~

Figure 15.5 Abstract Evaluation Model ofa Composite Sequence

• Sequence match condition: How match or fail results of evaluation sub-threads are
combined to produce match/fail results for the sequence
• Sequence termination condition: when evaluation thread is terminated
Sequence operators (section 15.3.3) will be defined, based on this view of the sequence
evaluation model.

15.3.2.1 Evaluation Sub-Threads and Multiple Matches


Depending on the operator used, evaluation of a composite sequence may match multiple
times. These mUltiple matches may occur at different cycles or even in the same cycle. For
example, sequence (8 = 8 1 or 8 2) may match twice in the same cycle if both 8 1 and 8 2 match
in the same cycle. Note that the evaluation sub-threads shown in figure 15.5 represent evalu-
ation threads of their corresponding sub-sequences, and each may contain multiple
sub-threads producing their evaluation results. As such, each sub-thread may match multiple
times and the effect of such mUltiple matches in sub-threads to the overall sequence result is
defined by the sequence operator used to combine the evaluation sub-threads.
The number of times a sequence matches is important only when a sequence is used in
building a larger sequence. Consider the following two examples:
51 or 52 (51. s2. 53 are multiple cycle sequences)
(51 or 52) ##1 s3 P2 i5 equivalent to: (51 ##1 s3) or (S2 ##1 53)

Only one match is sufficient for a property to succeed. As such, P1 is satisfied if either
51 or 52 produce a match, and it is not necessary to search for a second match. Therefore, only
the first match of sequence (51 or 52) is relevant when considering Pl' On the other hand, both
matches for sequences 51 and 52 are important in evaluating P2' The reason is that either one
of (51 ##153) or (52 ##153) may match, and ignoring one of the matches of the (51 or 52) may
lead to missed matched results.
Figure 15.6 shows a view of the evaluation flow for sequence (5: 51 ##1 52) started at
time 22, where sequences 51 and 52 are composite sequences. In this example, evaluation of
sequence 51 is started at time 22, producing matches at times 24, 28, and 29. Note that these
matches are produced because of the description of sequence 51 which is not shown in this
example. For every match of sequence 51, a new evaluation thread is started for sequence 52
in the cycle after the match for 51 occurred (using ##0 would start the evaluation for 52 in the
same cycle that the match for 51 occurred). An evaluation sub-thread is terminated ifit fails.
but such failure is not reported. The evaluation of sequence 5 started at time 22 fails if all
Sequences 383

evaluation sub-threads for evaluating 52 fail. In this example, if (51 ##1 52) is a property defi-
nition, then the property succeeds on the first match produced by evaluation thread for 52 (at
time 27). It~ however, this sequence is used in building a larger sequence (e.g., (51 ##152) ##1
53) then all matches produced by sub-threads started for evaluation of 52 (times 27, 31, 32, 33,
35) will lead to the start of an evaluation sub-thread for sequence 53.

2 23 24 25 26 ~7 28 29 .0 31 32 33 34 3
I
tim e
V r-...
0 1 2 3 4 5 6 71
~ I
/ ~
0 1 ~. I
I ~ I
V
0 1 2 3 4
\
51
i
~
1/
I
0 1 2 3 4 5 6 71 ti;j,e I \

~
~ A
Success L/_----'-T----'-I\~~ Fail ;,--,i ~
Multiple successes per evaluation are possible Evaluation fails only once if it never succeeds

Figure 15.6 Abstract Evaluation Model of Sequence (S] ##1 S2)

15.3.3 Sequence Operators


SystemVerilog sequence operators are listed in table 15.2, with highest precedence listed
first. Sequence operators can be either a base sequence operator or a composite sequence
operator. A composite sequence operator can be expressed in terms of base sequence opera-
tors. In the following subsections, the equivalent description of each composite operator in
terms of base operators is provided.
Some operators accept sequences as operands while others accept only Boolean expres-
sions. In the description of these operators, an 5 is used to represent a sequence operand
while a B is used to represent a Boolean expression operand. An operator accepting a
sequence operand can also accept Boolean expressions as operands. Sequence operators are
described in the following subsections.

15.3.3.1 Sequence Delay Repeat Operators


Sequence delgy repeat operator is used to specify a number of repetitions for the delay oper-
ator. The delay repeat operator can have one of the following forms:
384 Property Specification and Evaluation Engine

Type Operator Associativity Base/Composite


Delay Repelltlon ##n -_ ....
Sequence Repetition [*] [=] [->] ....--
Delay ## Left base
throughout right composite
Range within left composite
intersect left base
and left base
Boolean
or left base

Table 15.2: Sequence Operators

##n: n is an integer constant


##identifier Identifier is an object that evaluates to an integer at compile time
##( expression) expression evaluates to an integer at compile time
##[start:end] start and end are identifiers or expressions that evaluate to an integer
at compile time. "end" can be set to "$" meaning any number of cycles.

Note that the specified delay value should be a constant that results in an integer equal
to or larger than zero at compile time. The first three forms of the delay operator, as shown
above, represent the same form of a simple delay repeat operator with different means of
specifying a delay value. The last form shows a range delay repeat operator, which is a
shorthand notation for multiple linear sequences. Range delay repeat operators can be speci-
fied in terms of the simple form of delay, and the "or" sequence operator as follows:
8 1 ##[start:endl 8 2 (8 1 ##star! 8 2 ) or (8 1 ##(star!+l) 8 2 ) or ...
or (8 1 ##(end-1) 8 2 ) + (8 1 ##(end) 8 2 )

Examples of simple and range repeat operators and their equivalent sequences are
shown below:
8 1 ##3 8 2 8 1 ##1 (1) ##1 (1) ##1 8 2
where (1) is a Boolean expressions always evaluating to TRUE
8 1 ##[1 :2] 8 2 (8 1 ##1 B) or (8 1 ##1 (1) ##1 8 2 )
where match in either sequence will match the main sequence

A simple delay repeat operator results in a linear sequence and as such, the evaluation
model discussed in section 15.3.2 applies to sequences defined using the simple delay repeat
operator. A range repeat operator can be restated as the grouping of multiple linear sequences
using the "or" sequence operator. As such, the evaluation model of sequence repeat operators
follows that of the sequence "or" operator.

15.3.3.2 Sequence Repeat Operators


Sequence repeat operators are used to specify a repeat parameter for a Boolean expression or
a sequence, the same way a delay repeat operator specified a repeat parameter for the delay
operator. The following types of sequence repeat operators are provided in SystemVerilog:
(S) ['nJ Consecutive Repeat Operator applied to a sequence
(B) I=n] Non-Consecutive Repeat Operator applied to a Boolean Expression
(B) I->n] Goto Operator applied to a Boolean Expression

Sequence consecutive repeat operator specifies a sequence that matches a trace in


which s occurs n times in consecutive cycles. This operator can be applied to both Boolean
expressions and sequences. The match occurs at the cycle where the last required match ofs
Sequences 385

occurs. Applying the consecutive repeat operator is equivalent to concatenating sequence S a


total of n times using the unit delay operators (##1). In specifying a repeat operator, a range
can be used instead of a single value. In this case, the range will result in the sequence match-
ing any trace containing a match in the given range. The following shows examples of this
operator:
(8, ##1 8 2 ) [*3] (8, ##1 8 2) ##1 (8, ##1 8 2) ##1 (8, ##1 8 2)
(8, ##1 8 2) [*2:3] (8, ##1 82) ##1 (8, ##1 8 2) or
(8, ##1 8 2) ##1 ((8, ##1 8 2) ##1 (8, ##1 8 2)
(8) [*1 :$] 8 or (8 ##1 8) or (S ##1 8 ##1 8) or ...
Match 1 or more occurrence of S
(8) [*0] no match. Never matches.

Sequence non-consecutive repeat operator specifies a sequence that matches a trace in


which B occurs, possibly not consecutively, a total of n times. The resulting sequence
matches not only at the cycle where the nth occurrence of B matches, but also at every cycle
after that for which B does not match. This operator can be applied only to Boolean expres-
sions. The following shows examples of this operator:
matches at end of any of following example traces:
(8,.8,.8,)
(8,.8,.8 2 .8,)
matches at cycle where 3rd. 8 1 occurs and at any cyele after that:
(B,. 8 2. 8,. 8 2. B,. 8 2• 8 2. 8 2 )
(8) [=1:2] is equivalent to sequence (8 [=1]) or (8 [=2]) )

Sequence gota repeat operator specifies a sequence that matches a trace in which B
occurs, possibly not consecutively, a total of n times. This sequence matches only at the cycle
where the nth occurrence of B matches. This operator can be applied only to Boolean expres-
sions. The following shows examples ofthis operator:
matches at the end of following traces:
(8,.8,.8,)
(8,.8 2 .8 1.8 1 )
(8,.8 2 .8 1 .82 .8 1)
(8) [->1 :2] is equivalent to sequence ( 8 [->1]) or (8 [->2]) )

It is important to note that the non-consecutive and goto repeat operators produce differ-
ent result only when used as part of other sequences or properties. Consider the following
properties:

I Program 15.2: Sequence repeat operators


I

1 property p1;
2 @(posedge clk) a [=3];
3 end property
4
5 property p2;
6 @(posedge clk) a [->3];
7 end property
8
9 property p3;
10 @(posedge elk) a [=3] ##1 b;
11 end property
12
13 property p4;
386 Property Specification and Evaluation Engine

14 @(posedge elk) a [->3] ##1 b;


15 endproperty

Consider trace (a, c, a, a, c, c, b) occurring during time interval (1, 2, 3, 4, 5, 6, 7). Proper-
ties P1 and P2 both succeed at time 4, even though property P1 uses the non-consecutive repeat
operator and property P2 uses the goto repeat operator. The reason is that the sequence
expression for both these properties match time 4, and a property evaluation is terminated
after its first match. Property P3 succeeds at time 7 when b is true, but property P4 fails since
it requires b to occur immediately after the last cycle where a occurred.
Conceptually, sequences created by sequence repeat operators can be represented as a
group of linear sequences combined with the "or" sequence operator. As such, the evaluation
model of sequence repeat operators foHows that of the sequence "or" operator.

15.3.3.3 Sequence AND Operator


The sequence "and" operator has the following syntax:
S =51 and 52
Match condition: An evaluation of sequence 8 produces a match in any cycle where the
following conditions are satisfied:
• Sequences 8 1 and 8 2 both match at least once
• Sequence 8 1 matches at least once while sequence 8 2 has already matched at least
once in any of the previous cycles
• Sequence 8 2 matches at least once while sequence S1 has already matched at least
once in any of the previous cycles
An evaluation of sequence S produces only one match in any cycle, regardless of how
many times its operands match in that cycle. An evaluation of sequence 8 may, however,
match mUltiple times across its lifetime. If sequence 8 1 matches n1 times and sequences 8 2
matches "2 times, then an evaluation of sequence 8 produces less than (n1+n2) matches, and at
least mln(n1' n2) matches. The exact number of matches depends on the ordering between
matches from sequence S1 and matches from sequence 8 2 ,
Termination condition: Evaluation of sequence s terminates immediately if any of the
evaluation sub-threads fail. Otherwise, evaluation is terminated when both evaluation
sub-threads have completed.
Figure 15.7 shows the evaluation model of the sequence "and" operator. The evaluation
threads for each sub-sequence is shown inside the main evaluation box for sequence 5. As
shown in the figure, sequence S1 matches at times 3 and 10, and sequence 8 2 matches at times
2 and 8. Sequence 8 matches once at time 3 (for 8 1 match at time 3 and S2 match at previous
time 2), once at time 8 (for 8 2 match at time 8 and 51 match at previous time 3) and once at
time 10 (for 51 match at time 10 and S2 previous match at either time 2 or 8). The example
shows that evaluation of sequence s continues even after evaluation sub-thread for S2 is ter-
minated, since no fail was produced by that evaluation sub-thread.
Sequences 387

AND Sequence
Evaluation Result
Sampling cycles

Evaluation Thread S,

Evaluation Thread S,

Figure 15.7 Evaluation Model of AND Sequence Operator

15.3.3.4 Sequence OR Operator


The sequence "or" operator has the following syntax:
S =8 , or 8 2
Match condition: Sequence 8 matches when either evaluation sub-threads of 8 1 or 8 2
match. Sequence s may match mUltiple times in one cycle or at difference cycles, depending
on sequences 8 1 and 52' If sequence 51 matches "1 times and sequences 52 matches "2 times,
then sequence 8 produces ("1+"2) matches, one for every match produced by 51 and one for
every match produced by 52'
Termination condition: Evaluation of sequence 8 terminates when both evaluation
sub-threads have completed.
Figure 15.8 shows the evaluation model of the sequence "or" operator. The evaluation
threads for each sub-sequence is shown inside the main evaluation box for sequence 5. As
shown in the figure, sequence 8 1 matches at times 3, 8, and 10, and sequence 8 2 matches at
times 2 and 8. Sequence 5 matches once at times 2, 3, 8 (two times), and 10. The example
shows that evaluation of sequence 5 continues even after evaluation sub-thread for 52 is ter-
minated in order to wait for evaluation of 8 2 to complete.
1 match 1 match 2 matches 1 match

OR Sequence
Evaluation Result time
Sampling cycle.

Evaluation Thread S,

Evaluation Thread 8 2
~~~~~~~~~~~

Figure 15.8 Evaluation Model of OR Sequence Operator

15.3.3.5 Sequence INTERSECT Operator


The sequence "intersect' operator has the following syntax:
388 Property Specification and Evaluation Engine

8 = 8 1 intersect 8 2
Match condition: Sequence 8 matches when both evaluations of 81 and 8 2 match in the
same cycle. An evaluation of sequence 8 produces only one match in any cycle, regardless of
how many times its operands match in that cycle. An evaluation of sequence 8 may, how-
ever, match multiple times across its lifetime. If sequence 81 matches nl times and sequences
8 2 matches n2 times, then an evaluation of sequence 8 produces at most mln(nl1 n2) matches,
and possibly no matches if matches from 81 and 8 2 do not overlap. The exact number of
matches depends on the ordering between matches from sequence 81 and matches from
sequence 8 2 ,
Termination condition: Evaluation of sequence 8 terminates immediately if any of the
evaluation sub-threads completes with either a fail or match condition. The reason is that it is
no longer possible to produce a match if one of the sub-threads terminates.
Figure 15.9 shows the evaluation model of the sequence "intersect' operator. The eval-
uation threads for each sub-sequence is shown inside the main evaluation box for sequence
8. As shown in the figure, sequence 81 matches at time 3. Sequence 8 2 matches at times 2 and
8. Sequence 8 fails when evaluation sub-thread for sequence 8 2 terminates. In doing so, eval-
uation of sequence Sl is also terminated even though it is not completed yet.

AND Sequence
Evaluation Result
Sampling cycles

Evaluation Thread S 1

Evaluation Thread 52

Figure 15.9 Evaluation Model of INTERSECT Sequence Operator

15.3.3.6 Sequence THROUGHOUT Operator


The sequence "throughout" operator has the following syntax:
8 :: 8 throughout 8 1

This operator is used to specify that a Boolean condition must be true throughout the
evaluation of sequence 81' This operator is a composite operator and can be specified equiv-
alentlyas:
8:: (8) ['0:$] intersect (8 1 )

The first part of this expression matches for zero or more occurrences of Boolean
expression B. Sequence 8 matches if sequence 81 matches while Boolean expression B has
been continuously valid since the evaluation started. Sequence 8 either fails or matches the
same number of times that sequence 81 matches.
Sequences 389

15.3.3.7 Sequence WITHIN Operator


The sequence "within" operator has the following syntax:
8 = 81 within 8 2
This operator is used to specify that sequence 8 1 must match within the period that
sequence 8 2 matched. As such, assuming both sequence 8 1 and 8 2 match, the start of evalua-
tion for 8 1 can be no sooner than the start of evaluation for S2, and the end of evaluation for
8 1 can be no later than the end of evaluation for S2'

This operator is a composite operator and can be specified equivalently as:


8 = (1 ["0:$1 ##1 (S1) ##1 (1 ["0:$])) intersect (8 2 )

15.3.3.8 Sequence "first_match" Operator


The sequence ''jirsLmatch'' operator has the following syntax:
8 = first_match(81)
The sequence jirsLmatch operator is used to terminate the evaluation of sequence 8 1
after its first match is detected. Sequence 8 can match multiple times if sequence 8 1 has mul-
tiple matches in the first cycle in which its first match occurs.
This operator is used to limit the number of threads that are started in evaluating a com-
posite sequence. Consider sequence (firsCmatch(81) ##182) shown in figure 15.10. In this
example, evaluation of sequence S1 is terminated after its first match is identified at time 2.
Because of this termination, evaluations of 52 at times 7 and 8 are never started. Note that if
S1 had matched twice at time 2, then two evaluations of S2 would have been started at that
time.

o 2 3 6 7 3

a result of
of terminating
81, these two
evaluations

tQr,"';n,<it".rl at time

Figure 15.10 Evaluation ofjirsCmatch operator (flrscmatch(S1) ##1 S2)


390 Property Specification and Evaluation Engine

1S.:.1_§)!~~~m. ~~.~~.~~~J!!f?E~~~~~~ ...._... _. . ______. ._. . _._. ___.__ . . . ___. _._. __._
In SystemVeri log, design rules are described using the property construct. Properties are cre-
ated by using property operators to combine sequences and properties. Both sequences and
properties used to create a larger property can be either in-lined (i.e., written explicitly) or
instantiated (instance of a named sequence or property). The result of a property evaluation
is a true or false statement indicating whether or not the given property succeeded starting at
a given point in time.
SystemVerilog property declarations are used for:
• Checking whether or not a sequence produces any match.
• Combining the results of smaller properties using property Boolean operators
• Specifying conditions for when a property should be evaluated
A SystemVerilog base property is one that is expressed with a single sequence (note
that this sequence may in fact represent a very complex behavior)2. Property declarations are
then used to combine the result of base properties using Boolean operators and to specify
conditions for when a given property should be evaluated.

15.4.1 Property Declarations and Formal Arguments


Properties can be written in-line as part of an assertion declaration. Properties can also be
declared as a named Property, which can then be instantiated in other properties. Named
property declarations are very similar to named sequence declarations in that both can
include local variables and formal arguments. Named properties are, however, different from
named sequences in that they cannot be used where properties are not allowed.
Consider the following example:

'Program 15.3: Named property declaration and instantiation


1 module top;
2 bit a, b, c, clk;
3 initial begin for (int i = 0; i <= 10; 1++) #1 clk = !elk ; end
4 property P1 (AA); AA [*2:4]; endproperty
5 sequence 81 (AA); AA [*2:4]; endsequence
6
7 property valid_prop:
B @(negedge elk) disable iff (reset==1'bO) (81(a)) 1=> (P1(b));
9 endproperty
10
11 property invalid_prop1;
12 @(negedge elk) (P1(a)) 1=> (P1(b)); 1/ invalid property
13 endproperty
14
15 property invalid_prop2;
16 @(negedge clk) P1(a) ##1 S1(a); 1/ invalid property
17 endproperty
1B endmodule
'-------._--_._.. _---

:. :\ base propeny declaration is one lhat does not cllnlain any properly specific operators (I.e., not, impli-
cationl and does I1l't spe.:it~·ll1ullipk docks for clauses of Boolean operators (i.e., alld, or).
SystemVeriiog Properties 391

Sequence 51 and property P1 have the exact same definition (lines 4, 5). However, even
though property valid_prop (line 7) is a valid property, properties invalld_proP1 (line 11) and
invalid_proP2 (line 15) are not valid. The reason is that the implication operator (line 12)
accepts only sequences as an antecedent clause (section 15.4.3). Also, properties cannot be
combined using sequence operators, therefore sequence operator ##1 cannot be used to com-
bine property P1 with sequence 51 (line 16).

Named properties may include a disable clause, which contains a reset expression. If
the reset expression evaluates to true between the time a property evaluation is started and
when its evaluation is completed, then the property is assumed to be true. The reset expres-
sion is evaluated for every independent evaluation of a property (section 15.4.2). In the
above example, property valid_prop includes a disable clause with reset expression
"reset==1'bO". Disable clauses cannot be nested. In other words, a property, after collapsing
all its named properties, cannot contain more than one disable clause.

Properties can be declared in the following blocks:


• Module block
• Interface block
• Clocking block
• Package
• Compilation unit scope

Named property instantiation refers to using the name of a declared property in writing
other properties. Such usage is equivalent to replacing the actual declaration of a named
property with the instance of that property while replacing that property's formal arguments
with the actual argument provided in its instantiation.

Details of System Verilog properties are described in the following subsections.

15.4.2 Property Evaluation Model


Property evaluation produces either a true or false result. Evaluation of a property is said to
succeed if it evaluates to true, and to fail if it evaluates to false.

Property operators fall into three categories:


• Sequence evaluation
• Implication operators
• Boolean operators

Sequence evaluation is used to check whether or not a sequence matches at least one
time. A base property, used to evaluate a sequence, is declared by writing that sequence
in-line or using an instance of a named sequence in the property expression. Consider a prop-
erty (P1= 51)' The evaluation of property P1 consists of evaluating first_match(5d where prop-
erty evaluation succeeds if sequence 51 matches at least one time, and fails if sequence 51
produces no match. The evaluation of a base property started at a given cycle continues until
its sequence fails or matches once.

Figure 15.11 shows the abstract evaluation model ofa base property (evaluating a single
sequence). In this figure, evaluation of sequence 51 is shown to match twice at times 5 and 9.
392 Property Specification and Evaluation Engine

Evaluation of property P1 (defined as sequence 51), however, starts at time 0 and ends at time
5 when the first match of sequence 51 is observed. When evaluating property P1, the evalua-
tion of sequence 51 is terminated after its first match is observed, since the property has
already succeeded. Property P1 would fail if sequence 51 does not match at all. A property
evaluation succeeds or fails only once. This is in contrast with sequence evaluation, where a
sequence may match multiple times. As such, a property evaluation thread is identified by its
start time, its end time, and its final success or fail result.

time
o 2 3 4 6 7 8
~equ~nce ~1

o 2 3 4 6 7, 8
Prope~y P1:

property P 1; P, evaluation start time: time 0


81; P, evaluation completion time: 4
endproperty P, evaluation result: first_match(S,)

Figure 15.11 Abstract Evaluation Model of a Base Property

A composite property is created by combining base properties using property operators.


Figure 15.12 shows the abstract evaluation model of a composite property created by using
the "or" property operator. As can be seen, each evaluation sub-thread is identified with a
start time (which is the same as the evaluation start time for property p), an evaluation com-
pletion time, and a final result. In this figure, evaluation of property P1 starts at time 0, ends
at time 10, and succeeds. Also, evaluation of property P2 starts at time 0, ends at time 8, and
fails. The evaluation of the composite property ends at time 10 and succeeds. lfthe property
operator used in this example was (p1 and P2), then the evaluation ofthe composite property
would complete at time 8 and produce a fail result. Note that the evaluation thread of the
composite property is the same as the one for a base property, in that it is identified by a start
time, a completion time, and a single success or fail result. This means that the evaluation
model shown in this figure can be recursively applied to base and composite properties.

P = P1 or P2
Evaillation Result
Sampling cycles
time

P1 Evaluation sub-thread

P2 Evaluation sub-thread
~~~~~~~~=+~

Figure 15.12 Abstract E\'aluatio!1 tvfodeJ of a Composite Property


SystemVeriiog Properties 393

As described in this section, property evaluation can be described in terms of the fol-
lowing aspects:
• Property evaluation start time
• Property evaluation completion time
• How property results are combined to derive the result for a composite property
Implication and Boolean property operators (section 15.4.3) will be described in terms
of these aspects of property evaluation.

15.4.2.1 Evaluation Threads: Properties and Sequences


It is important to have a clear understanding of all the threads used to evaluate a composite
property. A composite property evaluation consist of the following thread types
• Property evaluation thread
• Property evaluation sub-thread
• Sequence evaluation thread
• Sequence evaluation sub-thread
Consider a composite property (p = P1 or P 2) created by using property operator "or" to
combine base properties P1 and P2 . A property evaluation thread is started at every cycle of
the sampling event for property P. Each property evaluation thread will then launch multiple
property evaluation sub-threads, one for each base property P 1 and P 2 • Each property evalua-
tion sub-thread will then launch a sequence evaluation thread (figure 15.12). If necessary,
each sequence evaluation thread will then launch multiple sequence evaluation sub-threads
for evaluating that sequence (figure 15.5). Note that the flow described for thread creation is
a logical view of the evaluation process and the actual implementation may in fact be differ-
ent.

15.4.2.2 Property Evaluation Start Points


At every sampling event of a property, a new evaluation of that property is started. This
means that at any time-and depending on the time it takes for a property evaluation to com-
plete-multiple evaluations of the same property started at different times may be active.
This is an important concept that should be carefully understood, since the success or failure
of a property reported in a cycle depends on the collective result of all evaluations running in
parallel.

Figure 15.13 shows an example of how multiple evaluation threads of a property started
at different cycles can lead to matches or fails at different times during the simulation run-
time. Rows 5 through II show evaluation threads started at times 1 through 7 for each new
sampling event of the property. As can be seen in this figure, at each sampling event, some
evaluations may fail, some may match, and some may be in the "evaluation started" phase.
For example, at time 5, the evaluation started at time 1 succeeds, and evaluation started at
time 5 fails. At time 11. both evaluations started at times 7 and 11 fail.
Success or failure of a property at a sampling event is the result of a combination of all
e,·aluations that complete in that sampling event. This means that in each cycle, only one
394 Property Specification and Evaluation Engine

1 elk
-----------------L~~~-+_~~_r~+_~~_+~~--

@(negedge elk) sampling event

3 A

4 B
·-·.. -·--·-·---·-----·-·-------------~~---+-~--_r~-+--~r~_+--~---

10

11

OVerall Result F

o 2 4 6. 10

Figure 15.13 Multiple Property Evaluation Threads

result is reported for all property evaluation threads, even if multiple threads of evaluation
fail or succeed in that cycle. The following rules apply to this evaluation:
• A property succeeds in a cycle, if and only if no property evaluation thread fails in
that cycle and one or more property evaluation threads succeed in that cycle (time 13
in figure 15.13).
• A property fails in a cycle if and only if one or more of its evaluation threads fail in
that cycle (time 5 in figure 15.13).
• In a given cycle, a property is in the "evaluation started" state if none of its evaluation
threads produce a result in that cycle (time 7 in figure 15.13).

The example in figure 15.13 is based on a linear sequence, producing a single success or
fail result for each evaluation thread. It is important to point out that any property, regardless
of its complexity, produces only a single success or fail result, and as such, the model above
can be used to compute the overall result for that property evaluation.

The following program segment based on values shown in figure 15.13, produces a
match at time 13, and fails at times 3, 5, and 11.

I Program 15.4: Multiple evaluation threads during property evaluation


,

1 sequence s_abb;
2 a ##1 b ##1 !b: /I a linear sequence
3 endsequence
4
5 property p_abb ;
SystemVerllog Properties 395

6 @(negedge clk) s_abb; 1/ a base property (made up of a single sequence)


7 end property
8
9 env_prop: assert property (p_abb)
10 $display("PASS",,$time);
11 else
12 $display("FAIL",,$time);

15.4.2.3 Properties and Degenerate Sequences


System Verilog sequences can be classified based on the number of matches they can pro-
duce. These categories are:
• Sequences that never match
• Sequences that produce only an empty match
• Sequences that produce a non-empty match
A sequence that never matches is a sequence that fails for every possible simulation
trace. Some examples of sequences that never match are shown below:
S1: (a == la); /I never matches since the Boolean expression can never be true
S2: (a intersect (b ##1 c»; /I never matches since Intersect operands must have the same duration

An empty match refers to a match for an empty trace 3. An empty match is usually used
to specify cases where a sub-sequence can appear zero or more times in a larger sequence. As
such, the ability to define an empty match is introduced in order to allow for more concise
sequence descriptions. Consider the following example:
S1: (a) or (a ##1 b)
Si (a ##1 b ['a]) or (a ##1 b)
S3: a ##1 b['0:1]

Sequence 8 1 is defined as Boolean expression (a), or Boolean expression (a) followed by


Boolean expression (b) in the following cycle. Sequence 8 2 makes use of the empty match
sequence (['Olb) to rewrite the description of sequence 8 1 into a form that can be further sim-
plified into sequence 8 3 which is a more concise description of sequence 8 1,
Seguence degeneracy is defined in terms of what number of matches a sequence admits.
Table 15.3 shows the definition for degenerate and non-degenerate sequences.

Admits Admits
Sequence Type Example
Empty Match Non-Empty Match
Stnctly Degenerate no no A mtersect B [*2]
Degenerate yes no A [*0]
Non-Degenerate yes yes A [*0:2]
Strictly Non-Degenerate no yes A [*1:2]

Table 15.3: Degenerate and Non-Degenerate Sequences

3. A simulation trace is a set of signal values in consecllti\'e cycles (e.g .• {a.b.c,a,b}). An empty trace
refers to the absence of any cycl~s. For ~xample. ifl l. T~. and T3 are three simulation traces and trace T2
is an empty trace. then [race :TI:r~.T;}lc()nclltellati()1l of these three traces) is the same as trace {TI>13}.
396 Property Specification and Evaluation Engine

SystemVeriiog enforces the following restrictions for using sequences in writing prop-
erties:
• A sequence used to define a base property should be strictly non-degenerate.
• A sequence used as the antecedent of an overlapping implication (1-» should be
non-degenerate or strictly non-degenerate. In other words, it has to admit a
non-empty match.
• A sequence used as the antecedent of a non-overlapping implication (1=» should not
be strictly degenerate. In other words, it has to admit at least one match even ifit is an
empty match.

Overlapping and non-overlapping implication operators are further discussed in section


15.4.3.

15.4.3 Property Operators


SystemVeriiog property operators are listed in table 15.4. Property operators fall into two
major categories:
• Boolean operators
• Implication operators
Boolean operators are used to combine the results of their operands. Implication opera-
tors are used to control when a given property should be evaluated.

Operator Type Operator Associativity


not ----
Boolean and left
or left
if-else right
Implication 1-> right
1=> right

Table 15.4: Propelty Operators

These operators are described in the following sections. The description of these opera-
tors is based on the property abstract evaluation model presented in section 15.4.2. Given a
composite property P created by using a property operator to combine operand properties
(e.g., in (P = PI and P 2 ) property operator "and" is used to combine operand properties PI and
P 2 to create property P), and assuming that a property evaluation thread is started at the cur-
rent cycle for property P, each operator is described in terms of:
• Start time of property evaluation sub-threads for operands properties P1, P 2.
• End time of evaluation thread for property P
• Evaluation result for property P as a function of evaluation results for its operands
SystemVerilog Properties 397

15.4.3.1 Boolean Operators


SystemVeri log property evaluation results can be combined using the full set of Boolean
operators. These include:
• "not" property operator
• "and" property operator
• "or" property operator
The "not" Boolean operator (P: not (P 1)) is defined as follows:
• Sub-thread start time: Evaluation sub-thread for property P1 is started when evalua-
tion of property P is started.
• End time: Evaluation of property P is completed when evaluation of property P 1 is
completed.
• Result: The evaluation result for property P is the Boolean complement of evaluation
result for property P1 •
The "or' Boolean operator (P: P1 or P2) is defined as follows:
• Sub-thread start time: Evaluation sub-threads for properties P1 and P 2 are started
when evaluation of property P is started.
• End time: Evaluation of property P is completed either when evaluation for both
properties P1 and P2 complete (when both P1 and P2 fail) or when evaluation of either
properties P1 or P2 complete (when either P 1 or P 2 succeed).
• Result: Property P succeeds if either of properties P1 or P2 succeed. It fails otherwise.
The "and" Boolean operator (P: Pi and P2) is defined as follows:
• Sub-thread start time: Evaluations of sub-threads for properties P1 and P2 are started
when evaluation of property P is started.
• End time: Evaluation of property P is completed either when evaluation for both
properties P1 and P2 are completed (when both P 1 and P2 succeed) or when evaluation
of either properties P1 or P2 complete (when either Pi or P2 fail).
• Result: Property P succeeds if both properties P 1 and P2 succeed. It fails otherwise.

15.4.3.2 Implication Operators


The evaluation result of a property is used as a condition for other verification activity. For
example, the evaluation result of a property used in an assert statement is used to decide
whether the pass or fail statement of its action block should be executed. Under some cir-
cumstances, however, the evaluation result of a property may not be relevant to correct oper-
ation of the design and must be ignored. These conditions fall into two categories:
• Success of a property evaluation is obviated by other concurrent events in the simula-
tion environment (e.g., a reset condition).
• Success of a property e\'a\uation is required only when a qualifying condition has
occurred.
The first category of conditions includcs situations where a property evaluation has
already started but concurrent circumstances make the result of that property evaluation irrel-
evant. For example. a propeny e'"aluation for a system bus may have already been started
398 Property Specification and Evaluation Engine

when a reset condition is detected. Similarly, a property evaluation for a cache coherency
protocol may have already been started when the cache is flushed. In both cases, the result of
the already started evaluation is no longer relevant. SystemVerilog provides the disable
clause (section 15.4.1) to handle such situations. The evaluation ofa property is terminated
when the qualifying condition for the disable clause is satisfied any time after the evaluation
of that property is started.
A more commonly occurring situation, however, is when the success or failure of a
property is meaningful only when a qualifying condition has already occurred. Such circum-
stances can be identified anywhere from properties defined for individual wires of a design
all the way to properties defined for system level behaviors. For example, at the bit level, the
address lines of a bus must hold valid binary values only when the bus is in a read or write
mode. At the system level, the burst size of a memory bus should be of a given size only
when the device was initially configured to operate with that burst size. SystemVerilog pro-
vides the implication operators for specifying such conditional design behaviors.
Implication operators introduce the notion of vacuous success of a property. A property
that succeeds vacuously is a property whose qualifying condition is not met and, therefore,
the evaluation result of the property attached to the qualifying condition is found to be irrele-
vant to the correct operation of the design. A vacuous success of a top level property (e.g., a
property at the top level of an assert or cover statement) is treated as though that property
was never evaluated. For example, if a property used in an assert statement succeeds vacu-
ously, then neither the pass nor fail statements of that assert statement are executed. Vacuous
success of properties that are used as operands to form a larger property are treated as real
successes. For example, a vacuous fail of property P1 in property P defined as (P: not (P 1))
results in fail of property P.
SystemVerilog provides the following implication operators:
• Overlapping implication: 1->
• Non-overlapping implication: 1==>
• if-else operator
Property over/aoping implication operator has the following form:
P: S1 1-> P 1
The left-hand side operand of the overlapping implication operator is called the ante-
cedent. Only sequences can be used as the antecedent expression. The right-hand side of the
implication operator is called the consequent. Antecedent is the qualifying condition, while
the consequent is the property to be evaluated. The operation of overlapping implication
operator is defined as follows:
• Sub-thread start time: Evaluation sub-thread for sequence 51 is started when evalua-
tion thread for P is started. An evaluation thread for property P1 is started/or eve!],
match of sequence 51 and in the same cycle that the match was produced.
• End time: Evaluation of property P is completed when either sequence 51 fails, or
when evaluation of sequence 51 completes and every eyaluation sub-thread that was
started for property P 1 completes.
• Result: E\"aluation of property P succeeds vacuously if sequence 51 fails. Otherwise.
evaluation of property Poi fails if any of the evaluation threads started for property P 1
fail. Otherwise. e\"aluation of property Poi succeeds.
SystemVeriiog Properties 399

Property non-overlapping implication operator has the following form:


P: 5 1 1=> P1

As with the overlapping implication operator, only sequences can be used as the ante-
cedent expression. The operation of a non-overlapping implication operator is defined as fol-
lows:
• Sub-thread start time: Evaluation sub-thread for sequence 8 1 is started when evalua-
tion thread for p is started. An evaluation thread for property P1 is started/or every
match of sequence 8 1 and in the next cycle that the match was produced.
• End time: Same as that for overlapping implication
• Result: Same as that for overlapping implication
The if-else operator has the following form:
P: if (8) P 1 [else P2] /I else P2 is optional

Only a Boolean expression is allowed as the if-clause. The operation of an if-else oper-
ator is defined as follows:
• Sub-thread start time: Evaluation sub-thread for property P1 is started when evalua-
tion thread for P is started and only if Boolean expression B evaluates to true. Evalua-
tion sub-thread for property P2 is started when evaluation thread for P is started and
only if Boolean expression B evaluates to false.
• End time: Evaluation of property P is completed when evaluation of either property
P1 or property P2 , whichever was started, is completed.
• Result: Evaluation of property P succeeds if B is true and property P1 succeeds or B is
false and property P2 succeeds. Otherwise, evaluation of property P fails.
Implication operators are different from other property operators in that they include an
implicit requirement that the subsequent property should succeed for every match of the
antecedent sequence. This is in contrast with base property evaluations where only the first
match of a sequence leads to the property succeeding. This behavior of the implication oper-
ator can be leveraged to gain information about all matches produced by a sequence. Con-
sider the following program segment:

~ 15.5: Implication operator showing every match of consequent clause


1 module top;
2 bit a, b, c, clk;
3
4 sequence ss;
5 (a ##[1 :$] b);
6 endsequence
7
8 property all_matches(tt, seq);
9 ($time ==tt) 1-> seq 1-> (1, $display(t,,$time));
10 end property
11
12 env_prop: assert property (@(negedge clk) all_matches(4, ss));
13 endmodule

In this example, property all_matches is defmed to print the time for all matches of
sequence seq for evaluations started at time tt lIines 8-10). The declaration of this property
takes ad\'antage of the overlapping implication operator. The first antecedent is used to pro-
duce a vacuous success for any time other than time tt. This means that property evaluation
400 Property Specification and Evaluation Engine

continues only for a start time of tt. If the current simulation time is the same as tt, then the
evaluation of sequence seq is started in the same cycle (because of the use of an overlapping
implication operator). Sequence seq is the antecedent of the next nested implication operator.
As such, for every match of sequence seq, the consequent property is evaluated. In this case,
the consequent property always succeeds, since it is simply a constant value 1, and upon suc-
cess, displays the current simulation time. Note that since an overlapping implication opera-
tor is used, the success time of property is the same as the cycle where the corresponding
match for sequence seq was produced. Property all_matches is used on line 12 to print the
time at which a match occurred for all matches of sequence ss started at time 4.

SystemVerilog provides a rich set of sequence and property operators. These operators are
meant to provide an intuitive set of constructs for specifying complex behaviors. This means
that some operators can be expressed in terms of other operators. This is similar to Boolean
expressions where an XOR operation can be defined in terms of AND, OR, and NOT opera-
tions, but is provided as an operator since an intuitive behavior can be attached to an XOR
operation.
Base sequence and property operators are shown in table 15.5. All other sequence and
property operators can be defined in terms of the operators shown in this table.

Type Name Syntax


Concatenallon SI ##1 S2
Fusion SI ##0 S2
Disjunction SI or S2
Sequence Intersect S I intersect S2
First Match firsunatch(S)
Null Repetition S [*0]
Unbounded Repetition S [1:$]
Negation not P
Disjunction PI or P2
Property
Conjunction P I and P2
Overlapping Implication S 1-> p

Table 15.5: Sequence and Property Base Operators

Tables 15.6 and 15.7 show derived sequence and property operators and their imple-
mentation in tenns of base operators.
When learning SystemVerilog sequences and properties, the focus should be on learn-
ing how to use each property in an intuith'e \\'ay. However. as behavior complexity grows.
understanding the operation of base operators allo\\'s for better insight into how each derived
operator is expected to behave.
Multi-Clock Sequences and Properties 401

Name Syntax Base Implementation


Consecutive Repeat 8 [*mJ 8 ##1 8 ... ~ 11#1 81/ repeats m times
Consecutive Repeat Range 8 [On:m] 8[On] or 8[On+l] or ... or 8 [*m-1.] or 8[*m] Ilm-n+ I clauses
Goto Repeat B [->n:m] (iB [*0:$] ##1 B) [*n:m]
Non-consecutive Repeat B[=n:m] ( iB [*0:$] ##1 B) [On:m] ##1 iB [*0:$]
Delay Range 8 1 ##[*n:m] 8 2 8 1 ##1 1[*(n-I):(m-I)] ##18 2
Conjunction 8 1 and 8 2 « 8 1 ##1 I [*0:$]) intersect 8 2 ) or ( 8 1 intersect ( 8 2 ##1 1[*0:$]))
Within 8 1 within 8 2 (\(*0:$] ## I 8 I ## I I [*0:$]) intersect 8 2
Throughout B throughout 8 ( B [*0:$]) intersect 8

Table 15.6: Sequence Derived Operators

Name Syntax Base Implementation


Non-overlappmg Implication ~ I' >P (8##11 )I->P
if if (B) P (B 1-> P)
if-else if(B) (PI) else (P 2) ( B 1-> PI ) and ( !B 1-> P2 )

Table 15.7: Property Derived Operators

15.6 Multi-Clock Sequences and Properties


In SystemVerilog, a clocked sequence or property can be created by pre-pending that
sequence or property with a clocking event. In tum, these clocked sequences and properties
can be combined using sequence and property operators to create multi-clocked sequences
and properties. Some examples of multi-clocked sequences and properties are shown below:
SCI: @(posedge clk) SI IIS 1 does not include any clocking specification
Sci @(negedge elk) S2 IIS 2 does not include any clocking specification
Sc12: SCI ##1 Sc2
Pc12: @(posedge clk) SI or@(negedge clk) S2

Sc1 and Sc2 are singly clocked sequences, Sc12 is a multi-clocked sequence, and Pc12 is a
multi-clocked property (using different edges of signal elk).
The last clocking event in a cascaded set of clocking events overrides all previous
clocking events. Sequence Sc1 in the following example is in fact a singly clocked sequence
whose behavior is equivalent to sequence Sc2'
ScI: @(posedgeclk3)@(posedge clk2) @(posedge clk1) SI
Sc2: @(posedge elk1) SI

System Veri log imposes restrictions on how clocked and multi-clocked sequences and
properties can be created and combined using operators. All such restrictions are specified in
order to enforce a set of semantic requirements on multi-clock sequences and properties.
The following section describes the semantic requirements of multi-clock sequences
and properties, and the resulting restrictions on how sequence and property operators can be
used.
462 Property Specification and Evaluation Engine

15.6.1 Semantic Requirements


SystemVerilog imposes a set of semantic requirements on multi-clock sequences and proper-
ties. Understanding these semantic requirements clarifies the reason for restrictions on how
sequence and property operators can be used to create multi-clock properties and also creates
a more intuitive understanding of the types of multi-clocked sequences and properties that
can be created in SystemVerilog.
SystemVerilog multi-clock semantic requirements are:
Merge rule: All sequence evaluation sub-threads whose results are combined to com-
pute the result of a composite sequence must be singly clocked sequences that have
the same clocking event.
Concatenation rule: In transitioning from one clocking event to another while evalu-
ating a multi-clock sequence, the first cycle of a new clocking event should be strictly
after the last cycle of the previous clocking event (they cannot overlap).
Repeat rule: The clocking event used for the next evaluation cycle of a sequence
should be strictly known. In other words, no ambiguities should exist in what the next
clocking event will be.
The first requirement affects merging of concurrently running evaluation threads. For
example, in evaluating sequence Sc defined as (Sc1 intersect Sc2), both sequences sc1 and Sc2
mLlst be singly clocked sequences that are clocked using the same clocking event. The reason
is that the evaluation results of sc1 and Sc2 are combined to decide whether or not sequence
Sc matches or fails.

The second requirement affects how sequences and properties can be concatenated.
Consider the following examples where S1 and S2 are un-clocked sequences:
Scf @(clk 1) S1
Sc2: @(clk2) S2
SScf Sc1 ##1 Sc2 Illegal
SSc2: Sc1 ##0 Sc2 lIillegal
PPc1: Sc1 1-> Sc2 lIi11egal
PPd Sc1 1=> Sc2 Illegal
PPc3: @(clk1) S1 1-> (@(clk1) S3 ##1 @(clk2) S2) I/Iegal

In the above examples, SSc2 is not a valid multi-clocked sequence. The reason is that in
using the fusion operator (##0), the first evaluation cycle of sc2 overlaps the last evaluation
cycle of sch requiring that the clocking event for the last cycle of sc1 to be the same as the
clocking event for the first cycle of Sc2' And this requirement is violated since sc1 and sC2 use
different clocking events. Similarly, property PP c1 is an invalid multi-clocked property. The
reason is that in using the overlapping implication operator (1-», the first evaluation of
sequence Sc2 starts in the last evaluation cycle of sequence sc1 and this violates the concate-
nation requirement since sc1 and sc2 use different clocking events. Property PP c3 is, however,
a legal multi-clocked property since the consequent clause is a valid multi-clocked sequence,
and also the sampling event of the first cycle of consequent (i.e., elk 1) is the same as the
clocking event of the last cycle of the antecedent (elk 1).
The third requirement affects how repeat operators can be used in concatenating
sequences. For example, assuming sequences sc1 and Sc2 are singly-clocked sequences with
different clocking events, sequence Sc defined as (SC1 ##1 SC2) is an invalid sequence if
sequence sc2 permits empty matches (e.g., Sc2 = A[*O:1]). In this case, upon an empty match of
Multl.Clock Sequences and Properties 403

sequence Sc2' it is not clear whether the last cycle of Sc ends on the clocking event for
sequence Sc1 or the clocking event for sequence Sc2.
The semantic requirements discussed above impose restrictions on the clocking proper-
ties of the operands to sequence and property operators. These restrictions are summarized in
table 15.8.

Type Name Syntax


ConcatenatIOn Sel ##1 Se2 No Restrictions
Fusion Sel ##0 Se2 LCLK(Scl) - FCLK(Sd
Both singly clocked. CLK(Scl) =
Disjunction ScI or Se2 CLK(Se2)
Both singly clocked. CLK(Sel)-
Conjunction Sci and Se2 CLK(Sd
Both singly clocked. CLK(Sel) =
Intersect Se I intersect Se2
CLK(Sc2)
Sequence
Both singly clocked. CLK(Sel)-
Within Se I within Se2
CLK(Se2)
Throughout Be throughout So Se singly clocked. CLK(Be) = CLK(So)
First Match first_match(Sc) No Restrictions
Only allowed when clocking events for the
Null Repetition Sc [*0] previous and next sequences are the same
as CLK(Se)
Unbounded Repetition Se [1 :$] No Restrictions
Negation not Pc No Restrictions
Disjunction Pel OrP c2 No Restriclions
Conjunction Pc! and Pe2 No Restrictions
Property Overlapping Implication Se 1-> Pc LCLK(Sc) - FCLK(P c)
Non-overlapping Implication Sc 1=> Pc No Restrictions
if if (Be) Pc CLK(B c) - FCLK(P e)
if-else if (Be) (P cI) else (Pc) CLK(Bc) = FCLK(P el) = FCLK(P d
Be IS a clocked Boolean expression, SC IS a clocked sequence, Pc IS a clocked property
CLK(Se): Clocking event for singly clocked sequence Sc
FCLK(Sc) : Clocking event for the first cycle of multiple-clocked sequence Sc
LCLK(Se) : Clocking event for the last cycle of multiple-clocked sequence So

Table 15.8: Multi-Clock Rules for Sequence and Property Operators

The following observations summarize the restrictions listed in this table:


• The concatenation sequence operator can be used to combine any singly or
multi-clocked sequences.
• The clocking event for the first cycle of the second tenn in the fusion sequence oper-
ator should be the same the clocking event for the last cycle of its first tenn.
• All operand sequences for sequence disjunction, conjunction, intersect, within, and
throughout operators must be singly clocked sequences that have the same clocking
event.
• Null sequence repetition can be used only in places where a null match does not
result in ambiguity about the next clocking event. This means that the clocking event
of the last cycle before a null match and the clocking event ofthe first cycle after a
404 Property Specification and Evaluation Engine

possible null match should be the same clock.


• Property Boolean operators can be used to combine singly or multiple clocked prop-
erties with no restrictions. The reason is that property Boolean operators depend on
only the true or false result of each operand, and exact timing of their evaluation is
not relevant to the final result.
• For an overlapping implication property operator, the first cycle of the consequent
overlaps with the last cycle of the antecedent. As such, the clocking event for the first
cycle of the consequent must be the same as the last clocking cycle of the antecedent.

15. 7 Sequence and Property Dictionary -~---

Describing a design property consists of two steps:


• Using the sequence construct to specify design behaviors
• Using the property construct to specify the allowed relationship between behaviors
described using sequences
These topics are covered in the following sections.

15.7.1 Sequence Dictionary


Design be!"'aviors are described using sequences. Two types of elements are used in describ-
ing a sequence:
• Condition: A condition is described by a Boolean expression. It takes zero time to
evaluate, and therefore has no beginning and no end time.
• Behavior: A behavior is described by a sequence. A behavior takes more than one
evaluation cycle to complete and as such has a beginning cycle and an end cycle.
The use of one multi-cycle behavior in describing a more complex behavior should be
clear as to the intended relationship between the timing between these behaviors. Consider a
bus read operation that takes multiple cycles to complete and has a start time and an end
time. For some behaviors, only the end time of this bus read cycle is relevant. For example,
the starting cycle of the bus read operation or how many cycles it took is not relevant to the
expected behavior that "a read-reply operation should take place only after a bus read cycle
has occurred." For other behaviors, however, the start time and duration of the bus read oper-
ation must be known. For example, the starting cycle and duration of a bus read operation is
relevant to the expected behavior that "signal rd should be active throughout a bus read
cycle."

If only the end time of a sub-behavior is relevant in describing a more complex behav-
ior, it is a good practice to separate the evaluation of this sub-behavior from the evaluation of
the more complex behavior. This can be accomplished by using either SystemVerilog's pre-
defined sequence methods (i.e., ellded(), matchedO. triggeredO)' or by using the action
block of a property that evaluates the sub-behavior to set an appropriate flag. In either case,
the effect of this separation is that the success of failure of a sub-behavior can be treated as a
Sequence and Property Dictionary 405

condition that takes zero time to evaluate, thereby reducing the complexity that must be dealt
with in writing sequences.

Examples of common design behaviors and their corresponding implementation using


sequences are described in the following subsections.

15.7.1.1 Sequences Based on One Condition


The following behaviors depend on a single condition. Condition B in the following behav-
iors can be any valid sequence Boolean expression (section 15.2).
Condition B holds for one cycle
This behavior states that condition B is observed for one cycle. The use of this sequence in a
property with a sampling clock implies that this condition must hold for every occurrence of
the property sampling clock.
Sequence:
(8)

Condition B holds for exactly N cycles


This behavior states that condition B holds for N evaluation cycles but not for more than N
cycles.
Sequence:
(B) [*N) ##1 !(B)

Condition B holds for at least N cycles


This behavior states that condition B holds for N evaluation cycles and the condition mayor
may not repeat in the following cycles.
Sequence:
(8) [*N]

Condition B holds for at most N cycles


This behavior states that condition B holds for any number of cycles between 0 and N consec-
utive cycles but not after that.
Sequence:
(8) [*O:N) ##1 !(B)

Condition B holds for [N1:N21 cycles


This behavior states that condition B holds for anywhere between N1 to N2 consecutive cycles
but not more than N2 cycles.
Sequence:
(B)["N1 :N2J ##1 !(B)

Condition B occurs within M cycles from now


This beha\'ior states that condition 8 holds once \\'ithin the next M cycles.
Sequence:
(1) ##[O:M] (B)
406 Property Specification and Evaluation Engine

Condition B occurs within M cycles from now and then holds for at least N cycles
This behavior states that the first occurrence of condition B is within the next M cycles and it
holds for N consecutive cycles after its first occurrence.
Sequence:
(1) ##[O:M] (B)[*N]

Condition B occurs [M 1:M 2] cycles from now and then holds for [N1:N21 cycles
This behavior states that even if condition B occurs in the first M1 cycles, it also occurs within
M1 to M2 cycles and after this occurrence, it holds for N1 to N2 consecutive cycles.

Sequence:
(1) ##[M1 :M2] (B)rN1:N2)

Condition B occurs first within [M1:M2] cycles and then holds for [N 1:Nz] cycles

This behavior states that condition B does not occur in the first M1 cycle and it does occur
within M1 to M2 cycles, and after this occurrence, it holds for N1 to N2 consecutive cycles.
Sequence:
liB) rM1:M2] ##1 (8)[*N1:N2]

Condition B occurs exactly N times


This behavior states that condition B occurs exactly N times but not necessarily in consecu-
tive evaluation cycles. This sequence makes use of the goto repeat operator.
Sequence:
(8)[->N) ##1 !(8)[0:$]

Condition B occurs exactly N times within the next M cycles


This behavior states that condition B occurs exactly N times in the next M cycles but not nec-
essarily in consecutive evaluation cycles.
Sequence:
(1 )[*M]) intersect «(B)[->Nl ##1 !(B)[O:$1)

15.7.1.2 Sequences Based on Two Conditions


The following behaviors depend on two conditions. Conditions B1 and B2 in the following
behaviors can be any valid sequence Boolean expression (section 15.2).
Conditions B1 and B2 occur only in the same cycle
This behavior states that conditions B1 and B2 always evaluate to the same value: This condi-
tion is implemented using an equivalence operator.
Sequence:
(81) == (82)
At least one of conditions B1 or B2 occurs in the next M cycles
This behavior states that in the next M cycles, one of conditions B1 and 8 2 evaluate to true.
The use of a Boolean OR operator to combine conditions 8 1 and 8 2 indicates that the imple-
mentation shown here can easily be extended to any number of conditions.
Sequence:
((81) II (B2»)[*Ml
Sequence and Property Dictionary 407

Condition B1 occurs before condition B2 occurs


This behavior states that conditions B1 and B2 both occur but condition B2 does not occur
before condition B1 . The implementation shown here provides an open-ended range where
conditions B1 and B2 can occur within any number of evaluation cycles.
Sequence:
!(82) [*0:$] ##0 (81) ##[1 :$] (82)

Condition B1 occurs within the next N1 cycles, and within N2 cycles before B2 occurs
This behavior states that conditions B1 and B2 both occur but condition B2 does not occur
before condition B1 • The implementation shown here provides a closed range where condi-
tions B1 must occur within N1 cycles and condition B2 must occur within N2 cycles after con-
dition B1 occurs.
Sequence:
!(82) [*0:N1] ##0 (81) ##[1 :N2] (82)

Condition B2 does not occur before condition B1 occurs


This behavior states only that condition B2 does not occur before condition B1 occurs. It does
not require that condition B2 occurs after condition B1 has occurred.
Sequence:
!(82) [*0:$] ##1 (81)

Condition B1 occurs before and holds until condition B2 occurs


This behavior states that both conditions 8 1 and 8 2 occur, and that B1 occurs before condition
8 2 , and that once 8 1 occurs, it holds until condition 8 2 occurs.
Sequence:
(81) [*1 :$] ##0 (82)

Condition B1 occurs before and holds until N cycles after condition B2 occurs
This behavior states that both conditions 8 1 and 8 2 occur, and that B1 occurs before condition
B 2, and that once 8 1 occurs, it holds until N cycles after condition B2 occurs.

Sequence:
(81) [*1 :$] ##0 (82) ##1 (B1) [*N]

Conditions B1 and B2 occur exactly N times each within the next M cycles
This behavior states that conditions B1 and 8 2 each occur N times within the next M cycles. It
does not, however, require that the occurrence of each condition be in any specific order or
that the occurrences of each condition be consecutive.
Sequence:
«1 )[*M]) intersect «(81 )[->N] ##1 !(81 )[0:$]) and «B2)[->N] ##1 !(82)[0:$]))

15.7.1.3 Sequences Based on Multi-Cycle Behaviors


The following behaviors depend on a condition and a multi-cycle behavior. Condition 8 in
the following behaviors can be any valid sequence Boolean expression (section 15.2).
Behavior s can be any valid sequence.
408 Property Specification and Evaluation Engine

Behavior 51 starts and ends while behavior 82 is taking place


This behavior states that sub-behaviors 81 and 82 both occur, but the start time of81 is at the
same time or after the start time of 52, and that the end time of 51 is before or at the same time
as the end time for 52'
Sequence:
(S1) within (S2)

Condition 8 occurs exactly N times while behavior 5 is taking place


This behavior states that from the start of evaluation to the end of evaluation of sub-behavior
S, condition 8 occurs exactly N times and not more than N times.
Sequence:
((8)[->NJ ##1 !(8)[0:$1) within (S)

Condition 8 holds during the last N cycles of behavior S taking place


This behavior states that condition B occurs in N consecutive evaluation cycles with the last
cycle being the same as the end cycle for evaluation of sub-behavior s.
Sequence:
((1) ##[0:$1 (8)[*NJ) intersect (S)

Condition 8 holds during the first N cycles of behavior S taking place


This behavior states that condition 8 occurs in N consecutive evaluation cycles with the first
cycle being the same as the first cycle for evaluation of sub-behavior s.
Sequence:
((8)[*N] ##1 (1) ##[0:$]) intersect (S)

Condition 8 holds while behavior S is taking place


This behavior states that condition B holds from the first cycle the evaluation of sub-behavior
S is started until the cycle in which the evaluation of sub-behavior Sends.
Sequence:
(8) throughout (S)

15.7.2 Property Dictionary


The challenging part of writing a property is in turning the description of relevant behaviors
into sequences. Once each intended behavior is described with a sequence, writing properties
is as simple as deciding the qualifying behavior (to be used as the antecedent clause), the
qualified behavior (to be used as the consequent clause), and the relative timing between
these behaviors (to determine the implication operator type).
As an example, consider the property "If condition 8 1 occurs before and holds until con-
dition 8 2, then conditions 8 3 and 8 4 occur exactly N times each within the following M
cycles." The implementation of this property is given by:
(81) [*1:$) ##0 (82) 1->
((1)[*MJ) intersect (((83)[->N) ##1 1(83)[0:$)) and ((84)[->N) ##1 !(84)[0:$)))
Sequence and Property Dictionary 409

This seemingly complex property is easily implemented by using the appropriate imple-
mentations for the qualifying and qualified behaviors, as shown in section 15.7.1, for ante-
cedent and consequent clauses of the property implication operator.
Examples of common design properties and their corresponding implementation using
the SystemVerilog property construct are shown in the following subsections. Given these
implementations, many design properties can be expressed by the combination of sequences
shown in section 15.7.1 and property implementation shown in this section.

15.7.2.1 Properties using a Condition as a Qualifying Condition


Condition 8 must always hold
property prop;
@(posedge elk) disable iff (reset) B;
endproperty

If condition 8 1 occurs, then condition 8 2 must occur in the next cycle


property prop;
@(posedge elk) disable iff (reset) B1 1=> B2;
endproperty

If condition 8 1 occurs, then condition 8 2 must occur in the same cycle


property prop;
@(posedge elk) disable iff (reset) B1 1-> B2;
endproperty

If condition 8 has occurred, then evaluation of behavior 5 started in the next cycle after
B occurred must succeed
property prop 1;
@(posedge elk) disable iff (reset) B 1=> S;
endproperty

If condition B has occurred, then evaluation of behavior 5 started in the same cycle that
8 occurred must succeed
property prop1;
@(posedge elk) disable iff (reset) B 1-> S;
endproperty

15.7.2.2 Properties using a Multi-Cycle Behavior as a Qualifier


If behavior 5 succeeds, then condition B must occur in the next cycle after 5 succeeded
property prop;
@(posedge elk) disable iff (reset) S 1=> B;
endproperty

If behavior 5 succeeds, then condition B must occur in the same cycle that 5 succeeded
property prop1 ;
@(posedge elk) disable iff (reset) S 1-> B;
endproperty

If behavior 51 succeeds, then evaluation of beha\'ior 52 started in the next cycle after 51
410 Property Specification and Evaluation Engine

succeeded must also succeed


property prop 1;
@(posedge clk) disable iff (reset) 81 1=> 82;
endproperty

If behavior 51 succeeds, then evaluation of behavior 52 started in the same cycle that 51
succeeded must also succeed
property prop1;
@(posedge clk) disable iff (reset) 81 1-> 82;
end property

15.7.2.3 Properties Used for Debugging Sequences


These properties are used to help in debugging named sequences.
Print evaluation end time of all matches for sequence 5 whose evaluation started in
time range [T1:T21
This property is used to print the evaluation end time of all matches for sequence 5 whose
evaluation started between time T1 and T2 for a named sequence S.
property aILmatches_starting_between(seq, t1, 12);
time start time;
($time <=t2 && $time>= t1, start_time = $time) 1-> seq 1-> (1, $display(start_time, $time));
endproperty

This property can be used as follows in an assertion statement for a previously defined
named sequence 5:
debug_assert: assert property (
@(posedge elk) all_matches_starting_between(ss, 2, 40)
);

The clocking event specified for the assert statement should be the same as the clocking
event for the first cycle of sequence ss.
Print evaluation start time of all matches for sequence 5 that are matched in time range
[T1:T21

This property is used to print the evaluation start time of all matches produced between time
T1 and T2 for a named sequence 5.
property all_matches_produced_between(seq, t1, t2);
time start time;
($time <=t2, start_time = $time) 1-> seq
1-> ($time>=t1 && $time <=t2) 1-> (1, $display(start_time, $time));
endproperty

This property is can be used as follows in an assertion statement for a previously


defined named sequence ss:
debug_assert: assert property (
@(posedge clk) all_matches_produced_between(ss, 10, 16)
);

The clocking event specified for the assert statement should be the same as the clocking
event for the first cycle of sequence ss.
CHAPTER 16 Assertion-Based
Verification (ABV)

An important observation about functional verification is that design properties that must be
verified remain the same as the design implementation progresses through different levels of
abstraction, regardless of what verification tools and methodologies are used to carry out the
verification tasks. Regardless of design flow (top-down or bottom-up), properties that must
be verified accumulate. For example, in a top-down design style starting from the architec-
tural specification, a property that must be held at the architectural level, must still be main-
tained when the detailed block level design is created. And in a bottom-up design style where
individual blocks are created first, a micro-code design implementation property that had to
be maintained during block level design must still hold when blocks are combined to create
the complete system.
These observations lead to the straightforward conclusion that the use of a robust and
practical mechanism for specifying, modeling, and collecting such properties throughout the
design process leads to immediate gains in verification completeness (i.e, all scenarios are
verified), correctness (i.e., each scenario is verified correctly), and productivity. Complete-
ness is improved because of the accumulating nature of these properties throughout the
design and verification process. Correctness is improved because these properties will be
verified using multiple verification technologies during the full verification cycle (e.g., for-
mal verification, simulation, acceleration and emulation) and at different levels of design
abstraction. Productivity improves both directly, as result of using well-defined procedures
for specifying and verifying properties, and indirectly as a result of improvements in verifi-
cation completeness and correctness. Assertion-based verification CAB V) methodology
describes the tools, techniques, and best-in-class practices that allow the benefits of this
approach to be realized.
The enabling technology for assertion-based verification is a powerful mechanism for
specifying design properties. SystemVerilog provides the sequence and property constructs
to describe design properties in a concise and expressive manner. These constructs were
described in chapter 15. This chapter describes the approach used for identifying and orga-
nizing assertions, and deploying assertion-based verification using System Veri log.
Assertion Definition Flow 413

A verification engineer's focus is more on features extracted from the design specifica-
tion and less on how these features are implemented. Not all design properties are suitable
for assertion-based verification. Data oriented scenarios are best verified using scoreboard-
ing techniques whereas control oriented properties are better candidates for assertion-based
verification. A verification engineer should decide on the design properties that are best ver-
ified using assertions. Block level verification engineers can identify assumptions and prop-
erties relevant to the local blocks they work on, and system level verification engineers can
focus on end-to-end scenarios and system-wide properties. In addition to local and
end-to-end verification scenarios that can be verified through assertions, verification engi-
neers must also define assertions about the following types of information (section 16.1.2.3
and 16.1.2.4):
• Assumptions made about the verification environment
• Properties defining correct DUV configuration
• Properties defining valid combinations of configuration and control registers, status
registers, and DUV pin values
• Properties for standard DUV interfaces

16.1.2 Assertions and Project Phases


Different types of properties should be considered during each phase of the design flow.
These phases are:
• Architectural design
• Block design and verification
• Cluster/chip integration and verification
• System integration and verification
Considerations relevant to each phase are discussed in the following sections.

16.1.2.1 Architectural Design


There is usually very little detailed information available during architectural design. As
such, properties derived from design specification at this stage are fairly abstract and consist
mostly of end-to-end design behaviors. The execution model of the design at this stage, if
available, consists of a transaction level model described at a high granularity level built in
SystemC. Assertions at this level of abstraction are, therefore, described at the transaction
level.

16.1.2.2 Block Design and Verification


Fonnal verification is used to verify assertions specified during the block design phase. The
main reason for this usage is that there is usually no simulation environment available when
blocks are being designed and in fact multiple blocks must be attached together before a sim-
ulation environment can be built.
Given the limitations inherent in formal verification, it may not be possible to evaluate
all assertions specified during block level design. These assertions must be marked and con-
414 Assertion-Based Verification (ABV)

sidered for evaluation when the block under consideration is being verified as part of a larger
module or chip using a simulation environment.
The natural order of execution for block related verification is as follows:
• Bring-up checks
• Reset stuck
• Clock not active
• Interface verification
• Operational mode verification
• Control registers
• Mode pins
• Internal functionality verification
• Operational modes are considered assumptions during formal verification.
• Assertions are specified for each operational mode.
• End-to-end functionality verification
Bring-up checks cover design conditions that must be satisfied before the block can
operate correctly. Examples include reset lines and clock connections.
Interface verification is considered next. The block can operate only if it can communi-
cate with its enclosing environment. Depending on the interface complexity, it may not be
possible to specify all interface properties as assertions. And not all assertions specified at
block level may be verifiable by the formal verification tool. All such interface properties
must be marked for consideration in the simulation environment.
A block can usually operate in different modes through control register settings or mode
pins. The formal verification tool's ability to verify assertions improves with an increasing
number of assumptions and decreasing assertion complexity. Using block operation modes
as assumptions to the fonnal verification tool allows more assertions to be covered by the
formal verification tool. In this approach, assertions for block properties are specified sepa-
rately for each operating mode.
Internal functionality is considered next. Internal functionality is specified for each set
of operating and interface modes. End-to-end behavior of a block may not be a good candi-
date for assertion-based verification because such properties are usually data-path oriented
behaviors. As such, such properties are best verified in the simulation environment. How-
ever, end-to-end properties that can be specified using assertions should be considered in this
stage of verification.

16.1.2.3 Cluster/Chip Integration and Verification


In this phase, individual blocks designed in the previous phase are grouped together to form
cl usters and chip level designs. The focus of verification at this stage is mostly on verifying
correct communication between blocks and that the operating environment of each block is
as expected. Assertions added at this stage should check the following properties:
• En\'ironmental assumptions for each block are satisfied
• Each block operating mode and configuration is valid
• Interactions between blocks follow assumptions made at the block level
• Properties that could not be checked during block level design
Assertions vs. Assumptions 415

• Properties about the overall function of the cluster


Assertions specified for these properties are either inherited from the block level design
or are added because of functionality provided by the increased level of integration. A simu-
lation-based environment is used at this level to collect coverage and to make sure no asser-
tions fail.

16.1.2.4 System Integration and Verification


The increasing complexity of a design at the system level requires the use of hardware accel-
eration and emulation systems. Assertions added at this level are similar in nature to the ones
added during chip and cluster creation, where in this case, clusters are connected to form a
complete system. An additional consideration in this phase is the ability to evaluate asser-
tions using the acceleration and emulation systems. Generally, acceleration and emulation
systems can handle the same assertions as formal verification and simulation tools, and tool
vendors provide the ability to compile assertions to be evaluated on such systems.

16.2 Assertions vs. Assumptions


Both assumptions and assertions define properties that must be satisfied by signal values of a
design block. Assumptions are, however, statements about the operating environment of a
block, while assertions are statements about how a block should operate under the given
assumptions.
Assumptions are an important consideration when using a formal verification tool. To
better understand this need for assumptions, consider the example shown in figure 16.1. This
example shows a block with three inputs A, B, and c, and two outputs D and E. It also gives
property P1 for the input signals, and property P 2 for the output signals. A formal verification
tool operates by first computing the equation describing the relationship between the output
signals and then proving that property P2 is always satisfied by the equation describing this
relationship between the output signals (see example in section 1.4.1.2). Property P1 defines
a relationship that must be maintained between the input signals and as such, can be used to
identify input combinations that will never occur (i.e., input don't care conditions). If prop-
erty P 1 is not considered during this proof construction, then property P 2 must hold for all
possible combinations of input variables (i.e., area Dom in figure 16.1). However, in reality,
property P 2 need to be satisfied only for valid combination of the input signals as described
by property P1 (i.e., area DomA in figure 16.1). This means that not including property P1 as
an assumption during formal verification of property P 2 can lead to false assertion failures
for combinations of the input signals that never occur.
Assumptions can be over-specified or under-specified. An over-specified set of assump-
tions (area Domo in figure 16.2) does not include all input space combinations for which
design properties must hold. This means that an over-specified set of assumptions may lead
the formal verification engine to miss and not report a failed property. An under-specified set
of assumptions (area Dom u in figure 16.2) includes input space combinations that never
occur. This means that an under-specified set of assumptions may lead to the formal veri fica-
416 Assertlon·Based Verification (ABV)

~:I~BIOCk
A
B
C

Input property (assumption) P1: (AB==1) rOc;:l Domain of variables A,B,C


~ (all possible combinations of A,B,C)
Output Property (assertion) P2 : (0 1= E) Range of function (A,B==l)
(all possible combinations of A,BtC where A,B=1)

Figure 16. I Assumptions and Formal Verification of Assertions


tion engine falsely reporting assertion failures for input space combinations that never occur.
Not including assumptions during formal verification is an extreme case of having an
under-specified set of assumptions. As an example, using property (A.B.C==1) instead of
property (A.B==1) as an assumption for the example in figure 16.l leads to having an
over-specified assumption where the formal verification engine may not report a failed asser-
tion when (A.B.C=1). Alternatively, using property (A==1) as an assumption for the example in
figure 16.1 leads to having an under-specified assumption where the formal verification
engine may falsely report the failure of an assertion for the impossible case when (A.B=1).
During formal verification, it is, therefore, important that assumptions made about any prop-
erty are carefully examined before being added to the verification environment.

~ Domain of input variables


~ (all possible combinations of inputs)

1"~~~~1 Range for under-specified assumptions

• Range for valid assumptions

• Range for over-specified assumptions

Figure 16.2 Over-Specified vs. Under-Specified Assumptions

Assumptions and assertions take alternating roles when verifying each one of a set of
interacting blocks. Figure 16.3 shows two interacting blocks Bl and 8 2, When verifying
block B1, properties defined for the output port Of8 1 are verified while considering properties
defined on its input port as assumptions. The situation is reversed when verifying block 8 2
where properties on block 8 2 's output are now verified with properties on its input consid-
ered as assumptions.
Differentiating between assumptions and assertions is not necessary when using a simu-
lation tool. The reason is that a simulation tool verifies an assertion by computing the values
of all signals used in that assertion, and these computed values already reflect any properties
or assumptions that must be satisfied anywhere else in the design.
SystemVeriiog Assertions 417

DUV

o Assumptions (constraints) II Assertions (Properties)

Figure 16.3 Interchanging Roles of Assumptions and Assertions

16.3 System Verilog Assertions


-------------------------------------
In SystemVerilog, assertions can be activated using different mechanisms. In addition, Sys-
temVerilog provides system tasks that allow the programmer to assign different severity (i.e.,
warning, error, info) to assertions and to also control the behavior and logging of assertions.
These topics are discussed in the following subsections.

16.3.1 Assertion Activation


SystemVerilog provides three methods for activating and evaluating assertions:
• Immediate assertions
• Concurrent assertions
• Procedural assertions
Immediate assertions are used for non-temporal properties (i.e., only Boolean expres-
sions are allowed) within procedural code. Concurrent assertions are instantiated in a mod-
ule, program, or interface (in parallel with procedural blocks) where a new evaluation of the
asserted property is started at every new sampling event for that assertion. Procedural asser-
tions follow the same semantics as concurrent assertions but can be embedded in procedural
code under special considerations. These assertions types are described in the following sub-
sections.

16.3.1.1 Immediate Assertions


Immediate assertions are used to check for non-temporal behaviors. This means that only
Boolean expressions can be used to define a property checked by immediate assertions.
Immediate assertions can be placed anywhere procedural code is allowed, using the follow-
ing syntax:
[ label] assert boolean_expression [action_block]:
418 Assertion-Based Verification (ABV)

An immediate assertion is evaluated immediately when it is reached in the procedural


code. If action_block is not defined, then $error system task is called when
boolean_expression evaluates to false and no action is taken when boolean_expression evalu-
ates to true. The default behavior for pass and fail of this assertion can be changed by defin-
ing the action_block.
An example of an immediate assertion is shown in the following code fragment.

IProgram 16.1: Immediate Assertion Statement


!

1 always @(negedge clk) begin


2 assert(m_task ==="read_memory 11m_task ===
'write_memory)
3 If(done) $dlsplay("%t Finished command %b",$time,lasUask);
4 else
5 $error("Unsupported memory task command %b",m_task);
6 end

Immediate assertions are also useful for checking issues that may arise during program
execution. The following example shows how an immediate assertion is used to check that
the randomizeO function produces valid results:

Iprogram
, 16.2: Checking program behavior using immediate assertions

1 always @(negedge clk) begin


2 rand_value_randomize_ok: assert (randomize(rand_value})
3 else $fatal(1, "Randomization failed for variable rand_value"};
4 end

16.3.1.2 Concurrent Assertions


Concurrent assertions allow for continuous checking of a property while simulation is in
progress. A new evaluation of a concurrent assertion is started for every new sampling event
of its property definition, leading to multiple concurrent evaluations of a multi-cycle prop-
erty. As such, a concurrent assertion is used to continuously monitor a given design property.
Concurrent assertions can be specified for Boolean expressions as well as properties span-
ning multiple cycles. Concurrent assertions are placed inside modules, program blocks, or
interfaces in parallel with procedural blocks (e.g., always block).
ConCUlTent assertions have the following general syntax:
[ label] directiVe property (property-expression) [ action_block ]:

SystemVerilog provides the following directives for concurrent assertions:


• assel't
• assume
• co)'er
The assel't directive is used to specify a property as a checker to make sure that the
property holds for the design. Both formal veritication and simulation tools check that asser-
SystemVeriiog Assertions
-
419

tions defined with the assert directive are checked and any failed evaluations are reported.
The syntax is:
[ label I assert property (property-expression) [ action_block I;

The assume directive is used to specify an assumption about the operating environment
of the design. Formal verification tools do not check properties defined using the assume
directive. Rather, such properties are used for verifying the properties that must be solved by
the formal verification engine. Simulation tools, however, treat assertions defined using the
assume directive the same as those defined with the assert directive. Note that no
action_block is allowed for this directive. Syntax for this directive is:
[ label] assume property (property_expression);

The cover directive is used to include the results of assertion evaluation in coverage
results. A pass statement can be specified for this directive, which will be evaluated when the
property succeeds.
[label] cover property (property-expression) [pass_statement];

The following code fragment shows examples of these concurrent assertion types:

iprogram 16.3: Concurrent assertion directives


I

1 property assert_prop;
2 @(posedge (clk» (a ##1 b);
3 end property
4 assert property (assert_prop) else -> error_detected;
5
6 property assume_prop;
7 @(posedge (d» (a ##1 b);
8 end property
9 p3Jabel: assume property (assume_prop);
10
11 property cover_prop;
12 @(posedge clk) a 1=> b ##2 c;
13 end property
14 cover property (cover_prop) $display("Covering C1");

L _ _ _ __

16.3.1.2.1 Procedural Assertions


Procedural assertions are similar to concurrent assertions but are placed inside procedural
code and are evaluated when program control flow reaches the assertion.
Procedural assertions require that:
• If property has no explicit clocking event, then one is inferred from the procedural
block. This inference requires that the first term in the event control of the procedural
block be defined using aposedge or negedge specifier, and that the variables used in
the expression for this specifier are not used in the body of the procedural block.
• If property has an explicit clocking event defined, then this clocking event must be
the same as the inferred clocking event for the procedural block.
• No time consuming statements (Le., tasks with delays, delays) can be present
between the beginning of the block until assertion is reached.
420 Assertion·Based Verification (ABV)

• The procedural assertion cannot be pl;:tced inside a looping statement.


Evaluation semantics of procedural assertions are the same as concurrent assertions
with the exception that the evaluation of the property is conditioned upon program execution
reaching the assertion statement. Consider the following code segment:

iprogram 16.4: Procedural assertions


I

1 property prop1;
2 q1 1= d1;
3 endproperty
4 always @(posedge clk) begin
5 q1 <=d1;
6 prop1_proc_asrt: assert property (prop1);
7 end
8
9 property prop2;
10 @(posedge elk)(q2 != d2);
11 endproperty
12 always @(posedgeclk) begin
13 if (a) begin
14 q2 <= d2;
15 prop2_proc_asrt: assert property (prop2);
16 '. end
17 end

In this example, assertions defined on lines 6 and 15 are procedural assertions. The
assertion on line 6 infers its clocking event from the always block, and the assertion on line
15 has its own clocking event defined, which is the same as the inferred clock for the always
block but is placed inside a conditional block. The following code fragment shows how these
same assertions can be specified using concurrent assertions:

I Program 16.5: Rewriting procedural assertions as concurrent assertions


I

1 property prop1 c;
2 @(posedge clk) q1 != d1;
3 end property
4 prop_concur_asrt: assert property (prop1 c);
5
6 always @(posedge elk) begin
7 q1 <= d1;
8 end
9
10
11 property prop2c;
12 @(posedge elk) a 1-> (q2 != d2);
13 end property
14 prop2_concur_asrt: assert property (prop2c);
15
16 always @(posedge elk) begin
17 if (a) begin
18 q2<=d2;
19 end
20 end
SystemVerilog Assertions 421

The definition for property prop1 is modified to include an explicit clocking event
before it can be used in a concurrent assertion. The definition for prop2 is modified so that
the condition for reaching procedural assertion is used as the antecedent of an implication
property whose consequent is the original property definition.

16.3.2 Severity System Tasks


SystemVeri log provides the following assertion severity levels:
• fatal
• error
• warning
• info
A system task is provided for each severity level, which when called produces the
desired behavior on simulation flow. These system tasks have the following syntax:
$fatal ( level, message! , args ]);
$error (message[ , args ])
$waming (message[ , args])
$info (message[ , args ])

$fatalO is used to immediately terminate simulation. The first argument passed to


$fatalO is the same argument passed to $finislt() system task. The effect of $errorO task
depends on settings for the simulation tools and is customizable. $warning() and $in/oO sys-
tem tasks produce logging message of the corresponding severity.
Severity tasks can be used anywhere procedural statements are allowed. $error system
task is called by the simulator by default when an assertion fails. The action block for an
assertion can be used to modify this behavior or call a system task with a different level of
severity.

16.3.3 Assertion Control System Tasks


SystemVerilog provides the following system tasks for controlling assertion checking
through the System Veri log program:
• $assertonO
• $assertoJJ()
• $assertkillO
Arguments to these tasks are interpreted in the same way as for $dumpvarsO system
task. The general syntax is:
$assert_conlroUask [(Ieve/l, IisLolmodu/es_or_assertions])];

Each assertion control task can be called either with no arguments, with the level argu-
ment. or with level and Iist_oCmodules_or_assertions. If no arguments are specified, then the
task affects all assertions in the hierarchy. level specifies the number of hierarchy levels
below each specified module instance that are affected. The number of levels is counted from
the top of module instance and not from where the task is located. level cannot be set to zero
422 Assertion-Based Verification (ABV)

when any assertions are specified as arguments. If level is set to 0, then all assertions in the
specified module instances will be affected.
IIsCoCmodules_or_assertions provides the scope to which the command applies. The
arguments in this list can be either modules or assertions within modules. Arguments in this
list can be instance names, hierarchical instance names, or hierarchical references to proper-
ties. These arguments cannot be a reference to a sequence or out of module reference to indi-
vidual assertions.
Task $assertoffO suspends checking of all specified assertions until $assertonO is
encountered. Assertions that are already executing, including assertion action blocks, will
continue executing. Task $asserton, resumes checking all specified assertions that were dis-
abled by a previous call to $asserto.ff(). Task $assertkillO halts checking of all specified
assertions that are currently executing, then suspends checking of all specified assertions
until $assertonO is encountered.

/6.4 Capturin8 Assertion Requirements


Assertions are added at different levels of design abstraction and during different stages of
the design and verification flow. As such, it is necessary to follow a consistent view for ana-
lyzing a DUV and its verification environment, and expressing verification targets as asser-
tions. Section 16.1 describes the types of assertions that must be contributed by each project
participant and during each phase of the design process. This section describes a methodol-
ogy that provides a consistent view of assertion extraction from a project planning perspec-
tive.
Use the following steps when capturing assertions for a design:
• Consider verification objectives.
• Partition the verification problem.
• Identify requirements for each partition.
• Map requirements to assertions.
• DeHne clocking and reset/interrupt conditions.
• Add assertions to the design.
These steps are described in the following subsection.

16.4.1 Step 1: Consider Verification Objectives


Complete functional verification of a DUV requires that the following behaviors be consid-
ered in verification objectives:
• DUV configuration
• DUV interaction with its environment (interface behavior)
• Core functionality of the DUV

-------------------------------------
Capturing Assertion Requirements 423

Not all of these behaviors may be necessary for all verification projects. As such, it is
necessary to define the scope of a verification project before considering what assertions
must be specified.

16.4.2 Step 2: Partition the Problem


Complete DUV verification requires that:
• DUV function is verified
• DUV usage in its target environment is verified
Verifying that a DUV functions as expected requires that:
• DUV interface requirements are specified
• How the design communicates with its environment (i.e., protocols)
• DUV configuration is valid
• Valid DUV pin values
• DUV core functionality is fully verified
• Individual subcomponents work correctly
• Overall DUV works correctly in all modes
Verifying that a DUV is used correctly in its target environment requires that:
• DUV is tied correctly to its environment
• DUV is correctly integrated into the global clocking/resetltestlpower lines
• Communication protocols are implemented correctly
• DUV static configuration and dynamic re-configuration is valid
• Valid combinations of configuration parameter values
• Valid transitions between configurations or operating modes
• Environment correctly drives the DUV
• Valid control inputs in any state
• Data inputs within valid ranges
• Inputs stable when required
These verification requirements are described in the following subsections in the con-
text of DUV interface, core function, and outputs.

16.4.2.1 Interface
DUV interface refers not only to its boundary pins but also to any interaction between the
DUV and its outside environment. Correct operation of this interface is the initial, and a
required, step in verifying a DUV, since without an operational interface, the DUV cannot be
verified. Interface requirements are also used as assumptions in fonnal verification tools.
DUV interface consists of the fo Hawing:
• Configuration
• DUV input pins
• Protocol
• Register interface
424 Assertion-Based Verification (ABV)

Configuration refers to how a block is configured (e.g., through configuration parame-


ters) to playa specific role in its target environment. DUV input pins refer to behavior at the
DUV boundary pins. Protocol refers to complex multi-cycle interaction between the DUV
and its containing environment. Register interface refers to how the outside environment
manipulates the register space of the DUV. These aspects of interface verification are dis-
cussed in the following subsections.

16.4.2.1.1 Configuration
Design and verification IPs are commonly used to improve productivity through reuse. The
widespread use of such IPs and IP developers' desire to make their product as commonly
useful as possible means that each IP is usually designed to be adaptable to multiple use
models. Different approaches for design configuration during different stages of the design
process include:
• Source code generation: creating different files for different configurations
• Source code compilation: using conditional compilation (e.g., using ifdefin Verilog)
• Design elaboration: using generating parameters and "generate" statements
• Design operation: assigning values to configuration inputs and registers
The following assertions must be specified for checking design configuration:
• Combined setting of all configuration parameters represents a valid configuration
mode
• If allowed, dynamic transitions from one configuration to another take place cor-
rectly
• DUV is configured as planned in its operating environment
The first two types of assertions are added by block designers. The third type is added
when that block is integrated into a larger block.

16.4.2.1.2 DUV Input Pins


DUV inputs refer to pins connecting the DUV to other blocks. Assertions about inputs must
be added by the designers of a block so that when that block is integrated in a larger module,
valid values for its pins are checked without requiring extra work by other designers.
Assertions about the following DUV input properties should be specified:
• Control inputs are presented at the appropriate times and in the right combination
and/or sequence
• Data input values are in the valid set or within the valid range of allowed values
• Input signals are stable when required
Assertions added for DUV input pins are used as assumptions for fonnal verification
tools. Assumptions about input pins of a block turn into assertions about intemal functional-
ity oflarger design blocks containing that block.
Design input pin assertions are added by block designers.
Capturing Assertion Requirements 425

16.4.2.1.3 Protocol
Any design of reasonable complexity uses a predefined protocol to communicate with its
environment. A protocol may be a bus type interface where the design can act either as bus
master, a bus slave, or both, or a point-to-point connection where communication takes place
using a predefined syntax over multiple clock cycles (e.g., UART, USB).
The signal activity at a block interface depends on the detailed description of its proto-
col and the activity originating from inside the block and from outside the environment.
When defining assertions for a block interface protocol, decisions made by the outside envi-
ronment are not known. This means that interface behaviors that depend on the outside activ-
ity cannot be verified by using assertions when the operating environment of that block is not
available. For example, in a UART interface, it is straightforward to verify that all stop-bits
appearing on the interface are valid, since the size of UART data word is available as a set-
ting in the internal configuration of the block that uses the UART interface. Some UART
behaviors, however, cannot be verified by simply looking at the interface signals. For exam-
ple, if the environment driving the UART input to a block asserts the break condition (forc-
ing the data value to a constant 0), it is not possible to decide by just looking at the interface
signals and block internal setting whether or not the constant zero value on the input line is
due to an environment error or due to a UART break condition.
Figure 16.4 shows a pictorial view of this distinction between protocol internal and
external properties. Assertions defined for interface protocols of a block should include at
least all interface properties that can be checked by looking at the interface signals and inter-
nal state of the design (Le., protocol internal properties). These assertions must be added by
the block designer. Designers can potentially identify external conditions that are needed to
decide all protocol properties and ask for such conditions to be elaborated when the design is
integrated into the larger design. Alternatively, protocol checkers can assume a worst case
scenario policy where any unexpected observation is assumed to be an error unless overrid-
den when the design is integrated in the target design.

Depend on pins and internal design state


Include as assertions when building design

Depend on pins. internal design, and environment state


Cannot include as assertions when building design

Figure 16.4 Protocol Internal and External Properties


426 Assertion-Based Verification (ABV)

16.4.2.1.4 Register Interface


A register bank is a common component of any design. It allows design interaction to take
place through memory-mapped transactions, thereby minimizing the number of interface
pins required for such configurations. Registers can be divided into two broad categories:
• Control registers: written to by the outside environment to control DUV behavior
• Status registers: updated by DUV and read by the outside environment
Any register that can be updated both by the DUV and from the outside environment
should be included in both categories. Assertions about control registers should be included
in the interface verification ofthe design. Properties that should be included for control reg-
isters include:
• Values assigned by the outside environment are valid
• Register values change only as allowed by specification or don't change at all after
initial setting if required by the specification
• Combined setting of input registers represents a valid state
Assertions about status registers should be included when verifying design outputs (sec-
tion 16.4.2.3).

16.4.2.2 Core Function


Assertions about the core functionality of a design should include the following:
• Implementation specific assumptions
• Core properties that are required for most fundamental operations to work
It is not possible to define all properties of a DUV as assertions. As such, the focus in
defining design core assertions should be: 1) to check properties that overall indicate the
design works correctly, and 2) properties that if failed, would be difficult to debug either
because they include micro-code assumptions little known by anyone but the designer or
because it would take them many cycles to become visible at the system boundary.
In general, assertions about the following core function properties should be included:
• FSM properties including state transitions and sequences
• Data transfer control logic including muxes, addressing, decoding, etc.
• Data transformation logic including ALUs, multipliers, and other complex blocks
• Parallel operation of mUltiple elements (e.g., pipeline behavior, interacting state
machines)
• Correct configuration and operation of subcomponents
• Correct setting of status registers

16.4.2.3 Design Outputs


The setting of a value on a design output can be viewed from two different perspectives.
First. it is clear that design outputs are assigned because of complex end-Io-end beha\iors.
However, specifying assertions for such end-to-end behaviors is usually complicated and
yerif~'ing this behavior is best done using scoreboarding techniques during the simulation
process. A different \'iew on setting of design output values is ho\\" these signals are assigned
Capturing Assertion Requirements 427
--------------------------------------------------------------------
as a function of the blocks driving them. Such properties are usually more tractable and do
not require end-to-end property descriptions. In addition, properties specified using such
local assignment considerations are more manageable by formal verification tools. As such,
it is recommended that design output assertions be specified using their local assignment
instead of their end-to-end behaviors.
Assertions on design outputs can be considered as design core assertions and as such
follow the same general guidelines discussed for design core assertions.

16.4.3 Step 3: Identify Requirements for Each Partition


Each partition described in the previous section consists of many properties that may be good
candidates for including as assertions. In writing assertions it is best to start from most gen-
eral properties applicable to typical design behavior and work towards more dedicated prop-
erties. A suggested flow for extracting such properties is as follows:
• Nominal functionality
• Boundary conditions
• Startup behavior
• Predictable errors
Nominal functionality refers to the typical behavior at the given partition. Examples
include: default configuration settings, typical traces on design inputs, properties that are
needed before the design core can work as expected, and design output properties.
Boundary conditions describe comer-case conditions and properties that must hold for
such comer cases to work correctly. Examples include: combinations of configuration and
inputs settings, and their effect on design behavior; and error conditions that must be
detected by the design, either by reacting to such error conditions or setting status registers.
Startup behavior describes how the design is expected to be reset, initialized, and
brought into a new mode of operation. Examples include: how the reset sequence should be
executed, what effect the reset sequence will have on the core functionality, and how the out-
put values change as a result of the reset process.
Predictable errors describe conditions that must never occur. These errors fall into two
categories: 1) errors generated by the outside environment, and 2) errors generated because
of design problems. Error conditions at the design input that the design is expected to detect
and react to are considered part of the design functionality and are included in the design
boundary condition properties. Error conditions that must be included here include errors on
the design input that cannot be detected by the design and would, therefore, either go unno-
ticed by the design or cause a fatal problem. It is a good practice to add new assertions as
bugs are detected so that same bug does not reappear in the design.

16.4.4 Step 4: Map Requirements to Assertion Forms


Assertions are statements about design beha\"iors that should be satistied either uncondition-
ally or \\"hen some qualifying beha\"ior is satisfied. As such. mapping requirements to asser-
tions requires the following steps:
428 Assertion-Based Verification (ABV)
--_.. _ . _ - - - - - - - - - - - - - - - - - - - - - - - - -
• Identify the target behavior that must be satisfied
• Identify the qualifying behavior, if one is needed
• Decide the timing relationship between the qualifying and the target behavior
Section 15.7.1 provides a range of behaviors and their equivalent representation using
the System Verilog sequence construct. Section 15.7.2 provides examples of how qualifying
and target behaviors can be combined to form a property. Section 16.3.1 then describes asser-
tion directives (e.g., assert, assume) that can be used to turn a property into an assertion
statement.

16.4.5 Step 5: Define Clocking and Reset/Interrupt Conditions


D'UV properties should be evaluated using the appropriate clocking event. For local proper-
ties, selection of such clocks is straightforward and obvious. As the property under consider-
ation spans more of the design and turns into an end-to-end property, then clock selection
may not be obvious and special steps must be taken in order to define a clock or combination
of clocks for which that property must hold. SystemVerilog allows properties to be defined
based on different clocking events (section 15.6). However, special care must be taken when
defining such properties.
Additionally, not all properties should be maintained at all times. Obvious examples are
properties that fail during reset condition. Other examples include cases where a specific
property must hold only under a specific operating mode. SystemVerilog provides the "dis-
able iff' construct for disabling assertions when they need not be evaluated. This is shown in
the following example:

rProgram 16.6: Disabling an assertion using "disable iff'


1 property abe;
2 @(negedge elk) disable iff (reset) s 1;
3 end property

16.4.6 Step 6: Add the Assertions to the Design


Assertions can be added to a design using three approaches:
• Embedded in the design
• Placed in a property file (module later bound to design)
• Placed in a verification component (module including assertions and coverage)
Table 16.1 summarizes the advantages and disadvantages of each approach.
Use the following guidelines in deciding placement for assertions:
• Designers should place assertions either in their design code or in a property file.
• Verification engineers should place assertions about the design in a property file and
assertions about the design interface in the testbench.
• Assertions for common interface protocols should be placed in a ,·erification compo-
nent that can be reused across mUltiple projects.
Assertion·Based Verification IPs 429
-
Location Advantages Disadvantages
• Always travelS with the design • Must guard that supportmg HDL code
• Can be placed near the logic with which does not get synthesized or affect code
it is associated coverage results
Embedded • Assertions that document behavior are • Cannot disable without special provisions
in the same location as code • Assertions can clutter an RTL file
• One file contains all information for
verification and synthesis
• No need to touch the design • Multiple files are required
• Easy to categorize assertions for perfor- • The parser will not complain if the file is
Module bound to mance optimizations missing
design • Easy to separate auxiliary code so that it
does not affect code coverage and syn-
thesis tools
• Portable • Multiple files are required
• Easy to instantiate in design • It is more work to instantiate the asser-
Stand-alone verifica-
• Easy to control parsing by using ifdefs tions in the design
tion component
• An extra level of hierarchy adds a little
complexity to any analysis

Table 16.1: Assertion Placement: Advantages and Disadvantages

16.5 Assertion-Based Verification IPs


Assertion-based verification IPs (ABVIPs) provide a set of built-in assertions and coverage
collection mechanisms for common design properties, including design behaviors, design
structures, and protocols. ABVIPs can be divided into two categories:
• Assertion libraries
• Assertion-based protocol checkers
The following subsections describe each category of verification IPs.

16.5.1 Assertion Libraries


Assertion libraries contain components that perform checks for commonly used design prop-
erties. These properties include:
• Onehot checks
• Handshake checks
• State transition checks
• Checks for common components such as FIFOs and arbiters
Assertion libraries are convenient to use but are limited in the type of properties that
they can check. As such, it is necessary to write additional assertions for properties not cov-
ered by a given assertion library. Assertion libraries are extensible. It is, therefore, recom-
mended that new assertions defined for a design follow the same fom1at as that of other
assertions in the library so they can be reused.
430 Assertion-Based Verification (ABV)

OVL (Open Verification Library), developed by Accelera, provides a collection of 30+


verification modules. Tool vendors often provide more comprehensive assertion libraries
which are a super-set of assertions defined in OVL.
Using assertion libraries such as OVL provides the following advantages:
• The library components are Verilog modules that have built-in assertions and cover-
age collection points. They can be instantiated in the same way as any other Verilog
design module, which makes it easy for designers to adopt and get started with ABY.
• Assertions provided in the library can be used as examples for learning assertions and
extending the library.
• The library components are highly pararneterizable, so they can be reused in different
design contexts.

16.5.1.1 Assertion Library Component Architecture


An assertion library component is modeled using a module and contains the following:
• Module declaration and port list
• Parameter declarations to help select the appropriate configuration
• Auxiliary HDL code to capture the variables required for verification
• SystemVerilog assertions that check for correct behavior
• Coverage statements (i.e., covergroup statements and cover directives)
The following example shows a SystemVerilog assertion library component:

I Program 16.7: System Veri log assertion library component


'1 module check_range(clk. reset_n, data, enable);
2
3 /lList of parameters
4 =
parameter DATA_WIDTH 32; IIDefault data WIDTH is taken as 32
5 parameter MAX_VALUE = {DATA_WIDTH{1'b1}};
6 parameter MIN_VALUE 0; =
7
8 /lList of inputs signals
9 input clk;
10 input reset_n;
11 input [DATA_WIDTH-1:01 data;
12 input enable;
13
14 /I auxiliary HDL code to capture variables required for cover coliection
15 reg [DATA_WIDTH -1 :01 values_checked; IIFor covergroup implementation
16 always @ (posedge clk) begin
17 if(reset_n == 1'bO)
18 values_checked <= {DATA_WIDTH{1'bO}};
19 else if (enable)
20 values_checked <= data;
21 end
22
23 /I SVA assertions
24 ial_range: assert property ( @(posedge clk)
25 «(reset_n == 1'b1) && enable) 1->
26 «data >= MIN_VALUE) && (data <= MAX_VALUE))));
27
28 liThe SVA cover directives
29 ial_cover_range_max_limit_reached : cover property ( @(posedge clk)
30 «reset_n == 1'b1) && enable && (data == MAX_VALUE)) ):
31
Assertion-Based Verification IPs 431

32 ial_coveuange_minJimiUeached : cover property ( @(posedge clk)


33 ((reset_n == 1'b1) && enable && (data == MIN_VALUE)));
34
35 /lCovergroup for values driven in "data"
36 covergroup iaLcg_type_range @(posedge (clk & enable));
37 iaUange_cover_values_checked: coverpoint values_checked {
38 bins value_binsl] = {[MIN_VALUE:MAX_VALUE]};
39 }
40 endgroup
41
42 IICovergroup instantiation
43 ial_cg_type_range ial_c9_range_cover_values_checked = newO;
44 end module

This assertion library component checks that a given data value is within the range pro-
vided by the parameters to this component. Lines 4--6 declare the parameters. Lines 14-21
include the auxiliary code needed to compute variable values_checked that is used to collect
coverage values. Lines 24-26 check that the data value is within the expected range. Lines
28-33 collect coverage on when data value is equal to the maximum or the minimum
allowed value. Lines 35-43 define and instantiate a covergroup to collect detailed coverage
information on the value of the data input.
Assertion library components often include ifdef statements so that its behavior (e.g.,
whether or not to collect coverage) can be controlled by the user. These conditional parame-
ters are, however, not shown in this example.

16.5.1.2 Using Assertion Library Components


Assertion library components are instantiated the same as any other modules in the design.
Where to instantiate an assertion library component is discussed in section 16.4.6.
The following code segment shows an example of using the assertion library compo-
nent shown in program 16.7:

'Program 16.8: Using an assertion library component


1 module test;
2 bit clk=1;
3 logic [15:0] data=O;
4
5 initial for (int i = 0; i <= 8; i++) #1 elk = !elk ;
6 always @(posedge clk) data = data + 3;
7 check_range #(16,10,0) check_range (clk, 1'b1, data, 1'b1);
8 endmodule

Line 5 generates four clk pulses. Line 6 increments the value of variable data by 3 on
each rising edge of clk. Line 7 instantiates the checkJange assertion library component,
requiring that the value of data remain between 0 and 10, with data having 16 bits. In this
example, the assertion will fail once the value of data is set to 12.

16.5.2 Assertion-Based Protocol Checkers


Assertion-based protocol checkers use assertions to verify that a design interface follows its
protocol properties. Building a complete and fully verified assertion-based protocol checker
432 Assertion-Based Verification (ABV)

is not a trivial task, but if one is available, it provides significant benefits in carrying out the
verification task. As such, assertion-based protocol checkers are best suited for popular and
commonly used protocols such as PeI-Express, AHB, AXI, etc.
Assertion-based protocol checkers provide the following benefits:
• Complete verification of design adherence to protocol standards
• Complete, pre-verified set of assertions
• Cover checks for capturing interesting protocol scenarios
• Parameterizable data and address bus width
• Ease of use across formal, simulation, and acceleration environments

10.5.2.1 Assertion-Based Protocol Checker Architecture


The programming architecture of an assertion-based protocol checker is very similar to the
one for assertion-based library components. The main difference, however, is that module
ports in an assertion-based protocol checker correspond to the interface signals, and assertion
and coverage points specified in the module correspond to a comprehensive set of protocol
properties that the interface signals must follow.
An added complexity of building an assertion-based protocol checker is that such a
component should be useful for checking the signal properties for all types of modules that
can interact through the given protoco\. For example, an ABPC for an AMBA bus should
have assertions for both bus masters and slaves since during deployment, any of these
devices may be represented by the DUV.
Figure 16.5 shows an example of a master/slave connection. Assume first that master is
the DUV. During simulation, both the master and slave assertions provided by ABPC are
evaluated and verified. In this case, slave assertions are environment assumptions that must
be maintained and master assertions are design properties that must be verified. If slave is the
DUV, then the situation is reversed, where slave assertions are design properties that must be
verified and master assertions are environment assumptions that must be maintained.
In formal verification, only the DUV and ABPC are required to carry out the functional
verification, since no test vectors need to be generated. If master is the DUV, then slave
assertions are considered as constraints for the formal verification tool, which then verifies
the master assertions. If slave is the DUV, then master assertions are considered as con-
straints for the formal verification tool, which then verifies the slave assertions.

AB Protocol Checker (ABPC)


I I

Master Assertions Slave Assertions

.~
Block 81 III Block 82

---
P
I I,
,,
II I
I
i
1

!
.- Slave

~ ..... -- - ..•. - _._......_.._-....---- .. - -- --

Figure 16.5 Assertion-Based Protocol Checker Usage


433

PART?
Coverage Modeling
and Measurement
434
CHAPTER 17 Coverage Collection
Engine

The implementation of a coverage-driven verification environment is not possible without a


coverage collection and analysis utility. Fundamentally, coverage collection is the process of
storing values produced during the simulation runtime (i.e., signal values, or logical values
representing complex objects such as transactions) and then using these stored values to
decide whether or not relevant scenarios occurred during the simulation process. In its crud-
est fonn, coverage analysis can be done by dumping all signal value change infonnation and
then using this data to check for the occurrence of any target scenario. Even contemplating
such a brute-force approach for a real sized design points immediately at challenges that
must be addressed in a viable coverage collection and analysis utility:
• Storing all signal value change information is not practical for a real sized design
with non-trivial behavior. As such, a coverage collection utility should include lan-
guage constructs that provide concise, yet expressive mechanisms, for specifying the
information that must be collected during the simulation runtime.
• Extracting relevant information from a set of stored values is a non-trivial task at
best. As such, a coverage collection utility should include a coverage engine for ana-
lyzing and reporting coverage results extracted from the stored data.
SystemVeri log defines a set of powerful language constructs for specifying coverage
infonnation that should be collected. These constructs guide the underlying coverage collec-
tion engine to decide what values should be stored during simulation. This coverage engine
and its accompanying analyzer are then used during a post-simulation phase to analyze, com-
bine from multiple runs, summarize, and report coverage results. The coverage engine also
provides a method-based interface for querying coverage results at simulation runtime. This
feature allows coverage results to be used for guiding random scenario generation during
simulation runtime.
The coverage engine in a SystemVerilog simulation tool must at least support the col-
lection and reporting of constructs supported by the System Veri log language. The full set of
features included in the associated coverage analysis and reporting tool does, however. vary
among tool vendors. The effectiveness of a coverage analysis and reporting tool is affected
by the following factors:
• Reporting utilities
436 Coverage Collection Engine

• User interface
• Information, beyond those specified in the System Veri log program, that can be
extracted from a coverage database (e.g., additional crosses, transitions. etc.)
The following steps are followed during coverage collection and analysis:
• Create a coverage plan derived from the verification plan
• Identify information that must be collected in order to enable the coverage analysis
tool to extract the information required by the coverage plan
• Add coverage collection code to the verification environment so that information
identified in the previous steps are collected during the simulation nmtime
• After the completion of simulation, use a coverage analysis and reporting tool to
report coverage results needed by the coverage plan
This chapter presents language constructs provided by System Veri log for implementing
coverage collection. Steps for building a coverage plan and identifying the information that
must be collected are discussed in chapter 18. Details of using coverage analysis and report-
ing engines is beyond the scope of this book and are described extensively in vendor specific
operation manuals for these tools.

17.1 Coverage Collection Overview


In SystemVerilog coverage information can be collected for the value of an integral data
object, transitions in the value of an integral data object, and simultaneous values of multiple
integral data objects. A coverage group is the abstraction provided by System Verilog for
specifying a set of data objects that are sampled at the same time. A coverage group imple-
mentation may include the following coverage collection elements:
• Point coverage
• Transition coverage
• Cross coverage
Point coverage provides information on how many times each possible value (e.g., 4),
or a value in a range of values (e.g., a number in the range [2:12]), of an integral variable (e.g.,
integer, logic, bit, etc.) was sampled during the simulation runtime. Transition coverage col-
lects information on what sequence of values were sampled for an integral variable (e.g.,
how many times the value for an integer variable changed from 3 to 12 and then to 14, or how
many times the value for an integer variable changed from a value in the range [12:99] to a
value in the range [33:123]). Cross coverage collects information on what simultaneous val-
ues were sampled for two or more integral variables (e.g., how many times two integer vari-
ables contained values 3 and 5 at the same time, or values in ranges [2:13] and [33:132] at the
same time).
Conceptually, coverage collection is carried out by collecting information on how many
times each possible value of a coverage element was sampled during the simulation process.
Practically, however, storing information for each possible "alue of a coverage element leads
to two problems:
Coverage Collection Overview 437

• For some integral data types (e.g., 32-bit integer), it is not feasible to keep track ofthe
number of times each value was sampled.
• In the majority of cases, treating each possible value of a variable separately doesn't
provide any immediately useful information.
Consider a 32-bit address line in an address-mapped system bus where each peripheral
responds to a specific address range. Ideally, full coverage of the address decoding logic
requires that all possible address values are observed during the simulation runtime. Given
this view, full coverage is defined as each address value having occurred at least once. In
reality, however, achieving this ideal target is not possible because it is not practical to keep
count of all observed address values, and more importantly, it is infeasible to generate all
possible values of the address line within a reasonable time. These limitations make it
impractical to define full coverage as each address line having been observed at least once.
SystemVerilog uses the concepts of coverage bins and coverage hits to facilitate the
practical definition of what full coverage means for coverage group elements. In this
approach, possible values for coverage elements (i.e., point, transition, cross) are grouped
into bins and a required hit target is attached to each bin. Full coverage for a bin is defined as
the number of sampled values falling within the range for that bin exceeding that bin's target
hit value. Full coverage for a coverage point or cross is then defined as all of its bins being
fully covered. This approach is further extended through weights and goals to derive a quan-
titative measure of coverage (see section 18.3).
The concepts of bins and hits can be used to define a practical definition for full cover-
age of the system-bus example outlined above. In this approach, a coverage point is defined
as the value of the address line, coverage bins are defined to correspond to the address range
for each peripheral. The hit requirement for each bin can be set either to 1 (if observing at
least one address for each peripheral is judged to be a good test of correct operation of its
decoding logic) or a number proportional to the size of the address space for that peripheral if
more confidence is required. The actual decision of what weight value should be attached to
each bin is subjective and depends on the judgement of the verification team.
Sampling events for coverage group elements can be defined using a number of differ-
ent approaches. But not all values sampled for coverage elements in a coverage group are
necessarily valid at every sampling event. For example, an address value falling in the range
for a peripheral may not contribute to the overall coverage if that peripheral is disabled when
that sample is observed. As such, SystemVerilog provides mechanisms for specifying when a
sampled value for coverage elements and coverage bins should be included in the overall
coverage calculations.
SystemVerilog provides additional features for controlling the definition, instantiation,
initialization, and activation of coverage groups. Details of these facilities are described in
the following sections.
438 Coverage Collection Engine
----------------------- ------------

~_? 2 Covera~e Groups ____._._______ .__ ._. . . . . . . ___ . _.. _. _. . ___._._ . __ .___ ._ . ._.
In SystemVerilog, the covergroup construct is used to define a coverage group containing a
set of interrelated coverage elements. All coverage elements included in a coverage group
must share the same sampling event, even though the actual sampling of individual coverage
elements may be disabled at a given sampling event.
Coverage groups are identified by the following properties:
• Covergroup name
• Coverage sampling event
• Fonnal arguments for customizing each instance of the covergroup
• Coverage elements
• Includes coverage points and coverage crosses
• Bin definitions for each coverage element
• Transition coverage specified as bins for coverage points
• Covergroup customizations using covergroup options
SystemVerilog treats a coverage group definition as a user defined data type. This
means that a coverage group object does not exist unless it is explicitly instantiated (except
for coverage groups defined in classes, section 17.5). Multiple instances of a coverage group
can be created by using the name of a covergroup definition. The following program shows a
simple example of a coverage group definition and instantiation:

'Program 17.1: Coverage Group Definition and Instantiation


1 module top:
2 bit reset, elk, sig_a, sig_b:
3
4 covergroup eg_ab @(posedge elk):
5 ep_a: coverpoint sig_a iff(!reset):
6 ep_b: coverpoint sig_b iff(reset):
7 endgroup
8
9 ep_ab cp_abO == newO:
10 endmodule

This program shows the definition for a coverage group e9_ab that tracks values
assigned to signals si9_a and sl9_b during simulation runtime. The default clocking event for
e9_ab is defined to be the positive edge of elk. Coverage group e9_ab contains two coverage
points ep_a and ep_b which collect infonnation for signals sl9_a and sl9_b respectively. Cov-
erage points ep_a and ep_b are sampled only when reset is set to 0 and 1 respectively. In this
configuration, coverage group e9_ab is activated at every positive edge of signal elk but the
value of sl9_a is sampled only if reset is set to 0, and the value of si9_b is sampled only if
reset is set to 1. An instance of c9_ab is declared and created by calling its new() method in
module top (line 9).
Coverage group sampling events and formal arguments are described in the following
subsections. Details of coverage elements are described in the sections 17.3 and 17.4. Cover-
age options are described in section 17.6.
Coverage Points and Transitions 439

17.2.1 Coverage Group Activation


A coverage group is activated at every occurrence of its sampling event, if one is defined. A
coverage group can also be activated procedurally by calling the coverage group predefined
method sampleO to activate an instance of a coverage group.
The sampling event for a coverage group can be in one of the following forms:
• Clocking event
• Block event expression
Clocking events are specified using the same notation used for defining the sampling
event for other language constructs such as an always block (e.g., @(posedge elk), @(slgnal_a),
@(negedge elk or reset), etc.). A coverage group is activated immediately (Le., its values are
sampled) upon the occurrence of its samplihg event.
SystemVerilog allows a coverage group instance to be stopped, restarted, or explicitly
activated using procedural statements. This usage is shown in the following example:

'Program 17.2: Using methods to explicitly control coverage group activation


1 module top;
2 bit sig_a, clk;
3
4 covergroup CQ_aa @(posedge clk);
5 cp_a: coverpoint sig_a;
6 endgroup
7
8
9
10 initial begin
11 #10 cg_aaO.stopO;
12 =
#10 sig_a 10;
13 cg_aaO.sampleO;
14 #10 sig_a = 20;
15 cg_aaO.sampleO;
16 #10 cvr_a.startO;
17 end
18 end module
L ____________________ ___

In this example, the predefined stopO method of covergroup construct is used to stop
the automatic activation of coverage group eg_aaO at time 10 (line 11). After this time, eg_aaO
is not automatically activated when its sampling event (i.e., (posedge elk)) occurs. Coverage
group eg_aaO is explicitly activated using the predefined sampleO method (lines 13, 15).
Automatic activation of eg_aaO is restarted again by calling its predefined startO method
(line 16).

A coverag-e point is the fundamental unit of coverage collection as it defines the connection
between the simulation environment and the coverage model. This means that all values
brought into the coverage collection engine are sampled through a coverage point. These
sampled values are then used to collect point, transition, and cross coverage information.
440 Coverage Collection Engine

A coverage point is identified by the following properties:


• Label: Name given to the coverage point
• Source: Integral variable whose value, or expression whose result, is to be sampled
• Domain: Set of possible values for the coverage point source
• Guard expression: used for conditional activation of the coverage point
• Value bins: Coverage bins defining relevant value ranges
• Transition bins: Coverage bins defining relevant transitions between value ranges
The following program shows a simple example of using a coverage point in the defini-
tion of a coverage group:

'Program 17.3: Coverage point source definition and its variations


1 module top:
2 bit reset, elk = 0:
3 bit [1 0:01 length, length1:
4
5 covergroup cgJength @(posedge elk):
6 cp_len: coverpoint length iff (!reset):
7 coverpoint length 1 iff (!reset):
8 coverpoint (Iength+length 1) iff (!reset):
9 endgroup
10
11 cg_length evUength =new:
12 end module

In this example, three coverage points are defined in coverage group cg_length (lines
6-8). A label can optionally be specified for a coverage point. For example, label cp_len is
specified for the coverage point defined on line 6. If no label is specified and the coverage
point source is a single variable, then the name of that variable is used as the label for that
coverage point. For example, the label for coverage point defined on line 7 is length1 and is
taken from the variable that it samples. If no label is assigned to a coverage point and the
coverage point source is an expression, then an automatically generated and tool specific
label is specified implicitly for that coverage point (line 8). The label specified for a cover-
age point is used to call the predefined methods of that coverage point. It is also needed when
specifying a cross coverage element that includes that coverage point.
A coverage point source is defined as the variable or expression whose result is sam-
pled for that coverage point. A coverage point source can be a single integral variable or the
result of an expression that produces an integral value. In the above example, coverage point
cp_len (line 6) samples variable length, coverage point length 1 (line 7) samples variable
length lo and the coverage point defined on line 8 samples the result of expression
(length+length 1). A coverage point domain is defined as the set of all possible values for its
source. For example, if the coverage point source is an enumerated type, then its domain is
the set of all literals defined explicitly for that enumerated type. If the coverage point source
is an M-bit integer, then its domain is the set of 2M values that can be represented by that inte-
ger.
Each coverage point may include a coverage point guard expression. Upon occurrence
of the sampling event for the containing coverage group, a value for the coverage point is
sampled only if the sampling guard expression evaluates to true. In the above example, the
cO\'erage points defined on lines 6-8 are sampled if variable reset is set to zero (i.e .. reset is
Coverage Points and Transitions 441

inactive). Different sampling guard expressions may be defined for coverage points in the
same coverage group. As such, the sampling time of each coverage point can independently
be customized to conditions that make it relevant.
Values sampled for a coverage point are organized into coverage bins. Two types of bins
can be defined for a coverage point: a value bin or a transition bin. A coverage point value
bin identifies a range of values for the coverage point while a coverage point transition bin
defines a set of transitions for the value of a coverage point. Value and transition bins are
described in section 17.3.2.

17.3.1 Coverage Point Sampling Semantics


Generally speaking, a coverage point source is sampled when its containing coverage group
is activated. This simplistic view of coverage collection may lead to problems with and/or
limitations on sampled values. A problem may occur when a coverage group is activated
multiple times in the same time-slot. A limitation may exist when sampled data should be
taken from a specific scheduling region within a time-slot in which the coverage group is
activated. This section describes techniques that allow better control over coverage group
activation and sampling.
In a given time-slot, the sampled value of a coverage point depends on two factors:
• Scheduling region in which the coverage group is activated
• Scheduling region in which coverage point values are sampled
The activation time of a coverage group is well defined when a coverage group is acti-
vated explicitly (i.e., using sampleO method) or by using a block event expression. In these
cases, the activation time depends on sequential flow of program execution which is gener-
ally well defined and not susceptible to race conditions in which a code segment may be exe-
cuted mUltiple times when only one pass was intended.
The situation is somewhat more complex when a clocking event expression is used for
activating a coverage group. In such cases, activation of the coverage group depends on
changes in signal values and events, and not on program execution flow. Given that a clock-
ing event may be triggered multiple times in a given time-slot, using clocking event expres-
sions may lead to a behavior where a coverage group is activated multiple times in the same
time-slot. This behavior mayor may not be useful depending on the intended purpose of cov-
erage collection.
System Verilog provides a coverage type option (see section 17.6) for preventing a cov-
erage group to be activated more than once during anyone time-slot. The strobe option, if
specified, forces a coverage group to be activated in the postponed region of the time-slot in
which its sampling event was triggered. This activation will happen only once, even if the
sampling event was triggered multiple times during that time-slot. The following example
shows the use ofthis option along with a sampling event defined as a clocking event:
covergroup cg_a @(posedge elk);
type_option.strobe:: 1;
cp_a: coverpoint sig_a iff(!reset);
endgroLJp
442 Coverage Collection Engine

In summary, the number of times and the scheduling region where a coverage group is
activated can be controlled by using the strobe option. When the strobe option is not used,
the coverage group is activated once for each triggering of its event expression even if this
happens multiple times in a time-slot. When the strobe option is used, the coverage group is
activated only once at the end of the time-slot in which its event expression was triggered at
least once.
In SystemVerilog, values sampled for a coverage point are tightly connected with the
activation time of the containing coverage group. Essentially, sampled values are the value of
the coverage point source at the time of activation. More fine-grained control of this behav-
ior may, however, be needed, depending on the specific coverage collection requirements.
Values sampled for coverage collection may need to be taken from any of the following
regions of the current time-slot:
• Stable values before entering the current time-slot
• Values in the observed region of the current time-slot, resulting in stable values of
sampled variables in the current time-slot before any property pass or fail code is exe-
cuted
• Stable values at the end of the current time-slot
Using the strobe option results in a coverage group to be activated in the postponed
region of the cycle in which its clocking event is activated. Given the default behavior of
coverage collection in which variables are sampled at the time of activation, this behavior
results in the sampled values to be taken from the postponed region of the current time-slot,
yielding the final stable values of the sampled variable in the current time-slot.
Sampling stable values from preponed or the observed regions can be accomplished by
using a clocking block. An example of this approach is shown in the following program:

'Program 17.4: Using clocking blocks to control sampling time of coverage points
1 module top;
2 bit reset, clk = 0;
3 bit [1 0:01 length, length1;
4
5 clocking cb @(posedge elk);
6 input #1step preponedJength = length;
7 input #0 observedJength :: length;
8 end clocking
9
10 covergroup cg_length @(posedge elk);
11 cp_pl: coverpoint cb.preponed_length iff (!reset);
12 cp_ol: coverpoint cb.observed_length iff (!reset);
13 endgroup
14
15 cg_length cgJengthO = new;
16 . end module
~

In this program, reading the value of variable eb.preponedJength returns the value of
variable length in the preponed region of the last time-slot in which clocking event (posedge
elk) was triggered. Similarly, reading the value of variable eb.observed_length returns the
\ulue of variable length in the observed region of the last time-slot in which clocking event
(posedge elk) was triggered. Collecting coverage on these variables (lines 11, 12) leads to the
collected coverage to correspond to the values of variable length in the preponed and
0bserwd regions respectively.
Coverage Points and Transitions 443

17.3.2 Coverage Point Value and Transition Bins


SystemVerilog uses the concept of a coverage bin to group together values and transitions
that need not be differentiated for coverage collection purposes. Obviously, the need for any
differentiation is dictated by the coverage plan. As an example, consider a generic packet
that has a valid size between 10 and 1000 bits. A crude coverage plan may only require cover-
age infonnation on the number of short, valid, and long packets that were observed during
the simulation process. This plan would require only three value bins, each covering ranges
[1:9], [10:1000], and [1001:MAX_VALUE], respectively. During the simulation runtime, the count
attribute of the bin corresponding to each sampled value will be incremented (e.g., sampling
a size of 8 results in incrementing the counter for the first bin).
Transition bins are also important for coverage collection purposes. For example, the
coverage plan may require knowledge of how many times a short packet was followed by a
valid packet and then by a short packet again. A transition bin is used to collect this type of
coverage information. In this case, the transition bin is defined by three ranges of values con-
sisting Of[1:9], then [10:1000] and then [1:9]. When any three consecutively sampled values of
packet size fall into these consecutive ranges, then the count attribute of this transition bin is
incremented.
Value and transition bins are described in the following subsections.

17.3.2.1 Coverage Point Value Bins


A value bin is identified by the following properties:
• Label: The name that identifies the value bin
• Domain: The set of values that define the bin
• Count: The number of times the sampled value is equal to any bin member
• Size: The number of values in bin domain
• Guard Expression: Used for conditional update of bin count
Consider the following example program:

I Program 17.5: Coverage point bin definition constructs


1 module top;
2 bit length_is_relevant, reset=O, elk = 0;
3 bit [9:0jlength;
4
5 eovergroup cgJength @(posedge elk);
6 cp1: coverpoint length iff (!reset) {
7 =
bins bb1 {[1:10j, [8:12j, [20:30], 34, 38, 34, [80:88]};
8 bins bb2[2j = {[111 :120]} iff (lengthJs_relevant);
9 bins bb3[3] = {(111 :120]} iff (lengthJs_relevant);
10 bins bb4[] = {[121 :220]} iff (Iength_is_relevant):
11 bins bb5 = default;
12 bins bb61] = default;
13 }
14 ep2: coverpoint length iff (!reset) {
15 bins begin_domain = {[$:10]};
16 bins end_domain = {[10:$]};
17 bins full_domain = {[$:$j};
18 }
19 endgroup
20
444 Coverage Collection Engine

21 cgJength cg_engthO =new(length, 34);


22 end module

This program shows the definition of coverage group cg_length which contains cover-
age point definitions CP1 and CP2' The following aspects of coverage point bin definition are
highlighted in this example:
• Multiple value bins may be defined for each coverage point (lines 7-12).
• Bin domain specification (Le., the syntactical representation of bin values) can be
given using a mix of single values (e.g., 34, 38 on line 7) and/or ranges of values (e.g.,
[1:10],[8:12] on line 7).
• A bin domain specification may include duplicated values or overlapping ranges of
values, leading to the same value being specified multiple times. All such multiply
included values are assumed to have been specified only once. For example, the over-
lapping ranges [1:10],[8:12]on line 7 can be written equivalently as [1:12]. Also, note
that for bin bb 1 (line 7), value 34 is assumed to have been specified only once even
though it is included in its bin member specification twice.
• A sized bin array notation can be used to create a fixed number of bins for a range of
values (line 8, 9). If the number of bins (NB) divides the number of values in bin
domain specification (NV) evenly (i.e., NV%NB=O), then each bin will have (NV/NB)
values in its domain. For example, bin definition on line 8 creates two bins (bb2 [0]
and bb2[1]), whose members are defined by ranges [111:115] and [116:120] (having sizes
5 and 5) respectively. If the number of bins does not divide the number of values
evenly (i.e., NV%NB>O), then the remaining values are included in the last bin, which
will then contain (NB/NB + NV%NB) domain values. For example, the bin definition on
line 9 creates three bins bb 3[0], bb 3[1], and bb 3[21, whose members are defined by
ranges [111:113], [114,116], and [117,120] (having sizes 3,3, and 4), respectively. IfNB is
larger than NV, then one bin is created for each value.
• An unsized bin array notation can be used to create a bin array whose number of bins
is derived from the number of values in the bin domain specification. In this case, one
bin is created for each value included in the bin domain specification, with each bin
having a domain size of 1. The square bracket notation (i.e., "[ J") is used for creating
unsized bin arrays. For example, bin definition on line 10 creates 100 bins
(bb 4 [O], ... ,bb4[99]). each having a single member taken consecutively from the range
[121 :220].
• A bin definition may optionally include a bin guard expression. The count attribute of
a bin (or any of the bins in its bin array) is incremented only if the condition in its bin
guard expression is satisfied.
• Bin domains for bins of the same coverage point may overlap. In this case, the count
attribute of all bins that include the overlapping value are incremented if the over-
lapped value is sampled during coverage collection.
• The default keyword refers to all values in the domain of a coverage point that are
not included in the domain of any of the bins specified for that coverage point. For
example, the definition on line 11 defines a single bin bbs having as its members all
values not already included in any of the other bin definitions for coverage point CP1'
The definition on line 12 detines a bin array bb s having one bin for every value not
already included in any of the other bin definitions for coverage point CP1'
• Keyword "S" can be used to refer to the beginning or end value of a coverage point
Coverage Points and Transitions 445

domain. As such, the full domain of a coverage point can be represented by range
[$;$]. This notation is used in the above example to define bins describing a beginning
range, an end range, and the full domain of variable length (lines 15-17).
System Verilog allows bins to be marked as illegal or irrelevant to coverage collection.
Illegal bins correspond to values in the coverage point domain that should not occur during
the simulation process. Ignored bins correspond to values that do not contribute to coverage
collection and should be ignored for coverage collection purposes. The following program
shows examples of illegal and ignore bin definitions:

'Program 17.6: Illegal and ignore bins


1 module top:
2 bit lengthjs_relevant, reset=O, elk = 0:
3 bit [9:0] length:
4
5 covergroup egJength @(posedge elk):
6 ep3: eoverpoint length iff (!reset) {
7 bins valid_bins[] = {(11 :500]}:
8 illegal_bins nogood_bins = {[$:10]}:
9 ignore_bins noeare_bins = {[501 :$]}:
10 }
11 endgroup
12 end module

In this example, bins nogood_bins and nocare_bins are defined as illegal and ignored
bins, respectively. A runtime error message is generated if a member of an illegal bin is sam-
pled during the simulation runtime, even if that value is a member of another bin for the
same coverage point. A value specified as a member of an ignored bin, is ignored in every
bin having that value as a member. Also, keyword default cannot be used to define the con-
tents of an ignored bin.
Bins are automatically created for any coverage point that does not have any explicitly
defined bins. In this case, an implicit sized bin array is created for the coverage point. The
number of bins in this bin array is taken from the coverage group option auto_bin.:..max. Bin
domain specification for this implicit bin definition is assumed to be the full domain of the
coverage point (i.e., all possible values for the coverage point source). Given the bin array
size and number of possible values for the coverage point, domain for each bin is decided
according to the creation rule for sized bin arrays described earlier in this section. The auto-
matic creation of bins is shown in the following example:

rp;-ogram 17.7: Implicit bin definitions


1 module top:
2 bit lengthjs_relevant, reset=O, clk = 0;
3 bit [9:0] length;
4
5 covergroup cgJength @(posedge clk):
6 cp4: coverpoint length iff (!reset) {
7 /I The following implicit bin statement is assumed for cp4:
8 /I bins auto[option.auto_bin_max] = {[2:$]};
9 illegal_bins nogood_bins = {O};
10 ignore_bins nocare_bins = {1};
11
12
13 cp5: coverpoint length iff (lreset) {
14 option.auto_bin_max = 3;
15
446 Coverage Col/ection Engine

16 endgroup
17 end module

In this example, coverage point CP4 has only illegal and ignore bins and no explicitly
defined bins. In this case, an implicit bin creation statement (line 8) is used for creating the
bins for CP4' The number of bins is taken from option auto_bin_max, and the full domain of
the coverage point, excluding the values indicated in the ignore and illegal bins, is divided
between these bins forming the domain for each bin. The full domain of coverage point CP4 is
[0:1023] given that length is a 10-bit value. For an enumerated data type, the full domain of
coverage point consists of the set of all literals for that enumerated type.

It is possible to modify the value for option auto_bin_max for each coverage point.
This syntax is shown for coverage point CPs where the number of bins is set to 3.

17.3.2.2 Coverage Point Transition Bins


In SystemVerilog, value transition coverage for a data object is collected by defining transi-
tion bins for the coverage point that samples that data object. A transition is defined as the
change of value from one sampling event to the next sampling event. A transition set repre-
sents a set of transitions (e.g., 1=>3, 1=>4). A transition set can be specified by giving ranges
of values for the consecutive samples of a transition (e.g., {1,3}=>{4,5}). A transition sequence
specifies changes in the value of a coverage point across multiple (two or more) sampling
cycles. A transition sequence set represents a set of transition sequences. Transition length
gives the number of cycles involved in a transition sequence. For example, the transition
length of a transition is 2, while the transition length of a transition sequence may be 2 or
more. Simple examples of these transitions are shown below:
Transition: (12 => 14)
Transition Set: ({[12: 13].16} => ([14:16], 18}) representing transitions:
12 => 14, 12 =>15,12 =>16.12 => 18.
13 => 14, 13 =>15.13 =>16,13 => 18.
16 => 14.16 =>15,16 =>16.16 => 18.
Transition Sequence: (13 => 14 => 15 => 16)
Transition Sequence Set: ({[12: 13)) => 14 => 15 => {[16:17]}) representing transition sequences:
12 => 14 => 15 => 16.
12 => 14 => 15:;:> 17.
13 => 14 => 15 => 16,
13=>14=>15=>17

The values of a transition set or a transition sequence set at each cycle can be given
using the same notation used for bin domain specification for value bins (i.e., using single
value, a range value, using formal arguments, etc.).

SystemVerilog provides repeat operators for specifying transition sequence sets. These
constructs include:
• Consecutive repeat operator
• Goto repeat operator
• Non-consecutive repeat operator

The beha\ior described by each operator is similar to those defined for sequence repeat
operators (section 15.3.3). The following examples show the use of these repeat operators.
Coverage Points and Transitions 447

Abbreviation ATS (Any Transition Sequence) represents a transition sequence of any num-
ber of cycles and any values for each cycle.
Consecutive Repeat: (12 [*3]) represents transition sequence:
(12 => 12 => 12)
Goto Repeat: (12 [-> 3]) Represents transition sequence:
(ATS => 12 => ATS => 12 => ATS => 12)
Non-Consecutive Repeat: (12 [= 3]) Represents transition sequence:
(ATS => 12 => ATS => 12 => ATS => 12 => ATS)

A range of repeat values can be given for a repeat operator. Examples of these operators
are shown below:
Consecutive Range Repeat: 12 [*2:3]) represents transition sequences:
(12 => 12), (12 => 12 => 12)
Goto Range Repeat: (12 [-> 2:3]) Represents transition sequences:
(ATS => 12 => ATS => 12),
(ATS => 12 => ATS => 12 => ATS => 12)
Non-Consecutive Range Repeat: (12 [= 2:3]) Represents transition sequences:
(ATS => 12 => ATS => 12 => ATS),
(ATS => 12 => ATS => 12 => ATS => 12 => ATS)

A transition sequence may have a bounded or an unbounded length. Transition


sequences that make use of goto repeat and non-consecutive repeat operators have an
unbounded length since these transition sequences allow for any transition sequence of any
length to come in between occurrence of their operands.
A transition bin is identified by the following properties:
• Label: The name that identifies the transition bin
• Domain: Transition sequences included in bin domain specification
• Count: The number of times a transition included in bin domain is observed during
coverage collection
• Size: The number of transition sequences in bin domain
• Guard expression: Used for conditional update of bin count
Consider the following example program:

'Program 17.8: Transition bin definitions


1 module top;
2 bit length_is_relevant, reset=O, elk = 0:
3 bit [9:0] length;
4
5 covergroup cg-'ength @(posedge elk);
6 cp-,ength: coverpoint length iff (!reset) {
7 bins tb1 = (10 => 20), ({11,12} => {21,22}) iff (Iength_is_relevant);
8 bins tb2 = ({[10:20],30} => ([40:50],60});
9 bins tb3[] = (12 [*2:3) => 30,40);
10 bins tb4 = (12 [=2:3] => 30,40);
11 bins tb5 = (12 [->2:3] => 30,40);
12 illegal_bins tb6 = ({[1000:$]) => {[$:$]} => ([1000:$)});
13 ignore_bins tb7 = ({[$:9]) => {[$:$l} => ([$:9]});
14 bins tb8 = default sequence;
15 /I bins tb8[] = default sequence; /I illegal
16 /I bins tb4 = (12 [=2:3] => {30,40}); /I illegal
17 /I bins tb5 = (12 [->2:3] => {30,40}); /I illegal
18 }
19 endgroup
20 endmodule
448 Coverage Collection Engine

This program shows the definition of coverage group c9_length containing coverage
point cp_length. The following aspects of transition bin definition are highlighted in this
example:
• MUltiple transition bins may be defined for a coverage point (lines 7-14).
• Transition bin domain specification (i.e., the syntactical representation of bin transi-
tion sequences) can be given using a mix of single transitions (e.g., 10=>20 on line 7)
and/or sets of transitions (e.g., 11,12=>21,22 on line 7).
• An unsized bin array notation can be used to create a bin array whose number of bins
is derived from the number of transition sequences in the transition bin domain spec-
ification. In this case, one bin is created for each transition sequence included in the
bin domain specification, with each bin having a size of 1. The square bracket nota-
tion (i.e., "[ ]") is used for creating unsized bin arrays. For example, the bin definition
on line 9 creates four bins (tb 3[0] •... ,tb3[3]), each having a single transition domain
taken respectively from transition sequences (12=> 12=> 30). (12=> 12=> 40), (12=> 12=>
12=> 30). and (12=> 12=> 12=> 40).
• A transition bin definition may optionally include a bin guard expression. The count
attribute ofa bin (or any of the bins in its bin array) are incremented only if the con-
dition in its guard expression is satisfied.
• Domains for transition bins specified for the same coverage point may overlap. In
this case, the count attribute of all bins that include the overlapped transition
sequence are incremented if the overlapped transition sequence is observed during
coverage collection.
• The default sequence keyword refers to all transition sequences in the domain of a
coverage point that are not included in a domain of any of the transition bins speci-
fied for that coverage point. The bin definition on line 14 defines a single bin tb a hav-
ing as its domain all transitions not already included in any of the other bin domains
for coverage point CP1. It is illegal to use this keyword with an unsized bin array defi-
nition (line 15).
• Unsized bin arrays cannot be used for unbounded length transition sequences (lines
16, 17).
• Illegal and ignore bins can also be specified for transition bins (lines 12, 13).

Transition and value bins described in this section provide a mechanism for collecting
coverage on the value of a coverage point in one or across multiple sampling cycles. Cross
coverage is used to collect information about the simultaneous values observed for two or
more coverage points. Cross coverage is described in the next section.

17.4 Cross Coverage Elements


_._- ---.---.. -------.~-.-.~----~.--.-.---.------.-----------.--------~-.---~----

A cross coverage element collects information on simultaneous values (i.e., values at the
same sampling event) of t\\'o or more coverage points. As such. cross coverage is used to col-
lect infonnation about the cOlTelation between t\\·o or more variables or expression results.

Cross coverage description is identified by the following properties:


• Label: Name given to the cross coverage element
Cross Coverage Elements 449

• Source: Coverage points whose combined conditions are monitored


• Guard expression: Used for conditional activation of the cross coverage
• Cross Domain: Cross product of value bins for all cross element sources (i.e., cover-
age points used in defining the cross element).
• Cross bins: bins defined as a subset of cross domain, specifying simultaneous values
of coverage points that should be grouped.
The foHowing program shows a simple example of using a cross coverage element in
the definition of a coverage group:

'Program 17.9: Cross coverage element definition


1 module top;
2 bit lengthjs_relevant, reset=O, clk = 0;
3 typedef enum {TOO_SHORT, SHORT, MEDIUM, LONG, TOO_LONG} length_t;
4 length_t length_A, length_B, length_C;
5
6 covergroup cgJength @(posedge clk);
7 cp_A: coverpoint length_A:
8 cp_B: coverpolnt length_B;
9 xc_AB: cross cp_A, cp_B iff (!reset);
10 xc_AC: cross cp_A, length_C iff(lreset);
11 endgroup
12 endmodule

In this example, coverage group cg_length contains two coverage points cp_A and cp_B.
Cross coverage element xc_AB is defined to collect coverage information on simultaneous
values sampled for coverage points cp_A and cp_B. A cross coverage element must be
defined in terms of coverage points. However, if a cross coverage is defined in terms of an
integral variable, then an implicit coverage point is created for that variable and the cross
coverage element is defined based on this implicitly defined coverage point. Cross coverage
element xC_AC (line 10) is defined in terms of coverage point cp_A and variable length_C. In
this case, an implicit coverage point is created for variable length_C which in tum is used in
creating the cross coverage element xc_AC.
A cross coverage element may optionally include a cross coverage guard expression
(lines 9, 10). A cross coverage element is evaluated if the expression for its guard expression
evaluates to true. Otherwise, the cross coverage element is ignored.
A cross coverage source is a coverage point used in defining that cross coverage ele-
ment. Sources of a cross coverage element must oe from the same coverage group. It is ille-
gal to define a cross coverage element whose sources are taken from different coverage
groups.
Data collected for cross coverage elements is organized in terms of value bins of its
source coverage points. This means that a cross coverage element does not keep track of its
coverage point values but what value bins in these coverage points had a hit. Cross domain is
the set of all bins in the cross product of bins for each cross element source. For example, if
coverage point cp_a has bins bah ba2, and coverage point cp_b has bins bb 1, bb 2, then the
cross domain for xc_ab (cross of cp_a and cp_b) is given by the set {(ba1,bb1), (ba1,bb2),
(ba2,bb1), (ba2,bb2)}. Coverage point value bins marked either as illegal or ignore are not
included in defining cross domain. A cross bin has a hit if all bins in its definition have a hit.
For example. bin {ba1,bb1} is hit at a sampling event if both bins ba1 and bb 1 have a hit in that
450 Coverage Collection Engine

sampling event. SystemVerilog provides special constructs for defining bins for a cross ele-
ment as a subset of bins in its domain. Cross coverage bins are described in the following
subsection.

17.4.1 Cross Coverage Bins


Cross coverage bins are defined in terms of value bins for coverage points used to define that
cross coverage element. In the absence of any bin definition, a cross coverage element will
implicitly include all bins in its domain. Consider the following example program:

IProgram 17.10: Cross coverage bin definition


1 module top;
2 bit lengthJs_relevant, reset=O, clk = 0;
3 bit [15:0] packetjength, link_speed;
4
5 covergroup cg_length_speed @(posedge clk);
6 cp_len: coverpoint packetjength {
7 ignore_bins way-tao_short = {[$:2]};
8 =
bins too_short {[3: 1OJ};
9 bins short = ([11 :200)};
10 bins regular[3] = {[201 :800]};
11 bins long = {[801: 1OOO]};
12 bins too_long = {[1001 :$]};
13 }
14 cp_sp: coverpoint link_speed {
15 iIIegaLbins way-too_slow = {[$:2]};
16 bins too_slow = {[3:10]};
17 bins slow = {[11 :100]};
18 bins average[3] = {[1 01:1 OOO]};
19 bins fast = {[1001:10000]};
20 bins too_fast = ([10001 :$)};
21 }
22 xcJength_speed: cross cp_len, cp_sp iff (!reset);
23 endgroup
24 endmodule

Coverage group cg_length_speed defines coverage points cp_len and cp_sp. These cov-
erage points represent coverage information collected for the length of a packet and the
speed with which this packet was received (exact method of calculating this speed is not rel-
evant to this context). Each coverage point includes a number of bin definitions that repre-
sent length and speed qualities that may be of interest for coverage collection purposes.
Cross coverage element xc_length_speed (line 20) is defined in terms of coverage points
CP_len and cg_speed. Given that no bins are explicitly defined for xc_length_speed, the full set
of bins in domain of cross xc_length_speed is implicitly created. The domain for cross ele-
ment xc_length_speed is shown in figure 17.1, where all squares in this diagram correspond to
one cross bin for xc_length_speed. Note that value bins way_too_slow and way_too_short are
not included in this diagram, since they are marked as illegal and ignore, respectively. The
highlighted areas in this diagram shmv the grouping of individual bins into larger bins by
using special syntax introduced later in this section.
In this figure. every row corresponds to one value bin of coverage point cp_len, and
ewry column corresponds to one value bin of coverage point cp_sp. Every square in this grid
cOlTesponds to a cross coverage bin for cross coverage element xc_speed_length.
Cross Coverage Elements 451

cp_sp

S' tn Q)
ii!' S'
10 i C6 re. 0
!I!. ~(1) Iii!'
re.
~
:9 -'"
~

~ .....
-'" -'" 'i= '::;j ..... .....
..... ..... ~ ~ ~ 0 0
.9 ~ 0
0 ~ :..j -'"
..... ~
.9 0 0 0
.9 .9 0 0
0 ~
.9 0
.9

too_short [3:10]

short

[0]

~ regular [1]

~ [2]

long

too-'ong

Figure 17.1 Cross Product Bin Creation

The grid in figure 17.1 shows the full domain of cross coverage element
xc_speed_length. For a cross element composed of three coverage points, the figure would be
drawn in three dimensions. SystemVerilog allows coverage bins for a cross coverage element
to be defined as a subset of its domain. To that end, SystemVerilog provides special syntax
for defining the desired subset. Specifying a subset for a two dimensional cross coverage ele-
ment is described using the diagram in figure 17.1. Such specification for higher dimension
cross coverage elements follows naturally from this discussion.
The following forms can be used to define any subset of bins in figure 17.1:
• All bins in a row (or a set ofrows)
• All bins in a column (or a set of columns)
• All bins at the intersection of a row and a column (or a set ofrows and columns)
• Any combination of the above forms
SystemVerilog provides the binsoj, conjunction, and disjunction operators to define any
of the subsets outlined above.
The billsoj operator is used to select a subset of value bins for the bins of a coverage
point (i.e .• a number of ro\\'s or columns in figure 17.1). The syntax for this construct is:
binsof(bin_expression) intersect (range_expression)
452 Coverage Collection Engine

Operator" /" can be used to negate the sense of selected bins so that bins specified using
this notation are excluded from the set of selected bins. This operator can be applied to only
a binso.f construct. The bin conjunction overator "&&" is used to define the intersection of
bins selected using the binsof construct (i.e., intersection of rows and columns in figure
17.1). The bin disjunction overator "II" is used to combine bins selected using the binsofand
conjunction operators. Examples of bins selection using this syntax are shown in table 17.1.

Bin Selection Statement Cross Coverage Bins Selected


blnsoltcp_len) rows 1,2,3,4,5,b,1
binsoft cp_len .too_short) row I
binsof(cp_len.regular[OJ) row 3
binsoft cp_len. too_short) II binsoftcpJen.regular[ I]) rows 1,4
binsof(cp_Ien.regular) rows 3,4,5
binsoftcp_len.regular) intersect ([300:500J) rows 3,4
!binsoftcpJen.regular) rows 1,2,6,7
!binsoftcp_len.regular) II binsoftcpJen.regular[OJ) rows 1,2,3,6,7
binsof(cp_sp.average) columns 3,4,5
binsof(cp_len.too_short) && binsoftcp_sp.average) intersection of row I and columns 3,4,5

Table 17.1: Coverage Point Bin Selection for Cross Coverage Bin Definition

The program below shows the definitions for cross coverage bins shown in figure 17.1.

,iprogram 17.11:Cross coverage bin definition

1 xcJength_speed: cross cpJen, cp_sp iff (!reset) {


2 bins xc_too_short = binsof(cp_len.too_short)
3 bins xc slow =
4 blnsof(cp_sp) intersect {(3:1 ~O)} && (binsof(cpJen) intersect {(11: 1OOO};
5 bins xc_almost_average =
6 binsof( cp _ sp. average[O]) && (binsof(cpJen) intersect {[11 :1OOO} II
7 binsof(cp_sp.average[2]) && (binsof(cpJen) intersect {(11: 1OOO};
8 bins xc_average =
9 binsof(cp_sp.average[1]) && (binsof(cpJen) intersect {[11 :1000};
10 =
bins xc_fast binsof(cp_sp.average) &&
11 binsof(cp_sp) intersect {[1001 :$]} && (binsof(cpJen) intersect {[11 :1000};
12 bins xc_too_long = binsof(cpJen.tooJong)
13 }:

Any of the bins defined in this program can be marked as ignore or illegal. In case of an
overlap, the ignore and illegal settings take precedence in handling a sampled cross value.

A cowrage collection group can be embedded in a class declaration. This embedding is use-
ful in that the coverage group can freely collect co\'erage on private and protected class
Class-Based Coverage Col/ection 453

members. In addition, each class instance can include a new instance of the coverage group
allowing coverage infonnation to be collected for each instance of a class object.
A coverage group included in a class is treated the same as other composite data objects
that must be explicitly created (e.g., a class object). This behavior results in the following
restrictions in dealing with coverage groups that are embedded inside class declarations:
• The name of a coverage group declared inside a class cannot be used for any other
class member (data object or coverage group).
• A coverage group declaration inside a class is one and the same as declaring a pointer
to that coverage group. As such, the name of that coverage group cannot be used to
declare a new pointer to that coverage group. This is in contrast to using coverage
groups in other blocks where the name of a coverage group declaration must be used
as a data type to instantiate coverage groups of that type.
• A coverage group declared inside a class has to be explicitly allocated. Otherwise, no
coverage group is created and no coverage is collected for that class.
The following program shows an example of coverage groups embedded in a class dec-
laration:

iProgram 17.12: Coverage groups embedded in a class


1 module top;
2 class subpacket;
3 byte data;
4 endclass
5
6 class packet;
7 bit clk;
8 subpacket subp;
9 byte data;
10
11 covergroup cg_subp @(posedge elk);
12 cp1: coverpoint subp.data;
13 endgroup
14
15 covergroup cg_data @(posedge elk);
16 cp1: coverpoint data;
17 endgroup
18
19 function newO;
20 cg_subp = new();
21 cg_data = new();
22 endfunction
23 endclass
24 end module

This program highlights the following aspects of embedding coverage groups inside a
class declaration:
• Coverage groups C9_subp and c9_data are embedded inside class packet.
• Classes may contain multiple coverage group declarations.
• Each coverage group declaration is assumed to be an implicit creation of a pointer to
a coverage group object. The actual coverage group must be allocated explicitly. In
this example. cO\"erage groups C9_subp and C9_data are allocated inside the class con-
structor (lines 20. 21 ).
• Names C9_subp and C9_data cannot be used for any other class member.
454 Coverage Collection Engine

• Any variable referenced inside a coverage group declaration must be declared before
the declaration of that coverage group. In the above example, class properties elk, data
and subp are declared before coverage groups that reference these variables.
A class derived from a base class that includes a coverage group inherits that coverage
group as well. It is possible to redefine the definition of that coverage group by using its
name to define a new coverage group in the derived class. In this case, the coverage group in
the parent class can still be accessed using the super keyword. This access is, however,
allowed only for the immediate parent class of a derived class.

17.6 Coverage
-- ... - -
Options-------
---- -----------------_. _. -
"---- - "-_._-_. _.. _.. _--_. --_.. _----- -_._---------- --~--------.---.-------- ..-

A number of coverage options can be specified along with coverage constructs. These
options can be specified at the following syntactical levels:
• Coverage groups
• Coverage points
• Cross coverage elements
In addition, options can be specified for a coverage group type or for each instance of
that coverage group. Also, some options specified at the coverage group level imply defaults
values for the same options at the coverage point or cross element levels, unless overridden
at that level by providing a new value for that option.
Table 17.2 shows the complete list of all options allowed for coverage constructs. Col-
umn 2 gives the range of valid values for each option. Column 4 and 5 indicate ifeach option
is available at the coverage group level as a type or instance option. Column 6 indicates
\\-hether an option specified at the coverage group level is considered a default value for cov-
erage points and cross elements included in that coverage group. Columns 7, 8, 9 and 10
indicate whether each option is available as instance/type option for coverage point and cross
elements, respectively_

17.
-_._-
7 Coverage
-
Methods
-_._-_. .... -_. __.. -

System Veri log provides predefined methods that can be called for coverage constructs, as
well as system tasks and functions used for managing the coverage database.
The following system tasks and functions are provided in System Veri log:
• $set_coverage_db_name (db_name)
• Sload_cOl·erage_db (db_name)
• Sget_coverage ()
Task Sset_coverage_db_"ame sets the name for the file that stores the collected cover-
age results_ Task $load_coverage_db loads coverage infonnation from the name passed as an
Assertion·Based Coverage 455

Group Point Cross


. . .=oS= ~ ...OJt:I .
..; t: .. ....
Option Type Description u OJ
t:I ~ ~

~ ~
.!! .!!
Q ~
.:l
t:I
.s
1 2 3 4 5 6 7 8 9 10
name string Covergroup name llQ
Weight of each construct for grade cal-
culation. Weight for type and instance
weight int are used for type and instance grading,
Ii!l Ii!l Ii!l Ii!l Ii!l It!
respectively.
goal 0-100 Target coverage defined as a percentage. Ii!l Ii!l Ii!l Ii!l Ii!l Ii!l
comment string Text to include in the coverage report. Ii!l Ii!l Ii!l Ii!l Ii!l Ii!l
A bin must have at least this many hits
at_least int
before it is considered covered.
Ii!l Ii!l Ii!l 0
Maximum number of automatically gen-
auto_bin_max int Ii!l 0 Ii!l
erated bins for coverage points.
Maximum number of automatically gen-
cross_auto_bin_max int
erated bins for cross elements.
Ii!l 0 Ii!l
Number of missing (not covered) cross
product bins that must be saved to the
cross_ num-print_missing int
coverage database and printed in the
~ 0 0
coverage report.
Issue warning ifbins defined for a cover-
detect_overlap bool
age point include overlapped members.
0 0 0
Collect information for each instance of
per_instance bool
the coverage group.
0
Activate the coverage group only once at
strobe 011
the end of the time-slot.
0

Table 17.2: Coverage Options

argument. Function $get_coverage returns a real number between 0 and 100, giving a mea-
sure of overall coverage collected so far.
Predefined tasks and functions for coverage constructs are shown in table 17.3. In this
table, columns 3, 4, and 5 indicate whether that method is defined at syntactical levels of
coverage groups, coverage points, or cross elements, respectively.

17.8 Assertion-Based Coverage


---=------
Transition bins for coverage points provide a mechanism for collecting coverage on specific
changes in value of variables and expressions. However, this approach is useful only for tran-
sitions that can be described with the limited sequence description constructs allowed for
coverage transition bins. The cover variant of concurrent assertion statements provides a
mechanism for collecting co\"erage on complex behaviors that can be specified using proper-
ties and sequences.
The syntax for this construct is as follows:
456 Coverage Collection Engine

Method Description
(,.')
-
= .s., .,e
1:1.

e U
Il.
.,

I 2 3 4 5
void sampleO Activate a coverage group b!J
real get_coverageO Returns type coverage grade 0 0 0
real get_inst_coverageO Returns instance coverage grade 0 0 0
void seUnst_name(string) Set the name for each coverage group instance 0
void startO Starts collecting coverage information 0 0 0
void stopO Stops collecting coverage information 0 0 0
Returns the cumulative coverage information
real queryO
(for the coverage group type as a whole)
0 0 0
Returns the per-instance coverage information
real inst_queryO
for this instance
0 0 0

Table 17.3: Predefined Coverage Tasks and Functions

[Iabell cover property (property-expression) [pass_statement];


[Iabell cover property (sequence_expression) [pass_statement];

The cover directive is used to include the results of evaluation of a sequence or property
in coverage results. A pass statement can be specified for this statement, which is executed
anytime the sequence succeeds or the property evaluates to true.
If the cover variant of assertion statement is used with a property expression, then cov-
erage is collected on the following conditions:
• Number of times property evaluation is attempted
• Number of times the property succeeds
• Number oftimes property succeeds vacuously
• Number of times property fails
The pass statement is called for each success of the property expression.
If the cover variant of assertion statement is used with a sequence expression, then cov-
erage is collected on the following conditions:
• Number of times sequence evaluation is attempted
• Number oftimes sequences match
The pass statement is called each time the sequence matches, but at most, once in each
time-slot. Note that this behavior is different from the one for the assert statement where the
pass statement is called only on the first match of the sequence expression and at most once
in that time-slot. As an example, consider a sequence that when started at time 10, produces
matches at times 11 and 12. With an assert statement on such a sequence, the pass statement
is called only once, at time 11, since sequence evaluation stops because of the implicit use the
first match operator in a evaluating a property. With a cove,. statement on this sequence. the
pass statement is called once at time 11 and once at time 12.
CHAPTER 18 Coverage Planning,
Implementation, and
Analysis

Verification project productivity is not measured by the fact that verification progress is
made, but rather by how fast such progress is made. Coverage collection provides the sense
of direction necessary to guide a coverage-driven verification flow so that coverage progress
is made at the fastest possible rate. Given the direct impact of coverage collection results on
verification productivity, it is important to take the steps necessary for creating a well
designed and executed coverage collection flow.
A coverage collection flow consists of the following iterative steps:
• Coverage design and implementation
• Coverage collection
• Coverage grading
• Coverage analysis
In the coverage design phase, a coverage plan is produced. This coverage plan outlines
the types and quantity of information that must be collected in order to gain confidence in
full execution of the verification plan. System Veri log provides a rich set of coverage collec-
tion constructs leading to choices in how coverage collection can be implemented. As such,
an important part of coverage design phase is the decision of how to map coverage collection
targets into an actual implementation. Coverage grading refers to deriving a quantitative
measure from the set of data sampled during coverage collection. Coverage analysis pro-
vides a road map for using the coverage information collected so far to guide the following
simulation and coverage execution steps.
Chapter 17 provides a detailed description of SystemVerilog constructs for coverage
implementation. This chapter discusses coverage planning and execution phases and the
implementation of this strategy using constructs described in chapter 17. Section 18.1 gives
an example verification plan that is used as the source of examples discussed in this chapter.
Section 18.2 describes how coverage collection targets identified from a verification plan are
used to create a coverage implementation. Coverage grading and analysis are discussed in
sections 18.3 and 18.4, respectively.
458 Coverage Planning, Implementation, and Analysis

1B.1 XBar Verification Plan


Figure 18.1 shows a pictorial view of the 4-port crossbar switch XBar introduced in section
12.2. The XBar communication protocol is also described in section 12.2. In the configura-
tion shown in this figure, port 0 supports protocol Proto_A, port 1 supports protocol Proto_B,
and ports 2 and 3 support both protocols Proto_A and Proto_B. In this configuration, a port can
only initiate and respond to data requests that follow its supported protocol. Therefore, for
example, the agent connected to port 0 can respond to any incoming data request following
Proto_A and can initiate a data request to any port supporting Proto_A (i.e., ports 3 and 4).

Supports Proto_A

Supports Proto_B

Figure 18.1 XBar Design Coverage Setup View

A verification plan contains two types of scenarios, as related to coverage collection:


• Scenarios whose occurrence must be confirmed by collecting coverage
• Scenarios whose occurrence can be assumed because of the occurrence of certain
scenarios and the existence of environment checkers and monitors
For example, a beacon reply frame is always generated in response to a beacon request
frame. If the verification environment contains a scoreboard checking that sending a beacon
request frame from a source port to a destination port is followed by receiving a beacon reply
frame from the destination port at the source port, then coverage information need only be
collected for how many beacon request frames are sent from each source port to each desti-
nation port. For each generated beacon request frame, then the scoreboarding mechanism
implies that beacon reply frames moving in the opposite direction were also generated.
A partial verification plan for this environment is shown in table 18.1. Note that this
verification plan lists only scenarios relevant to coverage collection. The scenarios listed in
this partial verification plan serve as examples for co\'erage implementation approaches
described in the remainder of this chapter. This \'eritication plan consists of four separate
sections described in the following paragraphs.
XSar Verification Plan 459

Scenario Transfer Src Port Dest Port


0 l,l,J
Verify that each agent connected to
~ I 0,2,3
z an XBar transmit port responds cor·
Beacon Request
1= rectly to beacon requests arriving at 2 0,1,3
~ that port.
0 3 0,1,2
=:
=: 0 2,3
t: Data Request
2 0,3
~ Verify that each agent connected to Protocol A
3 0,2
~
an XBar transmit port responds cor·
rectly to data requests arriving at I 2,3
that port. Data Request
2 1,3
Protocol B
3 1,2

Scenario Packet Kind Following Idle Time Idle Time


0-100
Data Request/Reply protocol A
101-1000
~ Verify that long idle time preceding
Z
.... 0-333
a packet on the XBar receive port Data Request/Reply protocol B
~
.... 334-3333
does not cause problems .

....""
~ 0-555
Beacon Request/Reply
S 556-5555

Verify that idle time before each Data RequestlReply protocol A >1000
packet should not exceed the maxi· Data Request/Reply protocol B > 3333
mum time allowed for that kind. Beacon Request/Reply > 5555

XBar Receive Port Packet Kind Sequence


Scenario
From Packet To Packet To Packet
~'"
OZ Data Request A
=-9 SOF
Data Request B
~""
>00
Verify that for all legal packet Data Reply A
----
.... Z sequences arriving at XBar receive Data ReplyB
el~ ports, transfers formed by these
Data Request A
~"" packets are handled correctly by the
switch and the destination agents SOF
Beacon Request Data Request B
Beacon Reply Data Reply A
Data Reply B

Scenario XBar Transmit Port Packet Kind Sequence


""=-0
=:'"
OZ 1,2, or 3 consecutive SOF (one from each port)
",,- Veri fy that for packet sequences
~~
",Z
appearing at an XBar transmit port
(listed on the right), these packets
3 consecutive SOF followed by 2 Data Request A
3 consecutive (SOF followed by Data Reply A)
Z~ are handled correctly by the switch
~"" and the destination agents. ···list other interesting combinations as necessary····
""
Table 18.1: Partial Verification Plan for the XBar Design

The first section describes scenarios involving frames flowing bet\veen source and des·
tination ports. Note that this listing does not include reply type transfers, since it is implicitly
assumed that such transfers are generated in response to request type transfers and the auto-
matic checking and scoreboarding mechanisms in the verification environment \'erify that
the expected behavior for these transfer kinds is followed. Also note that the list of data
460 Coverage Planning, Implementation, and Analysis

request frames reflects the assumption that not all port agents support both protocols proto_A
and proto_B (See figure 18.1 for protocols supported by each port agent).
The second section lists scenarios that are required for checking idle time behavior at
each receive port of the XBar design. This section highlights the requirement that each group
of packet kinds has a different maximum allowed idle time. Note also that the ranges of
allowed delay values for each group is defined differently.
The third section lists the set of legal packet kind sequences that can be observed at any
of the XBar receive ports. The list of legal transitions is derived from the set of guidelines
described in the XBar communication protocol (section 14.1). All transitions starting with an
SGf packet (e.g., SOF packet followed by another SOF packet) and not listed in the table are
considered illegal and should not occur during the simulation process.
The fourth section lists "interesting" packet kind transitions at the transmit port of the
XBar design. Note that at XBar transmit ports, the number of possible packet kind sequences
is more than can efficiently be enumerated (unlike those at the XBar receive port). The rea-
son is that packets may arrive from any of the other ports and hence the number of packet
kinds and their possible orderings is a large number (e.g., an SOF packet followed by another
SOF packet is possible at the transmit port of the XBar design, since these SOF packets may
have arrived from different source ports).
The scenario categories shown in this table are used in the following sections to moti-
vate the need for, and illustrate, different coverage implementation approaches.

18.2 Coverage Design


Coverage design is the process of creating a coverage plan whose implementation and exe-
cution provides information that is necessary for deciding whether or not all verification plan
scenarios have occurred.
The structure and content of a coverage plan depends on the following issues:
• Glue logic that must be created between the verification environment and coverage
collection constructs
• Choice of SystemVerilog constructs used in implementing the elements of a coverage
plan
This interdependence essentially implies that a clear view of the final implementation of
the coverage plan (not only what constructs but also what sampling events, and variables to
be sampled and their timing) is required before details of a coverage plan can be laid down.
The following subsections describe these issues. The ideas presented in this section are then
used in section 18.3 to create the coverage plan for the XBar design.

IS.2.1 Coverage Collection Glue Logic


A coverage tool can collect only the following types of information:
Coverage Design 461

• Point coverage: How many times an integral variable (e.g., integer, logic, bit, etc.)
held a specific value (e.g., 4), or a value in a specific range (e.g., a number between 2
and 12).
• Transition coverage: What was the sequence of values assigned to a single integral
variable (e.g., how many times the value for an integer variable changed from 3 to 12
to 14, or how many times the value for an integer variable changed from a value in the
range 12-99 to a value in the range 33-123 to a value in the range 144-188).
• Cross coverage: What simultaneous values were assigned to two or more integral
variables (e.g., how many times two integer variables contained values 3 and 5 at the
same time, or values in ranges 2-13 and 33-132 at the same time).
A coverage engine does not really understand or have any knowledge of composite data
objects (e.g., packets) or multi-cycle behaviors (e.g., bus read cycle) that are usually the sub-
ject of coverage collection. This means that before coverage can be collected on any simula-
tion related behavior, that behavior must be made identifiable through one of information
types outlined above. For example, collecting coverage on how many times a system reset
has occurred is straightforward, since it constitutes a point coverage where the number of
positive edges for the reset signal are counted throughout the simulation process. Coverage
collection for abstract behaviors is, however, more involved. Consider a memory read opera-
tion that takes place across multiple simulation cycles. Collecting coverage on the number of
times that a memory read operation is performed for an address in the range Ox1112-0x1122,
requires one or more integral values to be created for storing information about the latest bus
operation type and its memory address. Only then can the required coverage information be
collected by sampling these integral variables.
The following steps must be performed in making simulation behaviors identifiable to
coverage collection:
• Collecting coverage on abstract data types, where each data item travels over multi-
ple simulation cycles (e.g., a data packet).
• Synchronizing the sampling of two asynchronous (i.e., timing independent), yet veri-
fication-dependent behaviors (e.g., injection of a packet of type A at port 1 followed
within 10 ns by injection of packet of type B at port 2).
Abstract data items, referred to in the first case, fall into two categories: I} data items
that are usually a part of the implementation of the verification environment (e.g., an XBar
packet), and 2} data values that are specific only to coverage collection (e.g., time lapse
between two events). In the first case, the verification environment has already been imple-
mented using these abstract data items so building coverage specific glue logic, for these
data items is not required. In the second case, special code must be added to the environment
to make this information available to coverage collection. For example, in the XBar verifica-
tion plan, it is not necessary to build special coverage related glue logic since the monitor
attached to each port already extracts these packets in a form that can readily be used in cov-
erage collection. In collecting coverage on scenarios related to idle times, however, a special
coverage related variable must be added to store the idle time before each variable so that
this idle time value can be sampled at the same time as coverage is collected on a packet.
The synchronization between two time-independent behaviors is a common require-
ment in implementing coverage collection. Consider a verification scenario for the XBar
design 'whose activation requires that two beacon request transfers are initiated to the same
4(;2 Coverage Planning, Implementation, and Analysis

port within 10 ns of each other. Collecting coverage on such conditions requires coverage
glue logic to be added to the environment.

18.2.2 Cross Coverage Implementation Models


Once the necessary coverage glue logic is put in place, the required coverage information
can be collected through coverage points, transitions, and crosses. The next step in building a
coverage plan is to decide how to collect the required coverage information.
Coverage collection on a single variable is straightforward and can be implemented
using coverage points. Collecting coverage on interdependent variables is, however, more
involved. Multi-variable coverage implementation falls into one of the following coverage
models:
• Muti-instance
• Hierarchical
• Multidimensional
• Multi-definitional
These implementation models are described in the following subsections.

18.2.2.1 Multi-Instance Coverage Models


A multi-instance coverage model uses multiple instances of the same coverage group defini-
tion to collect information on the same type of data from multiple places in the environment.
SystemVerilog coverage constructs allow coverage information to be accessed for each
instance (i.e., instance coverage) independently as well as collectively for all instances of the
coverage group (i.e., type coverage).
Multi-instance implementation is best suited to cases where data to be observed origi-
nates or terminates in multiple places in the environment and there is interest in maintaining
coverage information about each individual location as well as aggregate information on the
data items targeted for coverage collection.
The verification plan for the XBar design requires that coverage be collected on packet
types arriving at each receive port of the XBar design. A multi-instance model is a natural
implementation choice for this coverage requirement since data objects to be sampled have
the same format at all locations, and by using a multi-instance implementation, queries can
be made on coverage values for each instance (how many packets of each type were
observed at each port), as well as the aggregate results for all instances (how many packets of
each type was observed at all ports).
The foHowing example shows a multi-instance implementation of collecting coverage
on the kinds ofXBar packets observed at the XBar receive port.

iProgram 18.1: XBar receive monitor coverage collector


1 module top;
2 bit elk, reset;
3 typedef enum {INVALID, SOF, BEACON_REa, BEACON_REPLY, DATA_REa_A,
4 DATA_REa_B, DATA_REPLY _A. DATA_REPLY _B) packeCkind;
5
Coverage Design 463

6 class xbar_packet;
7 rand bit [3:0) data;
8 rand bit [3:0] src_addr;
9 rand bit [3:0) descaddr;
10 function packeCkind get_packet_kindO; /I implementation not shown
11 endfunction
12 endclass
13
14 class xmt monitor;
15 event coLcoverage ;
16 rand xbar_packet pkt;
17 int porCnum;
18
19 covergroup cg_xbar_packet @(col_coverage);
20 option.peUnstance 1; =
21 pkind: coverpoint pkt.geCpackeCkind{) iff (Ireset) {
22 bins pkind_binsO = {[Ul}:
23 ignore_bins nocare_bins = {SOF, BEACON_REPLY,
24 DATA_REPLY_A, DATA_REPLY_B}:
25 =
illegal_bins nogood_bins {INVALID}:
26 }
27 endgroup
28
29 function new{int pnum);
30 cg_xbar_packet = new{);
31 =
pkt new{);
32 porCnum = pnum;
33 endfunction
34 end class
35
36 xmt_monitor monO =new{O): /I monitor instance for port 0
37 xmt_monitor mon1 =new(1): /I monitor instance for port 1
38 xmt_monitor mon2 =new(2); /I monitor instance for port 2
39 xmt_monitor mon3 =new(3): /I monitor instance for port 3
40 end module

The above implementation shows a generic description of the XBar packet (lines 6-12).
This implementation includes a function declaration that extracts the packet kind from the
content of the packet (lines 10-11). A simplified class-based implementation of an XBar
receive port monitor is also shown (lines 14-34). For illustration purposes, all monitor
instances are created in this top level example (lines 36-39). In the real environment, each
monitor instance would exist in its appropriate verification component (see section 13.6).
The above implementation defines a coverage point based on the result of the packet
kind returned by function get_pkt_klndO. One bin is created for each packet kind (line 22). An
ignore bin is defined, reflecting the fact that only packets relevant to scenario creation should
be tracked. Note that SOF packet kind is also placed in the ignore bin, since checkers in the
environment guarantee that any packet of type DATA_REQ_A or DATA_REQ_B is preceded by
an SOF packet. An illegal bin is also defined to include the INVALID packet kind. Note that
instance based coverage collection is activated by using the appropriate option (line 20).
SystemVerilog coverage query functions allow coverage information to be collected for
each monitor instance as well as for the coverage type as a whole. Instance coverage infor-
mation for this example gives information on how many of each packet kind was observed at
the port corresponding to that instance. Type coverage information for this example provides
information on how many packets of each kind were observed at all receive ports.
464 Coverage Planning, Implementation, and Analysis

In the next section, this example is extended to include a cross coverage element
between packet type and its destination port. The result provided for this cross element is
more interesting in that instance coverage information for this cross element provides infor-
mation on how many packets of each kind were sent from the port associated to that instance
to each destination port, and type coverage information on this cross element provides infor-
mation of how many of each packet kind was received at each destination port from all
source ports.
Multi-instance coverage implementation can be emulated through a cross coverage def-
inition by introducing a variable corresponding to each location where a coverage group
instance is placed. This approach, however, defeats the main advantage of an instance based
implementation where coverage collection is viewed as tightly connected to structural blocks
in the design and the verification environment.

18.2.2.2 Multidimensional Coverage Models


A multidimensional coverage model is used in cases where information must be collected on
the relationship between multiple variables. The cross coverage element is the natural choice
for implementing a multidimensional coverage requirement. The identifying feature of a
multidimensional coverage model is that all bins obtained by creating the cross element may
be relevant to coverage collection, subject to selective elimination of cross bins using illegal
and ignore bins. This feature is in contrast to multi-definitional and hierarchical models (see
sections 18.2.2.3 and 18.2.2.4), where bin definitions may change depending on specific
cross combinations.
The following program shows the implementation of coverage collection on the rela-
tionship between packet kind and destination port in the XBar design.
r:--~--~------~-

Program 18.2: Multidimensional coverage collection using cross coverage


L

1 covergroup cg_xbar_packet @(coLcoverage);


2 option.perjnstance = 1;
3 pkind: coverpoint pkt.get_packet_kindO iff (!reset) {
4 bins pkind_bins[) = {[$:$]};
5 ignore_bins nocare_bins" {SOF, BEACON_REPLY,
6 DATA_REPLY_A, DATA_REPLY_B}:
7 illegal_bins nogood_bins = {INVALID};
8 }
9 dest_addr: coverpoinl pkt.dest_addr iff (!reset) {
10 =
bins dbins[] {[$:$]};
11 iliegaLbins nogood_bins = {(4:$]);
12 }
13 kind_cr.osS_dest: cross pkind, desCaddr iff (!reset) {
14 b~ns breq[] "binsof(pkind) intersect {BEACON_REa};
15 bIOS dreq_A[] " binsof(pkind) intersect {DATA_REa_A};
16 ?InS dre~_B[]" binsof(pkind) intersect {DATA_REa_B};
17 Illegal_bins nagoed bins=
18 binsef(dest_addr.dbins[Oj) and
19 . binsef(pkind.pkind_bins) intersect {DATA_REa_B} or
20 blnsof(~est_addr.dbins[1J) and
21 blnsof(Pkind.ptype_bins) intersect {DATA_REQ_A};
Coverage Design 465

22
23 endgroup

L ______~_________
The implementation shown above is intended to replace the one shown in program 18.1.
In this implementation, a coverage point corresponding to the destination address of each
observed packet is added to the coverage group (lines 9-11). Appropriate definitions are
used to define illegal bins (line 11), and to also define a bin array for this coverage point (line
10). Cross coverage element kind_cross_dest, defined using destination address and packet
kind is also added to this coverage group (lines 13-21). The following observations hold
about this implementation:
• The instance based coverage results for kind_cross_dest gives information on how
many packets of each kind have been sent from the port corresponding to the given
instance to each destination port. The type coverage result for kind_cross_dest gives
information on how many packets of a given kind are sent from all ports to a given
destination address.
• Bin array breq (line 14) contains four bins, one for each destination port. Each bin
provides a count of how many BEACON_REQ packets have been sent to the destina-
tion address corresponding to that bin.
• Bin arrays dreq_A and dreq_B (lines 15, 16) each contain four bins, one for each desti-
nation port with each bin providing a count of how many DATA_REQ_A and
DATA_REQ_B packets, respectively, have been sent to the destination address corre-
sponding to that bin.
• Bins ofpkind and dest_addr that are already marked as ignore or illegal in their defini-
tion (i.e., pkind.nogood_bins, pkind.nocare_bins, dest_addr.nogood_bins) are automati-
cally excluded from consideration when forming the bins for their cross product. As
such, it is not necessary to explicitly mark kind_cross_dest bins that include these bins
as illegal or ignore.
• Illegal bin kind_cross_dest.nogood_bins is defined to include cases where the combi-
nation of valid values for pkind and dest_addr produce invalid combinations. This
includes cases where a DATA_REQ_A packet is sent to port 1 and a DATA_REQ_B
packet is sent to port O. In this notation (binsof(dest_addr.dbins[OJ)) selects dest_addr
bin corresponding to port 0 and (binsof(pkind.pkind_bins} intersect {DATA_REQ_B})
selects the bin of pkind that corresponds to DATA_REQ_B packet.

Table 18.2 shows a visual representation of the bin space for cross product element
kind_cross_dest. Cross product bins excluded from the set of valid bins are marked in this
table with the line number causing its exclusion. Bins not excluded on rows marked as
BEACON_REQ, DATA_REQ_A, and DATA_REQ_B are form bin arrays breq, dreq_A and dreq_B
respectively. Also note that the cross product space shown in this table is larger than the
domain for cross product kind_cross_dest since it also shows rows corresponding to illegal
and ignored bins of its coverage points, which are not included in the domain of a cross cov-
erage element.
466 Coverage Planning, Implementation, and Analysis

::>
o
co
0- o
0" o

U> I~

U>

Table 18.2: XBar Packet Kind VS. Destination Address Bin Space

18.2.2.3 Multi-Definitional Coverage Model


A multi-definitional coverage model is used in cases where the definition of ranges for one
variable changes, depending on the value of another variable. For example, in the XBar
idle-time requirements, the definition of idle time values changes according to the type of
packet that interrupts the idle period. As shown in table 18.1, idle ranges for a DATA_REQ_B
packet is defined as {[O:333], [334:3333], (3334.$]), and idle ranges for a BEACON_REQ packet is
defined as {[O:555], [556:5555], [5556,$]}. In such cases, a straight cross coverage element can-
not be used to implement this coverage requirement. Multi-definitional coverage model is
used to implement this special coverage collection requirement.
The strategy for implementing a muti-definitional coverage model is to first define a
coverage point for each definition of the variables involved, followed by combining these
coverage points to create a cross coverage element, and then excluding from the resulting
cross coverage element all bins that represent illegal combinations of coverage point bins.
The following program shows the implementation of a multi-definitional model to
implement the coverage collection requirement for the XBar idle-time scenarios.

'Program 18.3: Multi-definitional coverage collection example


L-

1 covergroup cg_xbar_packet @(col_coverage);


2 option,per_instance 1:=
3 idle_pkind: coverpoint pkt.get_packet_kindO iff (!reset) {
4 =
bins pkind_bins[] {[$:$]};
5 ignore_bins nocare_bins = {SOF};
6 illegal_bins nogood_bins = {INVALID}:
7 }
8 idle_Beacon: coverpoint idle_time iff (!reset) {
9 option. weight =0;
10 =
bins b1 {[0:555]};
11 bins b2 = {[556:5555]};
12 bins b3 = {[5556:$]}:
Coverage Design
467
-
--------------------.------------------------------------
13 }
14 idle_Proto_A: coverpoint idle_time iff (!reset) {
15 option.weight = 0;
16 bins b1 = {[0:1 OO]};
17 bins b2 = {[1 01: 1OOO]};
18 bins b3 = {[1001:$]};
19 }
20 idle Proto B: coverpoint idle_time iff (!reset) {
21 - option.weight = 0;
22 bins b1 = ([0:333)};
23 bins b2 = {[334:3333]};
24 bins b3 ={[3334:$]};
25 }
26 idle_cross_kind : cross idle_pkind, idle_Proto_A, idle_Proto_B, idle_Beacon {
27 bins beaconO = binsof(idle_Beacon) and
28 binsof(pkind) intersect {BEACON_REO, BEACON_REPLY}.
29 bins proto_A[] =binsof(idle_Proto_A) and '
30 binsof(pkind) intersect {DATA_REO_A, DATA_REPLY_A}.
31 bins proto_BO =binsof(idle_Proto_B) and '
32 binsof(pkind) intersect {DATA_REO_B, DATA_REPLY_B}·
33 iIIegaLbins b_nogood[] =binsof(idle_Beacon.b3) and '
34 binsof{pkind) intersect {BEACON_REO, BEACON_REPLY}.
35 iIIegaLbins pA_nogooc1l] = binsof(idle_Proto_A.b3) and '
36 binsof(pkind) intersect {DATA_REO_A, DATA_REPLY_A};
37 iIIegaLbins pB_nogoodO = binsof(idle_Proto_B.b3) and
38 binsof(pkind) intersect {DATA_REO_B, DATA_REPLY_B);
39 ignore_bins nocare_bins = default;
40 }
41 endgroup

The above implementation makes use of separate coverage point definitio


idle_beacon, idle_Proto_A, idle_Proto_B, for each bin-grouping of idle time values. Ev~s
though it is possible to include all such bin definitions in one coverage point, using separatn
coverage points leads to a more compact cross coverage bin definition. The cross covera e
element idle_cross_kind, is defined as a cross between coverage points idle_pkind, idle_beaco~e
idle_Proto_A, and idle_Proto_B. Bin arrays idle_cross_kind.beacon, idle_cross_kind.proto_A, and
idle_cross_kind.proto_B are defined to hold the cross coverage information for idle tim
depending on the packet type that ends the idle period. A default statement is used (line 3;)
to remove from consideration all cross bins that are not included in any of the explicit!
defined bins. y

In the above implementation, coverage points idle_beacon, idle_Proto_A, and


idle_Proto_B do not contribute to cov~rage, as independent coverage points and their sampled
values are relevant only when used III cross coverage element idle_cross_kind. As such, th
weight for each of these coverage points is set to zero so that their definition does not affe ~
the coverage grade (lines 9, 15, and 21 ). c

Table 18.3 shows a vi~ual repre~entation of the. bin .spa~e for cross coverage element
kind_cross_idle. Illegal and Ignored bills a~e marked. III thiS view. ~ote that each of the bin
arrays beacon, proto_A, and proto_B contaills four bills. correspondlllg to those required by
the verification plan.
468 Coverage Planning, Implementation, and Analysis

0: 0: 0:
,m 1>
0"
CD
L."
a ~a
Q)
0
0
::J
I~ 1°
tll

~ 0" 0"
~ cr 0"
~ 0" 0"
(.0) N

'?
N

v
'" N (.0)

(J1
C1I
U1
(J1 ~
(J1

Table 18.3: XBar Post-Idle Packet Kind vs. Idle Time Bin Space

18.2.2.4 Hierarchical Coverage Models


A hierarchical coverage model is used in cases where the collection of coverage information
is dependent on status observed during simulation runtime. A hierarchical coverage model is
a special case of multi-definitional coverage model where coverage col1ection is not relevant
to all possible combinations of the crossed elements and is defined only for specific values.
In the XBar verification plan, ports 0 and 1 support only protocols Proto_A and Proto_B
respectively. This means that the bins corresponding to packets for these protocols should be
marked as illegal in their appropriate coverage collection instances on ports 0 and 1.
The following program shows the implementation of this requirement using a simpli-
fied version of the approach used for a multi-definitional model:

,'Program 1B.4: Hierarchical coverage collection example

1 covergroup cg_xbar_packet @(col_coverage);


2 option.perjnstance = 1;
3 pkind: coverpoint pkt.get_packet_kindO iff (!reset) {
4 =
bins pkind_bins[] {[$:$]};
5 =
ignore_bins nocare_bins {SOF,
6 BEACON_REPLY, DATA_REPLY_A, DATA_REPLY_B};
7 illegal_bins nogood_bins = {INVALID};
B }
9 src_addr: coverpoint pkt.src_addr iff (!reset) {
10 =
option.weight 0;
11 =
bins dbins[] {[Ul};
12 illegal_bins nogood_bins = {[4:$l};
13
Coverage Design 469

14 kind_cross_src: cross pkind, src_addr iff (!reset) (


15 illegal_bins nogood_bins "
16 binsof(src_addr.dbins[O]) and
17 binsof(pkind.pkind_bins) intersect {DATA_REO_B} or
18 binsof(src_addr.dbins[1]) and
19 binsof(pkind .ptype_bins) intersect {DATA_REO_A};
20
21 endgroup

~-------------
This implementation is similar to the multi-definitional model with the exception that
only one coverage point src_addr is defined for the source address. Coverage point src_addr
is then combined with coverage point pkind to form cross kind_cross_src (lines 14--20). Bin
definition constructs are then used to define illegal combinations of packet and source
address that should be excluded from the set of cross coverage bins.
An alternative approach for building a hierarchical coverage model is to use a cross
coverage element and guard expressions for each bin of the cross coverage element.

18.2.3 Transition Coverage Implementation


In general, transition coverage is implemented as a set of bin definitions for the coverage
point that samples the value whose transitions are to be covered. This approach works well
for transitions that are simple to specify and depend on only a single variable. Using a cover-
age point to model transition coverage collection is, however, difficult for complicated tran-
sition sequences and not possible for transitions involving multiple variables without the use
of glue logic.
The following program shows the implementation of the transition coverage require-
ments for the packet kind observed at the receive port of the XBar design:

,rp;:-ogram 18.5: Transition coverage collection

1 covergroup cgJbar_packet @(col_coverage);


2 option.perjnstance = 1;
3 pkind: coverpoint pkt.get_packet_kindO iff (!reset) {
4 bins trans_bini [] =
5 {SOF} =>
6 {OATA_REQ_A,DATA_REQ_B,OATA_REPLY _A,OATA_REPLY _B};
7 bins trans_bin2[] =
8 {SOF} =>
9 {BEACON_REQ, BEACON_REPLY} =>
10 {OATA_REQ_A.OATA_REQ_B,OATA_REPLY_A,DATA_REPLY _B};
11 illegal_bins nogood_trans_bin = (SOF=>SOF);
12 bins remaining_trans_bin = default;
13 }
14 endgroup

~-----------------
Special transition bins trans_bin1 and trans_bin2 are defined to count the occurrence of
transitions defined in the verification plan. In addition, nogood_trans_bin is defined to iden-
tify illegal transitions (line 11) and the remaining transitions are included in bin
remaining_trans_bin (line 12), In this implementation, trans_bin1 bin array contains four bins
470 Coverage Planning, Implementation, and Analysis

and trans_bin2 bin array contains eight bins, corresponding to all possible transitions as
allowed by their definition.
Defining complex transitions using coverage point bin definitions is not straightfor-
ward. In such cases, assertion-based coverage is a good alternative that allows the full power
of sequence definition constructs to be used in collecting coverage on transitions of interest.
The following program segment shows the implementation of coverage collection on transi-
tions observed on the transmit port of the Xbar design (as required by the Xbar verification
plan in table 18.1).
cover property (@(mO.coLcoverage) ( mO.pkt.kind==SOF #:#1 mO.pkt.kind==DATA_REQ_A) ['2]);

The drawback of using assertion-based coverage is that such assertions cannot be


placed inside class declarations, and as such cannot be embedded inside class objects as cov-
erage instances can be.

Covera~e g-radin~ provides a quantitative measure of overall coverage progress. This quanti-
tative measure is given as a percentage where full coverage corresponds to a grade of 100%.
The overall coverage grade is computed recursively. First a grade is computed for each bin of
cross elements and coverage points. The grade for each element is computed using the grade
for its bins, the grade for each coverage group is computed using the grade for its elements,
and the overall grade is computed using the grade for all coverage groups in the environ-
ment.
The coverage grade for each coverage construct is computed as a weighted sum of the
coverage grade for its sub-elements. As such, it is possible to selectively emphasize specific
coverage elements in the overall coverage grade by changing their weight option. The fol-
lowing equations are used for computing the grade for different coverage constructs. In the
following equations, a coverage element is either a coverage point or a cross coverage ele-
ment.

. _ . (
Grade(bm) - mill 1.0,
Hits(bin)
I (b'
at east m
y
L Grade(bin)
Grade( element) = ",al......,lb""in.,.s:----:-=-:_
numberOfBins

L Weight(element) x Grade(element)
Grade(group) = ",al"..,1c",le""m""en""t,,-s_ _ _ _ _ _ _ _ _ _ _ _ __

L Weight(element)
all elements
Coverage Analysis 471

L Weight(group) x Grade(group)
Grade(global) = ""ai!...llg""ro""u""ps"--_ _ _ _ _ _ _ _ _ _ __

L Weight(group)
all groups

In above equations, only Hit(bin), the number of hits for a bin, is extracted from the envi-
ronment. All other values have default values which can be changed using specific options
provided in SystemVerilog (see section 17.6).
The following steps are used to customize coverage grading:
o Define the minimum number of hits required for each bin using the "aCleast" cover-
age option. A default value for this option can be set at the coverage group level. This
default value will be used for all bins defined for elements of the coverage group. If
necessary, this default value can be overridden for each coverage elements (i.e., cov-
erage points and cross coverage elements).
o Specify a weight for each coverage point, cross coverage element, and coverage
group. Note that different weights can be specified for type and instance coverage.
o Specify a goal for each coverage point, cross coverage element, and coverage group.
Note that different goals can be specified for type and instance coverage.
o During simulation runtime, use the predefined method geCcoverageO to get type
coverage grade for a coverage point, a cross coverage element, or a coverage group.
o During simulation runtime, use the predefined methodget_inst_coverageO to get
instance coverage grade for a coverage point, a cross coverage element, or a coverage
group.
o Alternatively, use a post-simulation coverage analysis tool to load and view the cov-
erage results.
Coverage grades computed for each coverage construct can be used during the simula-
tion runtime to guide the generation process towards the missing scenarios. Additionally,
post-simulation coverage analysis can help improve the environment andlor change coverage
goals to adapt to conditions observed during simulation. These considerations are discussed
in the next section.

18.4 Coverage Analysis


Functional verification is an iterative process. This means that throughout the verification
flow, all aspects of verification-including the verification plan, the verification environ-
ment, the coverage plan, and the coverage implementation and goals-will go through multi-
ple rounds of changes and enhancements before verification is declared complete. The
results of coverage collection provides the primary guidance in deciding how to proceed at
each step of this process.
Each pass through this iterative process consists of these phases:
o Planning
472 Coverage Planning, Implementation, and Analysis

• Execution
• Analysis
• Reaction
In the planning phase, coverage model is developed and implemented. In the execution
phase, coverage information is collected. The execution phase consists of running the com-
plete regression suite so that the contribution of all existing testcases to overall coverage
progress is taken into account. The coverage engine handles the task of combining results
from individual runs into a common coverage database. The result of coverage collection is
studied in the analysis phase to decide what action should be taken in the reaction phase. The
flowchart shown in figure 18.2 summarizes the steps taken in each phase.

REACT

Fix verification plan and


environment checkers

Fix coverage plan and


coverage implementation

Figure 18.2 Coverage Execution, Analysis, and Reaction Flow

The first pass through the execution phase is started after the first version of the cover-
age plan is designed and implemented. The results of the execution phase indicates whether
any illegal bins were hits and/or any coverage holes still exist. Illegal cases should ideally be
detected by checkers and monitors in the environment. As such, if an illegal case is detected
only as pal1 of coverage collection. then either the verification plan should be enhanced to
include this check or the monitors and checkers in the environment should be fixed to detect
this condition as well.

--------- - - - - - - - - - - - - - - - - - - - - - - - - -
Coverage Analysis 473

A coverage hole is a bin that was not covered during the simulation runtime. It is possi-
ble that a coverage hole may not have been relevant to the overall verification progress and
may have been marked as such because of a mistake in coverage design. If this is the case,
then the coverage plan should be updated to either remove the bin that was not covered from
coverage implementation or mark it as an ignored bin.
If a coverage hole is in fact relevant to achieving full coverage, then the random genera-
tion environment and its current constraints must be checked to decide whether continued
simulation using the same setting is likely to create the condition that covers the coverage
hole. No immediate action needs to be taken for such coverage holes.
If a coverage hole is not likely to be covered with continued simulation, then the current
generation constraints must be examined to see whether modifying these random generation
constraints can lead to the generation of the missing scenario. A new testcase should then be
added to the verification environment with the new generation constraints.
If the missing scenario cannot be generated by modifying random generation con-
straints, then the verification environment must be enhanced to facilitate the creation of the
missing scenario. For scenarios that are difficult to generate, it may be necessary to create a
unit testcase.
Verification is declared complete when no illegal bins are hit during coverage collection
and the global coverage grade exceeds the global coverage goal.
The flow shown in figure 18.2 handles one exception (i.e., illegal case, coverage hole)
at a time. In practice, however, a number of simulation runs should be completed before this
analysis is performed. In addition, during one pass through this flow, all or most illegal cases
and coverage holes should be studied, and steps for dealing with them taken before new sim-
ulation runs are started.
474 Coverage Planning, Implementation, and Analysis
475

PART 8
Appendices
476
APPENDIX A Predefined Classes of
the OVMLibrary

Type Class Name Parameters Extends Class


analySls_tito #(type T-mt) tim_analysIS_fifo
virtual analysis_if #(type T-int) tlm_if_base
default_report_server
virtual ovm_agent ovm_threaded_component
#(type BEFORE=int,
ovm _algorithmic_comparator type AFTER=int, ovm_component
type TRANSFORMER=int)
ovm_analysis_export #(type T-int) ovm_POtt_base
ovm_analysisJmp #(type T=int, type IMP-int) ovm--port_base
ovm_analysis_port #(type T=int) ovm--port_base
ovm_barrier oVIll_object
oVIn_barrier-pool ovm_object
ovm_blocking_get..export #(type T=int) ovm--port_base
ovm_blockingJ;eUmp #(type T-int, type IMP-int) ovm_pert_base
oVIl1_blocking_get_peek_export #(type T-int) ovm_port_base
ovm_blockin~get_peek_imp #(type T=int, type IMP=int) ovm_port_base
ovm_blockingJ;et--peek_port #(type T-int) ovm --port_base
o,'m_blocking_get--port #(type T-int) ovm_port_base
ovm_blocking_ll1aster_export #(type REQ=int, type RSP=int) oVIll--port_base
#(type REQ-int,
type RSP=int,
ovm_blocking_master_imp type IMP=int, ovm""port_base
type REQ_IMP=IMP,
type RSP _IMP=IMP)
o\,m_blocking_master_po,t #(type REQ-int, type RSP-int) ovm--port_base
ovm _blocking--peek_export #(type T-int) ovm--port_base
0\'1n_ blocking_peek _imp #(type T-illt, type IMP-int) Ovm--port_base
ovm_blocking_pcek--port #(type T=illt) ovm--port_base
o\,l11_hJocking_put_e.xport #(type T-int) ovmyort_base
O\'m_hlockinLPut_imp #(type T-int. type IMP-int) ovm _port_base
o,'m _hlockinLPut_port "(type T=int) ovm--port_base
(\\·111_ hlolo.':king_sI3\"e_c.\r0rt =1 typo REQ-int. type RSP-,int) ovm_port_base

Table A.I: OVM Predefined Classes


478

Type Class Name Parameters Extends Class


#(type RE~~mt,
type RSP=int,
ovm_blocking_slaveJmp type IMP=int, ov",...port_base
type REQ_IMP=IMP,
type RSP)MP=IMP)
ovm _blockinlLslave"'port #(type REQ=int, type RSP=int) ovm"'port_base
ovm_blockinlLtransport_export #(type REQ-int, type RSP-int) ovm"'port_base
#(type REQ=int,
ovm_block ing_transport._ imp type RSP=int, ovm...port_base
type IMP=int)
ovm_blocking_transport"'port #(type REQ=int, type RSP=int) ovm...port_base
ovm_buiItJn_clone #(type T-int)
ovm_buiIUn_comp #(type T=int)
ovm_builtJn_converter #(type T=int)
ovm_builtJn"'pair #(type TI=int, type T2-TI) ovm_transaction
ovm_class_clone #(type T-int)
ovm_class_comp #(type T-int)
ovm_class_converter #(type T-int)
ovm_class"'pair #(type T1-int, type T2=T1) Dvm_transaction
Dvm_ comparer
virtual ovm_component ovmJeport_object
#(type T-ovm_component,
ovm_component_registry ovm_object_wrapper
string Tname="ovrn_component")
ovm_contig_setting
twm_connector #(type IF-int) ovm_connector_base
virtual Dvm_connector_base ovm_component
OVnl_copy_map
virtual Dvm_driver ovm_threaded_component
virtual ovm_env ovm_threaded_component
Dvm_event ovm_object
oVnl_event_callback ovm_object
o\'m_event.J'ool ovm_object
Dvm _exhausti ve _sequence Dvm_sequence
ovmJactory ovm_object
ovm_factory_override
ovm_get_export #(type T-int) oVI11_port_base
ovm-'leUmp #(type T=int, type IMP-int) OVITI"'port_base
ovm _get"'peek_export #(type 1'=int) OVITI _.port _base
ovm-'let"'peekJmp #(type T=int, type IMP-int) ovm"'port base
ovm_get_peek"'port #(type T=int) ovm port base
ovm-'letJlort #(type T=int) o"Tn"'port_base
ovm_hash #(type T=int, II-int, 12-int)
ovm_hier"'printer_knobs o\'m printer knobs
QvmjCcontainer #(type IF=int)
ovmJn_order_built_il1_comparator #(type T-int) oVIll_in __ order_comparator
ovm_ in_order_class_comparator #(tl'pe T=int) oVlnjn order_comparator
#(type T=int.
0\'111_ in_ ordtr_comparator lyp~ CllTllP _type=<.wlll_builtjn_cnmp *(Tl.
o\' III _threaded_component
tyPl' ('on\'crt=o\'nl_huilt_in_ccm\'ertl.!r :!:(Tl.
typ~ rair tYr~=O\1ll t'luilt_in pair :;(1'))

lwm)nt_contig_seuing O\'Ill_clllltig setting

Table A.l: OYM Predefined Classes


479

Type Class Name Parameters Extends Class


ovrn_lineJlrmter ovm tree.J'rmter
ovm_master_export #(type REQ-int, Iype RSP=inl) ovm...p0rt_base
#(type REQ=int,
Iype RSP=int,
ovm_master_imp type IMP=int, ovm...port_base
Iype REQ_IMP=IMP,
type RSP IMP=IMP)
ovm_master"'port #(type REQ=int, type RSP-int) ovm"'port_base
virtual ovm_monitor ovm_threaded_component
ovm_nonblocking.$et_export #(type T-inl) ovm...port_base
ovm_nonblocking.$et_imp #(type T=int, type IMP-int) ovm"'port_base
ovm_nonb lockinlLget...peek_export #(type T-int) ovm...p0rt_base
ovm_nonblocking.$et_peekJmp #(Iype T=int, type IMP-int) ovm-port_base
ovm_nonblockinuel-peek-port #(Iype T=int) ovm_port_base
ovm_nonblockinuet-port #(Iype T=int) ovm-port_base
ovm_nonblocldng master_export #(type REQ-int, type RSP=int) ovm-port_base
j/(type REQ=int,
Iype RSP=int,
ovm_nonblockinlLmaster_imp type IMP=int, ovm-port_base
Iype REQ_IMP=IMP,
type RSP IMP=IMP)
ovm_nonblockinlLmaster-port #(type REQ=int, type RSP-int) ovm-port_base
ovm_nonblockinueek_export #(type T=int) ovm-port_base
ovm_nonblockiDg-PeekJmp #(type T-int, type IMP-int) ovm-port_base
ovm_nonblocking-peek-port #(type T=int) ov",-port_base
ovm_nonblockinuut_export #(type T=int) ovm-port_base
ovm_nonblocking-put_imp #(type T-int, type IMP=int) ovm-port_base
ovm_nonblockinuut_port #(type T=int) Dvm-port_base
ovm_nonblocking_slave_export #(type REQ=int, type RSP=int) ovm-port_base
#(type REQ~int,
type RSP=inl,
ovm_nonblocking_slave_imp type IMP=int, ovm-port_base
type REQ_IMP=IMP,
type RSP IMP=IMP)
ovm_nonblockinlLslave_port #(type REQ=int; type RSP=int) ovm...POrt_base
ovm_"onblocking_ transport_•• port #(type REQ-int, type RSP-int) ovm_port_base
#(type REQ=int.
oVI11_nonblocking_transpDrt_imp type RSP=int, Dvm-port_base
type IMP=int)
oV111_nonblocking_transport-port #(type REQ=int, type RSP=int) ovm_port_base
virtual ovm_object ovm_void
ovm_object_contiLsctting ovm_contig_setting
#(type T=ovm_object,
ovrn_object,Jegistry ovm_objecl_wrapper
string Tname="ovm_object")
virtual o"m_object_wrapper
ovm _options_container
ovm_packer
ovm-peek_export #(type T=int) ovm-port_base
ovrn,JleekJmp #(type T=int. type IMP=int) ovm-port_base
o\"m-peekyort #(t)pe T=int) ovm-port_base
\ inual o\'nl_phase
\'irtual 0\'", "'pon _hase ;(type IF=o\'1n_\0idl ovm-port_base_base
\inual (wm J'OT1_~ase _~a:ic =( type IF=o\'n1,Jeport_ object) IF

Table A.I: OYM Predefined Classes


480

Type Class Name Parameters Extends Class

ovmyrinter_knobs
#(type T -int)
o\'myu!Jmp #(type T=int. type IMP=int)
#(type T=int)
ovm _random_sequence ovm_sequence
oVIll_random_stimulus #( type trans _ type=ovm_ transaction) ovm_component
ovm_record~r

ovmJeportJlob.l_server
o\'mJeport _handler
virtual o\'ln_report_object
ovm_report_server

#(type REQ-ovl11_sequence_item.
ovm_reLLrsp_driver Dvm_driver
type RSP=ovm_sequencejtem)
#(type REQ=ovm_sequence_item.
Dvm_sequence
type RSP=ovm_sequence_item)
#(type REQ=ovl11_sequence_item,
virtual ovm_scenario ovrn_scenario_base
type RSP=ovm_sequence_item)

#(type REQ-ovm_sequence_item,
ovrn_scenal'io_controller
type RSP=ovm _sequence_item)
Dvm_scenario_controller_base OVm _threaded_component

#(type REQ-ovm_sequence_item.
virtual ovm _scenario_driver
type RSP=ovm_sequence_item)
oVm _threaded_component
\'irtual OVIn _scenario_driver_ noparam ovm_scenario_driver_base
\'irtual ovm_scenario_lloparam Dvm_scenario_base
O\"m _scope_stack

\ irtual lWIll _scoreboard Dvm_threaded_component

lWIll_seq_colls_if Dvm_component

0\"111_seq_item _cons_if Dvm _ compqncnt

Dvm_component

ovm _component

0"111_ sequence ()vm _sequence_item


ovrn_transaction
o\'m_sequencer 0\0'01_sequencer_base

virtual DVm _sequencer_base ovm _threaded_component


DVm _simple_sequence 0\'111_sequence

#(type REQ=int, type RSP=int) ovm_part_base


#(type REQ-int.
type RSP=int,
type IMP=int,
type REQ_I~fP=I~fP.
type RSP _IMP=IMP)
#(type REQ·-int. type RSP-int)
{\\"m_status_containa
()\"m _stimullls_ scenario

=IIYr~ T=intl
0\ 1ll __ table_l'rinter

Table A.I: OV1\1 Predetined Classes


481

Type Class Name Parameters Extends Class


oVm _table_prmler_knobs ovm hler prrnter_knobs
virtual ovm_test ovm_threaded_component
virtual ovm_threaded_component ovm_component
virtual ovm_transaction ovm object
ovm_transport_export #(type REQ-int, Iype RSP-inl) ovm-port_base
#(type REQ-int,
ovm_transporUmp type RSP=int, ovm-port_base
type IMP=;nl)
ovm_transport_port #(Iype REQ-int, Iype RSP-;nt) ovm_port_base
ovm _tree_printer ovm-printer
ovm_tree-printer_knobs ovm_hier-printer_knobs
ovm_virtual_sequencer ovm_sequencer_base
ovm_void
#(type REQ=ovm_sequenceJtem,
request_driver Dvm_scenario_driver
type RSP=ovm_sequence_item)
tim_analysis_fifo #(type T=inl) tim_fifo
tlm_b...set_export #(type TI =int, type T2-int) ovm_blocking_get_imp
tlm_b.J!et-port #(Iype T-int) ovm_blocking...set-port
tlm_b-put_export #(type T1 =inl, type T2=int) ovm_blocking-puUmp
tlm_b-pul_port #(type T-inl) ovm_block;ng-pul-port
virtual tlm_blocking.J!et_if #(type T-int) tlm_itbase
virtual tlm_blocking.J!et-peek_if #(type T=int) Ilm_if_base
virtual tim_blocking_master_if #(type REQ=int, Iype RSP-int) tlm_itbase
virtual Ihn_blocking-peekJf #(Iype T-int) tlm_if_base
virtual Ilm_blocking-put_if #(type T=inl) tlm_itbase
virtual tlm_blocking_slave_if #(type REQ-int, Iype RSP-int) tlm_itbase
virtual tlm_hlocking_slave_if #(type REQ=int, type RSP=inl) thn_it:"base
virtual tlm_blocking_lransport_if #(Iype REQ=int, Iype RSP=inl) tlm_if_b.se
tim_event
lim_fifo #(lype T-int) tlm_tifo_base
virtual tlm_fifo_base #(type T=inl) ovm_component
tlm_get_export #(type TI-int, Iype T2-int) oVIll_geUmp
virtual tlm_get_if #(type T-int) tim itbase
virtual tlm.J!et_peekjf #(Iype T=inl) tlmJt:"b.se
tim_gel_port #(Iype T-int) ovm_gel-porl
virtual tllll_iCbase #(Iype TI-int. type T2-int) ovm_report_object
virlual tlm_master_if #(type REQ=int, Iype RSP=int) tlmJtb.se
Ilm_nb_get_exporl #(type TI-int, 'ype T2=inl) ovm_nonblocking.J!el_imp
tlm_"bJlel-port #(type T-int) ovm_nonblocking.J!et-port
Ilm_nb-put_exporl #(Iype TI=int. type T2=inl) oVIll_nonblocking-pul_imp
Ilm_nb_puI_porl #(type T=inl) ovm_nonblocking-put-port
virtual 11m _nonblocking_geUf #(type T-inl) tlm_if_base
\'irtual 11111_"onblocking_get_peek_if #(type T-int) 'Im_itbase
virtual tlm_nonblockinLl11asler_if #(type REQ=int, Iype RSP=int) 'Im_itbas.
,·irtual Ilm_nonhlockinLpeek_if #(type T-inl) tlmjf_base
\'irtual 1111l_nonhlockinLPul_if _I,'Yl'e T-illl) 'IIIl_iCb.se
"inual llm_l1onb!0ddng_sla\'~_if "Ilype REQ=inl. Iype RSP=intl IlnUCbase
\'inual tll1l_nonbI0~king_ tran~r('trt _1 f =llype REQ=illl. 'yr< RSP=inl) IIIIl_iCbase

Table A.I: OVM Predefined Classes


482

Type Class Name Parameters Extends Class


vlltual tlm-"eek_if #(type T~mt) tlm_if_base
tlm"'put_export #(type TI-int, type T2=int) ovm put imp
virtual tlm-"uUf #(type T-int) tlm_if_base
tlm.Jlut-"ort #(type T=int) ovm.Jlut..JlOrt
tlm_reCLrsp channel #(type REQ-int, type RSP=int) ovm_component
tim_scenario _lifo #(type T=int) tim_fifo
tim_scenario JeCLrsp _channel #(type REQ-int, type RSP-int) tlm_reCLrsp_channel
virtual tllll_slavejf #(type REQ-int, type RSP=int) tlm_itbase
tim_transport_channel #(type REQ=int, type RSP=int) tlmJeCLrsp_channel
virtual t1m_transportjf #(type REQ=int, type RSP=int) tlm_itbase

Table A.I: DVM Predefined Classes


APPENDIXB System Verilog
Reserved Keywords

alias casex QQ event illegill bi!l§


always casez edge ~ inmllrt
alwa:,:~ comb cell else export incdir

alwa:,:s ff chandle end extends include

alwa:,:s la!~h class endcase extern initial


and clocking endclass fuW inout

assert cmos endclocking first match input

assign con fig endconfig for inside

assume const endfunction force instance


automatic constraint endgenerate foreach int

~ context endgrol!ll forever integer

begin ~ !;ndinterfac!: fork interfac!:


bind cover endmodule forkjoin intersect

bins covergTOup endpackag~ function join


binsof coverpoint endprimitive generate join an:,:

bit cross endllrQgram genvar join none

break deassign endllTOpert:,: highzO large

buf default endspecify highzl liblist


bufifO defparam endseguence if library

bulifl design endtable ill' local

~ disable endtask ifilone localparam


case dist enum ignore bins )Qgk

Underlined keywords are ne\\ to System\erilog and not a part of Veri log
484

longinl posedge release struet typedef

macromodule primitive repeat super union

matches priority return supplyO unique

medium P!M!:!!.!I!. mmos supply 1 unsigned

modport property rpmos table use

module protected rtran tagged var


I

nand pullO rtranifD task vectored

negedge pull 1 rtranifl this virtual

new pulldown scalared throughout void

nmos pull up sequence time wait

nor pulsestyle onevent shortint timeprecisi!lll wllit order

noshowcancelled pulsestyle_ ondetect shortreal timeunit wand

not ~ showcancelled tran weakO

notiill rand signed tranifD weakl

notin randc small tranifl while

null randcase solve tri wildcard

or randseguence specify triO wire

output remos specparam tri / with

package real static triand within

packed realtime string trior wor

I
parameter ref strongO trireg xnor

pmos reg strong / !)'pf xor

Underlined keywords are ne\\' to SystemVeri/og and not a part of Veri log
Index

Symbols === 83,244


- 83 -> 125,250
-- 83 > 83
/\ 83 >= 83
/\~ 83 -» 125
:: 108 » 83
:1 250 »> 83
:= 250 1 83
! 83,452 1=> 398
!= 83 1-> 398
!=? 83 II 83
!== 83,244 ~ 83
~/\ 83
.* 94
@ 125,127 ~& 83

* 83 ~I 83
** 83 $ 380,384
1 83 $assertkillO 422
& 83 $assertoffO 422
&& 83 $assertonO 422
## 121 $bitsO 157, 158
##0 382 $dimensionsO 77
##1 385 $dumpvarsO 421
#0 115 $error 418
#lstep 121 $errorO 421
% 83 $fatalO 421
+ 83 $fellO 377
++ 83 $finishO 421
< 83 $get_coverage 0 454
« 83 $highO 77
«< 83 $incrementO 77
<= 83 $infoO 421
<$noname 100 SleftO 77
= 83 S\oad_coverage_db () 454
== 83 $lowO 77
==? 83 $pastO 377
486

$randomO 264 Key Data Type 78


$rightO 77 lastO 79
$root 100 nextO 79
$roseO 372, 377 numO 79
$sampledO 377 prevO 79
$set_coverage_db_nameO 454 Reading Un initialized Key 79
$sizeO 77 Arrays, Dynamic 77
$stableO 377 Array Member Randomization 247
$urandomO 264 deleteO 77
$urandom_rangeO 264 new 77
$wamingO 421 Randomization 246
Resizing during Randomization 247
Numerics Rules of Access 78
Istep 117 Size Randomization 247
sizeO 77
A Arrays, Static 76
Abstract Data Models Access Mechanisms 76
Benefits 268 Specification Guidelines 76
Class-Based Implementation 268 Assertion Activation Types
Commands 267 Concurrent Assertions 418
Composite Data Objects 267 Immediate Assertions 417
Drawbacks of Using Derived Procedural Assertions 419
Classes 275 Assertion Assumptions
Drawbacks of Using struct 268 Over-Specified 415
Ethernet Example 270 Under-Specified 415
Fields and Constraints 269 Assertion Definition during Project Phases
Guidelines for Hiding Contents 278 Architectural Design 413
Hierarchical Models 273 Block Design 413
Model Subtypes 274 Clusters and Chip Design 414
Status 267 System Integration 415
Subtype Modeling Guidelines 275 Assertion Library 429
Transactions 280 Component 430
Use of Functions vs. Tasks 278 Use-Model 431
Views 277 Assertion Severity Levels 421
always Block 101 Assertion-Based Verification 21, 371, 411
always_comb Block 101 Acceleration and Emulation-Based
Analysis Ports. See Transaction Analysis Evaluation 23
Ports and Channels Assertion Placement 429
Array Types 75 Assumptions 415
Arrays Black-Box Assertions 24
Access Flexibility 76 Block-Level Assertions 412
Access Using Part-Select 76 Context Assumptions 21
Packed Dimension 76 Design and Implementation
Signed Packed Dimension 76 Decisions 22
Storage Efficiency 76 Enabling Technology 411
Unpacked Dimension 76 Evaluation Tools 22
Arrays, Associative 78 Formal-Based Evaluation 22
Assignment Rules 80 rps 429
deleteO 79 Main Elements 21
Element Data Type 78 Must-Verify Property Categories 21
existsO 79 Participants 412
firstO 79 Protocol Checker Architecture 432
487

Protocol Checkers 431 static 104


Roles 412 super 103, 105, 106
Simulation-Based Evaluation 22 virtual 106
System Level Assertions 412 class Declarations
Verification Related Assertions 413 Abstract 105, 106
Verification Related Assumptions 22 Accessing Parent Fields and
White-Box Assertions 24 Methods 105
Assertions, Capturing Base Class 103
Configuration Assertions 424 Data Hiding 110
Core Function 426 Derived 103
Design Outputs 426 Hierarchy 105
DUV Inputs 424 Inheritance 105
Interface Assertions 423 Multiple Inheritance 274
Partitioning the Problem 423 Object Pointers 103
Protocol 425 Out-of-Block Declarations 108
Register Interface 426 Parameterized Classes 108
Steps 422 Polymorphism 109
Verification Objectives 422 Scope Resolution Operator 108
Assignment Specialization 108
Blocking 114 Static Fields 104
Continuous 71, 101 Static Methods 104
Non-Blocking 114 Class-Based VE Implementation
Procedural 71 Agent 321
Statements 84 Analysis Ports 313
to Nets 71 Component Creation Steps 322
to Variables 71 Coverage Collection Activation 317
Associative Arrays. See Arrays, Deriving New Tests from Default
Associative Test 326
Automatic Variables 70 Drawback of Using Events 315
Driver 320
B Driver Main Loop 329
Black-Box Verification 8 Event-Based Synchronization 314
Drawbacks 9 Example Showing Concept 310
Blocking Assignments. See Assignments Interface Verification Component 322
Bus Driver. See Driver Monitor 316
Monitor Run Phase 328
C Physical Interfaces 312
Canonical Model 17 Scoreboard Implementation 331
Case Statements 89 Sequence Interfaces 313
default 89 Sequence Item Interfaces 313
Priority vs. Unique 89 Sequencer 318
Random Case Statement 90 Sequencer Implementation 330
casex 90 Steps for Building the Hierarchy 315
casez 90 Testbench 327
class Construct 102 Testcases 325
constraint_modeO 254.255,257 Transaction Interfaces 313
local 110 Clocking Block 118
newO 103 #0 121
postJandomize() 259.277 #lstep 121
pre_randomizeO 259,277.319 Benefits 11 7
protected 11 0 Clocked Signals 118
rand_modeO 254.255,258 Clocking Event 118
488

Cycle Delay Operator 121 default 445


Default Input/Output Skews 118, 121 Domain 440
fnput Sampling Clock 118 Guard Expression 440
Input/Output Skew Syntax 120 Ignored Bins 445
Name 118 Illegal Bins 445
Output Driving Clock 118 Properties 440
Skew Overrides JI8 Sampling Semantics 441
Specification Guidelines 120 Sized Bin Array 444
Supported Signal Directions 119 Source 440
Unclocked Signals 118 Transition Bin 441
Component Hierarchy Elements Unsized Bin Array 444
Behavior Configuration Field 163 Value Bin 441
Cloned Child Object 163 Value Bin Properties 443
Conditional Child Component 163 Value Bins 443
Fixed Child Component 163 Coverage Transition Bin
Hierarchy Configuration Field 163 443
Reference Child Component 163 Bin Guard Expression 448
Reference Child Object 163 Default Sequence 448
Regular Field 163 Domain Specification 448
Source Child Object 163 Properties 447
Component Hierarchy Guidelines Unsized Bin Array 448
Configurability 162 Coverage, Transition 436
Recursive Construction 162 Goto Repeat Operator 447
Self-Containment 162 Implementation 469
Concurrent Assertions Non-Consecutive Repeat Operator 447
assert 418 Transition 446
assume 419 Transition Length 446
cover 419 Transition Sequence 446
const 87 Transition Sequence Set 446
constraint Blocks. See Randomization Transition Set 446
Constraints Coverage-Driven Verification
Continuous Assignment. See Assignments Benefits 24
Coverage Analysis 471 Concepts and Approaches 24
Coverage Bins 437,443 Extension to Metric-Driven
Coverage Collection Verification 30
Class-Based 452 Lifecycle 29
Elements 436 Methodology 24
Flow 457 Overlap with ABV 21
Hits 437 Simulation Result Reusability 15
Port Level Attributes 54 covergroup Construct 438
System Level Information 61 at least 471
Tasks and Functions 456 get_coverageO 471
Coverage Design 460 geUnst_coverageO 471
Coverage Grading 470 Options 454
Coverage Groups 436 sampleO 439
Activation 439 startO 439
Properties 438 stopO 439
Restrictions with Class-Based 453 strobe 441
Coverage Hole 473 Cross Co\erage 436_ -l-l8
Coverage Point 436, 439 Bin Conjunction Operator 452
Bin Domain Specification 444 Bin Disj unction Operator 452
Bin Guard Expression 444 Bins 450
489

binsof 451 signed 73


Domain 449 unsigned 73
Guard Expression 449 User-Defined 68, 82
Properties 448 Design Abstraction Levels 5
Source 449 Design Flow 6, 15
Cross Coverage Implementation Engineers'Responsibilities 15
Hierarchical 468 Interaction with Verification Flow 15
Multi-Definitional 466 Tools Used 23
Multidimensional 464 Design, Distinction with Testbench 20
Multi-Instance 462 Digital System
Generalized Model 65
o Internal State 66
Data Model Fields Directed-Test Verification
Physical Fields 269 Progress Chart 26
Virtual Fields 269 Driver 25, 49
Data Model Randomization Constraints Features 49, 50
Abstract Ranges 278 Injecting and Detecting Errors 51
Coordinated Ranges 279 Procedural Interface 51
Default Ranges 279 Requirements 50
Data Model Views User Interface 51
Constraint View 277 Dynamic Arrays. See Arrays, Dynamic
Method View 277
Data Object Update Mechanism E
Constants 71 Enumerated Type 74
Nets 71 Description Elements 74
Variables 71 firstO 75
Data Objects Methods 75
Attributes 70 nameO 75
automatic Lifetime 70 nextO 75
Constants 70, 71 nlimO 75
Lifetime 70 previous() 75
Literals 70 Shorthand Notation 74
Nets 70,71 Specification Guidelines 74
Parameters 70 Events 125
Propeliies 70 Control 91
Resolution Function 71 null 127
Signed vs. Unsigned Constants 72 Persistent State 125
static Lifetime 70 Pointers 125
Type 70 Properties 125
Update Mechanism 70 Trigger Statements 91
Variables 70, 71 Expression Evaluation 114
Data Types 69 extem 105, 108
bit 73
byte 73 F
Composite 81 final Block 101
int 73 fork-join statement 101
integer 73 fork-join_ any statement 101
logic 72. 73 fork-join _none statement 101
longint 73 Formal Verification 17
New in SystemVerilog 71 Advantages 17
reg 72.73 Equi\'aJence Checking 17
shortint 73 Equivalence Checking Difiiculties 17
490

Techniques 17 Agent 48
Function Architecture 47
inout Argument Type 85 Bus Monitor 48
input Argument Type 85 Coverage Collector 54
output Argument Type 85 Driver 48
Prototype 107 Logical View 48
ref Argument Type 86 Monitor 48
Return Value 87 Reuse during System Level
Signature 107 Verification 61
Function Declaration Sequencer 48
ANSI Format 87
Verilog Format 87 J
Function Lifetime 86 Jump Statements 90
automatic 86
static 86 L
Functional Verification 8 local 110,278
Sources of Functional Errors 8 localparam 99
Functions 85 Loop Statements
break 90
G continue 90
Garbage Collection 104 disable 90
generate Statement 296 do-while 90
Gray-Box Verification 10 for 90
Shades of Gray 10 forever 90
jump 90
H repeat 90
Hardware Acceleration 20 return 90
Considering Requirements during Early while 90
Verification Phases 21
Idea behind Acceleration and M
Emulation 20 Mailbox 125, 130
Hardware Emulation 20 Features 130
Hardware-Software Co-Verification 55 getO 130
Hierarchy newO 130
Data Object 69 numO 130
Module 69 Parameterized 130
Procedural 69 peekO 130
putO 130
try_getO 130
if-else Statement 88 tryyeekO 130
priority 88 tryyutO 130
priority vs. unique 88 Method
unique 88 Prototype 107
Increment/Decrement Statements 84 Signature 107
initial Block 101 Metric-Driven Verification 30
Integral Variables 244 Modeling Language
interface Block 92, 95 Limitations of Veri log 66
contents 97 Required Elements 66
port list 98 mod port 99
Virtual Interface 312 module Block
Interface Drivers 25 DefaultClocking 121
Interface Verification Component 46 Implicit Port Connection 94
491

Implicitly Connected Ports 94 Simple Implementation Example 141


Named Port Connection 94 Object Instances 103
Port Connection Approaches 94 Object-Oriented Programming 101
Port Connection Rules 94 Benefits 101
Port Directions 93 Flow 102
Ports 93 Polymorphism 109
Positional Port Connection 94 Operators 83
module Block, Lifetime Add 83
automatic vs. static 93 Arithmetic Negation 83
Module Hierarchy 69 Bitwise AND 83
Improvement over Verilog 91 Bitwise NOT 83
Properties 92 Bitwise OR 83
Module Verification Component 46 Bitwise XNOR 83
Architecture 56 Bitwise XOR 83
End-to-End Scenarios 56 Blocking Assignment 83
Reuse during System Level Blocking Trigger Operator 125
Verification 61 Case Equality 83
Module-Based VE Implementation Case Inequality 83
Drivers 300 Conditional Select 83
Event Interfaces 294 Decrement 83,85
Illustration Example 284 Divide 83
Monitors 298 Equality 83
Overriding Default Sequencer Greater or Equal 83
Behavior 307 Greater than 83
Overriding Sequencer Template Increment 83, 85
Item 303 Inequality 83
Scoreboards 304 Less or Equal 83
Sequencer Activation 304 Less Than 83
Sequencer Item Generation Loop 302 Logical AND 83
Sequencer Item Generators 302 Logical NOT 83
Sequencer Structure 301 Logical OR 83
Sequencer Template Item 303 Modulo 83
Sequencers 301 Multiply 83
Test Component 306 Non-Blocking Assignment 83
Transaction Interfaces 292 Non-Blocking Trigger Operator 125
Monitor Non-Repeating Concatenation 83
Checks 53 Part-Select Fixed Part 83
Syntax checks 54 Part-Select Member 83
Timing Checks 54 Part-Select Variable Left 83
Mutual Exclusion. See Threads Part-Select Variable Right 83
Power 83
N Reduction AND 83
Names Reduction NAND 83
Hierarchical Names 100 Reduction NOR 83
Nested Identifiers 100 Reduction OR 83
new 130 Reduction XNOR 83
Non-Blocking Assignments. See Reduction XOR 83
Assignments Repeating Concatenation 83
null 102 Shin Signed Left 83
Shift Signed Right 83
o Shift Unsigned Left 83
Object Factory Shift Unsigned Right 83
492

Subtract 83 Override Specification Guidelines 144


Wild Equality 83 Overwriting Previous Overrides 144
Wild Inequality 83 Type Override 141
OVM Wild-Card Characters 144
Analysis FIFO Channel 232 OVM Global Methods
Analysis Interface 225 create_componentO 145
Class Inheritance Hierarchy 139 run_testO 174,176,178,327,331,347
Component Factory 145 set_ configjntO 165
Components 139, 140 set_config_ objectO 165
Core Utilities 137, 149 set_config_stringO 165
Data Objects 140 OVM Hierarchy Configuration
Example Using Object Factory 143 Specification Guidelines 165
Factory 137 OVM Request/Response Channel 233
Field Automation 147 OVM Simulation Control Methods 176
Field Automation Attributes 148 OVM Simulation Phases
Hierarchy Creation and Callback Methods 175
Management 137 check Phase 175
Object Factory 140, 142 elaborate Phase 175
Reporting Utilities 137 extract Phase 175
Reports and Messages 137 post_new Phase 175
Sequences of Transactions 137 pre_run Phase 175
Simulation Phases 137, 174 report Phase 175
Test Component 173 run Phase 175
Thread Synchronization 139 OVM TLM FIFO channel 231
TLM interfaces 136 OVM Transaction Channels 229
Transaction Interfaces 136 Active Channel 230
Transactions 136 Choice of Active VSo Passive
OVM Analysis Interface Channel 230
writeO 225 Passive Channels 230
OVM Bidirectional Connector Objects OVM Transport Connector Object 220
nb_transportO 220 OVM Unidirectional Connector Object
Supported Methods 219,220 Types 214
transportO 220 Supported Methods 214
OVM Bidirectional Interfaces 219 OVM Unidirectional Interface Methods
OVM Connector Object Kinds can_getO 213
Export Objects 210 can ~eekO 213
Imp Objects 210 can~utO 213
Port Objects 210 getO 213
OVM Connector Objects peekO 213
Linking 211 putO 213
OVM Core Utilities try_getO 213
Clone 149 tryj)eekO 213
Compare 151 tryj)utO 213
Copy 150 OVM Virtual Sequencer 202, 353
Packing 156 Downstream Sequencers 356
Print 153 Implementation Steps 353
Printer Knobs 154 Sequence Library 356
Unpacking 156 OVM Virtual Sequences 181
OVM Factory OVM ABSTRACT 149
Component Methods 145 ovm _active _passi\Oe _ enull1 321
Instance Override 141 ovm_agent 172. 321
Override Chaining 144 OVM_ALL_ON 149.151
493

ovm_analysis_export 225,334 OVM_DEFAULT 149


ovm_analysis_imp 225, 226, 232, 334 oym_ defaultJine-IJrinter 153
ovm_anaJysis_imp_declO 228,331 ovm_default-IJrinter 153
ovm_analysis-IJort 225,226 ovm _ default_table--'printer 153
ovm_BIDIR_TYPE_export 210 ovm_default_tree-IJrinter 153
ovm_BIDIR_TYPEjmp 210 ovm_doO 188,191,346
ovm_BIDIR_TYPE-IJort 210 ovm_do_seq_withO 357
OVM_BIN 149 ovm_do_withO 188,191,346,349,358
ovm_ blocking_getjmp_declO 228 ovm_driver 172,320
ovm_ blocking_get-IJeekjmp_declO 228 runO 320
ovm_blocking_masterjmp 222,223 seCLitemyrodjf 320,330
ovm_blockinLmasterjmp_declO 228 ovm env 172,173,174,323,325
ovm_blocking_master-IJort 220,221,222 ovm=event 314,315
ovm_blocking-IJeek_imp_declO 228 triggerO 3 15
ovm_ blocking-IJeek-IJort 354 ovm event callback 314
ovm_ blocking-IJutjmp_declO 228 ovm=exhaustive_sequence 192,345
ovm_blocking_slave_imp_declO 228 ovrnJactory 143, 168
ovm_blocking_slave-IJort 220 create_componentO 147
ovm_blocking_transportjmp_declO 228 create_objectO 143, 144
~YM_COMPARE 149 print_all_overridesO 144
ovm_comparer 152 print_all_overrridesO 143
ovm_component 145,164,174,219,223 seUnst_overrideO 143, 144
buildO 165,194,320,322 set_type_overrideO 143, 144
checkO 175 ovrn_field_aajnt_<key_type>O 148
create_componentO 145, 146 ovrn_field_aajnUtringO 148
create_objectO 145, 146 ovm_field_aa_objecUntO 148
enable_stopjnterrupt 177, 178 ovrn_field_aa_object_stringO 148
end_oCelaborationO 175 ovrn_field_aa_stringjntO 148
extract() 175 ovm_field_aa_string_stringO 148
get_current_global-IJhaseO 176 ovrn_field_array_intO 148
global_stopJequestO 176, 177 oVl11_field_array_objectO 148
newO 166 ovm_field_array_stringO 148
post_newO 175 ovm_field_enumO 148
report() 175 ovm field int 148
resolve_all_bindingsO 174,211,219, oVl11_field_intO 148
224 ovm_field_objectO 148
runO 178, 326 ovm_field_queue_intO 148
run testO 176 ovm_field_queue_objectO 148
setyonfigjntO 165, 167,322,326,347 ovm_.field_queue_stringO 148
set_config_objectO 165,167,168 ovm_field_sarraLintO 148
set_config_stringO 165,168,194,331, ovm_field_sarray_objectO 148
347 ovm_field_sarray_stringO 148
set_global_stop_timeoutO 176, 177 ovm _field _ stringO 148
set_global_timeoutO 176 OYM- FLAGS - OFF 149
seUnst_overrideO 145, 146 OYM- FLAGS - ON 149
set_type_overrideO 145, 146 o\,l11_getjmp_dec\O 228
stopO 175,177 o\'m_get_peek_imp 216
stopJequestO 176, 178 o\,111_get_peek_imp_declO 228
stop-requestO 176 o\,111_get-IJeekyort 217
ovm_component_utilsO 145, 146 OYM HEX 149
OVM COPY 149 o\'m_master_i111p_dec\O 228
OVM DEC 149 0\'111 monitor 172.317
494

buildO 318 use metadata 159


runO 318 OVM]ASIVE 326
OVM_NOCOMPARE 149 ovmyeekjmp_dec\O 228
OVM_NOCOPY 149 OVM]HYSICAL 149
OVM_NODEFPRINT 149 OVM]RINT 149
ovm_nonblocking_getjmp_declO 228 ovmyrinter 153
ovm_nonblocking~etyeek_imp_declO ovmyutjmp 217
228 ovmyutjmp_dec\O 228
ovm_ nonblocking_masterjmp_declO ovm yutyort 216, 217
228 ovmJandom_sequence 192,345,346
ovm_nonblockingyeek_imp_declO 228 OVM_READONLY 149
ovm_ nonblockingyutjmp_declO 228 OVM_REFERENCE 150,151
ovm_nonblocking_slave_imp_declO 228 ovmJecLJsp_sequence 345
ovm_non_ blocking_transport_imp_declO ovm scoreboard 172
228 ovm=se~consjf 203,204
OVM_NOPACK 149 connectjfO 204
OVM_NOPRINT 149 current~rabberO 204
OVM_NORADIX 149 get_sequencer_type_nameO 204
ovm_object 142, 147 grabO 204, 360
compareO 151, 152 is connectedO 204
copyO 150 is=grabbedO 204
do_compareO 151, 152 is_virtual_sequencerO 204
do_copyO 150,151 start_ sequenceO 204
doyackO 155,157,158 ungrabO 204, 360
doyrintO 153 ovm_se~item_cons_if 198,199
do_unpack() 155, 157, 158 ovm_se~itemyrodjf 198,199,354
packO 155, 158 connectjfO 199
printO 153, 155 get_nextjtemO 199,200,330
sprintO 153 has_do_availableO 199
unpackO 155,158 item_doneO 199, 330, 345
ovm_object_utilsO 143, 185,312 try_next_itemO 199
ovm_object_utils_begin 142 ovm_seqyrodjf 203,204
ovm_object_utils_beginO 148 ovm_sequence 188,189,191,346
ovm_object_utils_end 142, 148 bodyO 188,191, 197
ovm_object_wrapper 142 do_sequence_kindO 193, 195
~YM_OCT 149 getyarent_seqO 361
OVM]ACK 149 get_se~kindO 192,348
ovmyacker 156 get_sequenceO 192,348
getyacked_sizeO 156 is_blockedO 201
is_nullO 156, 159 iSJelevantO 202,365,366
pack_fieldO 156, 157 mid_do() 195,197
pack_tieldjntO 156,157 post_bodyO 197
pack_objectO 156,157 post_doO 195,197
pack_realO 156 pre_ bodyO 197
pack_stringO 156,157 pre_doO 195,197
pack_timeO 156 startO 193
unpack_fieidO 156 wait_forJelevantO 202,365,366
unpack_fieldjntO 156 ovm_sequencejtem 185.312,344
unpack_objectO 156, 158 get_sequencerO 319
unpackJealO 156 ovm_sequencer 172,186,192,318
unpack_stringO 156 count 345,347
unpack_timeO 156 current_grabberO 201
495

default_sequence 345,347 net and variable Assignment Rules


grabO 201 Affecting 94
is_grabbedO 201 Net Type Ports 95
maxJandom_count 345 Variable Type Ports 94
runO 347,348,366 postJandomizeO. See class
seq.)tem_conUf 319 preJandomizeO. See class
ungrabO 201 private 278
ovm_sequencer_base Procedural Blocks 84
count 192 Procedural Continuous Assignments 84
default_sequence 192, 194 assign 85
get_se<LkindO 192 deassign 85
get_sequenceO 192 force 85
max_random_count 192 release 85
sequences 192 Procedural Statements 84
start_sequenceO 193,195 Processes. See Threads
ovm_sequencer_ utilsO 186, 318 Product Design Flow 3
ovm_sequence_ utilsO 188, 189, 191, 319 program Block
OVM_SHALLOW 150,151 Benefits 123
ovm_simple_sequence 192,345 Calling Design Task and Functions 124
ovm_slave_imp_declO 228 Main Goal 123
OVM STRING 149 Specification Guidelines 123
ovm subscriber 332 Property 372
writeO 333 Base Property 390
ovm_test 172, 173, 174, 178,325 Base-Property Evaluation 391
OVM TESTNAME 176 Composite 392
ovm_threaded_component 145, 164, 174 Composite Property Evaluation 392
ki1l0 176 Difference with Sequences 373
pre_runO 175 Multi-Clock 401
runO 175 Named Property 390
OVM TIME 149 property Construct 390
ovm_transportjmp 222,223 Property Evaluation
ovm_transportjmp_declO 228 Combining Results from Multiple
ovm_transport-'port 221, 222 Evaluation Starts 394
OVM_UNDEF 298,306,345 Considerations 393
OVM UNSIGNED 149 Model 391
ovm_update_ sequece_lib 354 Start Points 393
ovm_update_sequence_1ib 203 Threads of Evaluation 393
ovm_update_sequence_lib_and_itemO 18 Property Implication Operator
6,319,354 Antecedent 398
186 Conditions Requiring Implication 397
ovm_virtual_sequencer 172,203,354 Consequent 398
add_se<Lcons_if() 204 Vacuous Success 398
default_sequence 359 Property Operators
get_se<Lcons_ifO 354 and 397
se<Lconsjf 204,354 Base Operators 400
Boolean Operators 396
p Derived Operators 40 I
parameter 99 if-else 398, 399
Allowed in 99 Implication Operators 396
Properties 100 Multi-Clock Merge Rules 403
Polymorphism 109 Non-Overlapping Implication 398, 399
Port Connection Rules not 397
496

or 397 Randomization
Overlapping Implication 398 Constrained Randomization Problem
Property Specification Statement 238
Checking 18 Early Randomization 196
Hierarchy 372 Effect of Variable Ordering 240
Language 19,371 Late Randomization 196
Required Facilities 372 Non-Ordered 241
Property, Named Variable Ordering 239
Disable Clause 391 Randomization Constraint Operators
Instantiation 391 Distribution Operator 250
public 278 Function Call 255
if-else Constraints 251
Q Implication Operator 250
qt.leue 80 inside Operator 249
delete 81 Iterative Operators 252
insertO 80 Variable Ordering Constraints 254
pop_ backO 81 Randomization Constraints 247
popJrontO 81 Bidirectional 240
push_ backO 81 Constraint Guards 255
pushJrontO 81 Equivalence Constraints 272
sizeO 80 foreach 252
Global Constraints 253
R Hierarchical Constraint Block 274
rand 244,245,254,257,258,278 State Variables 242
randc 244,245,254,257,258,278 Unidirectional 240
implicit ordering 245 randomizeO 244,247,254
Random Generation 26 ref 255
Coverage Collection 28
Finite-State Machine 27 S
Important Considerations 238 Scheduling Regions 114
Measuring Verification Progress 28 Active 115
Need for Automatic Result Checking 28 Inactive 115
Need for Directed Tests 29 NBA 115, 116
Practical Limits 27 Observed 115, 116
Requirements 27 Postponed 115, 117
Uniform Distribution 238 Preponed 115
Verification Completeness 14 Reactive 115, 116
Verification Progress Chart 26 Scope 70
Random Generation Engine Scoreboarding
Modes of Operation 241 Checks 60
Operation 241 Features 60
Quality Metrics 237 Reference Model 60
Random Stability 261 Semaphores 125, 127,294
Object Stability 263 Active Region 127
Thread Stability 261 getO 127
Random Variable newO 127
Disahling 258 Process Synchronization 127
Properties 246 putO 127
Reachable Set 237 try~etO 127
Random Variable Ordering Sequence 372
Assignment-Based Order 241 (For Scenario Generating Sequences, See
Constraint-Based Order 241 Transaction Sequences)
497

Benefits of Named Sequences 379 Simulation-Based Verification 19


Degeneracy 395 State-Space of the Design 20
Evaluation State 380 Visual Representation 20
Formal Arguments 380 Software Verification Components 46, 54
Instantiation 379 Specification-Driven Verification 30
Linear 378 specparam 99
Sequence Boolean Expressions 372,374 srandomO 264
Disallowed Data Types 374 static 10 1, 104
Disallowed Operands 375 Static Arrays. See Arrays, Static
Operand Sampling 375 Static Variables 70
Operand Types 374 struct 81
Operators 375 struct, Packed 81
Sampling Event 375 Subsequence. See Transaction Subsequence
Sampling Event Glitch 376 super. See class Construct
Semantic Restrictions in Using Function System Verification Component 46
Calls 375 Architecture 56
Sequence Evaluation Model Coverage Collection 61
Empty Match 395 Monitor 59
Match Condition 382 Scenario Generation and Driving 58
Multiple Matches 382 Scenario Types 56
Termination Condition 382 Scoreboarding 60
Sequence Item. See Transaction Sequence Sequencer 57
Item System Configuration 58
Sequence Operator Types System Initialization 57
Base 383 VE monitor 58
Composite 383 System Veri log
Sequence Operators Advancing Simulation Time 114
and 386 Enhancements over Verilog 67
Base operators 400 Programming Hierarchy 68
Consecutive Repeat 384 Programming Structure 67
Delay Operator 377 Scheduling Semantics 114
Delay Repeat Operator 383 Simulation Reference Model 114
Derived Operators 401 Verification-Related Enhancements 113
first match 389
Goto Repeat 385 T
intersect 387 Task
Non-Consecutive Repeat 385 Prototype 107
or 387 Signature 107
Range Delay Repeat 384 Tasks 87
Sequence Repeat 384 Temporal Logic 18
Simple Delay Repeat 384 Testbench
throughout 388 Distinction with Design 20
within 389 Thread
Sequence, Multi-Clock 401 Dynamic Processes 101
Concatenation Rule 402 Mutual Exclusion 127,294
Merge Rule 402 Random Stability 261
Repeat Rule 402 Rendezvous 128
Semantic Requirements 402 Restart Conditions 114
Sequencer. See Transaction Sequencer Start Condition 114
Sequences of Transactions. See Transaction Static Processes 101
Sequences Threads of Execution 114
Simulation Trace 20, 395 Time-Slot 114
498

tIm_analysis_fifo 232 Flat Sequences 181,187,343


tIm fifo 231 Grabber Sequences 200, 358
tlm=re~rsp_channel 233 Hierarchical Sequences 181, 189,348
Transaction Instance 187
Consumer 207 Layered Sequence Implementation
Control Flow 207 Steps 361
Data Flow 207 Layered Sequences 181, 361
Drivers 25 Named Sequences 379
Initiator 207 Reactive Sequence Implementation
Movement 208 Considerations 351
Producer 207 Reactive Sequences 181, 350
Target 207 Root Sequence 190,349
Transfer Request 207 Static Constraints 187
Transaction Analysis Ports and Type 187
Channels 313 Transaction Sequence Interface 202,203,
Transaction Channels 3 13 313
Transaction Interface 207, 313 Transaction Sequence Item 182
Analysis Interface 209 Declaration 182
Bidirectional 208, 209 Execution Flow 195
Channels 209 Instance 182
Master Connector Object 220 Transaction Sequence Item Execution
Slave Connector Object 220 Macros
Types 209 ovm_ createO 196, 197
Unidirectional 208, 209 ovm_doO 196
Transaction Interface Connector ovm_ do_ withO 196, 197
Object 207,208,209 ovrnJand_sendO 196,197
Bidirectional 210 ovmJand_send_withO 196, 197
connect() 211 ovm_sendO 196, 197
Kind 208 Transaction Sequence Item Interface 183,
Linking Rules 211 313
Type 208 Transaction Sequence Library 184, 189
Unidirectional 210 Adding Sequences 346
Transaction Interfaces Transaction Sequencer 52, 183
Analysis Interface 209 Arbitration Mechanism 199
Transaction Level Model Architecture 184
Architectural View 6 Implementation Steps 184
Cycle Callable 6 Pull Mode 183
Programmer's View 6 Push Mode 183
Programmer's View with Timing 6 Single-Port Scenarios 52
TLM 4 System-Level Scenarios 53
Transaction Sequence 187,377 Transaction Subsequence 190
Action Block 187,348 Execution Flow 196
Callback Method Execution Order 197 Transaction Subsequence Execution
Callback Methods 197 Macros
Composite Sequences 378 OVl11_createO 196
Conditional Sequence Implementation ovm_create_seqO 205
Steps 364 oV!11_doO 196
Conditional Sequences 364 oVI11_do_seqO 205
Configuration Fields 348 ovm_do_se~with() 205
Declaration 187 o\'m_do_ withO 196
Driver 183 ovmJand_sendO 196.205
Dynamic Constraints 187 ovmJand_send_ with() 196.205
499

ovm_send() 196,205 Use-Cases and System-Level


Transaction-Based Communication 207 Scenarios 34
Transaction-Driven Verification 25 Verification Planning
Benefits 25 Benefits of Upfront Planning 32
Transition Coverage. See Coverage, Challenges 32
Transition Considering Engineering Teams 33
typedef 82 Considering Tools and Technologies 33
Content 34
U Factors Affecting Form and Content 33
union 81 Factors to Consider 32
union, Packed 81 Forecasting and Tracking Resources 34
From Plan to Implementation 42
V Goals 34
Verification Challenges 11 Holistic Planning 31
Code Performance 12 Maximizing Verification Quality 34
Completeness 11 Measuring Progress 43
Efficiency 11 Mitigating Project Risks 34
Productivity 11 Project Actors 36
Reusability 11 Reacting to Results 43
Verification Closure 8 Verification Planning, Reacting to Results
Verification Component Milestone Charting 44
Active 47 Regression Ranking 44
Passive 47 Simulation Failure Analysis 44
Reuse 61 Verification Planning, Reacting to Results
Verification Environment Coverage Hole Analysis 44
Class-Based implementation 283 Verification Project Management 32
Layered Architecture 45,55 Verification Quality 12
Module-Based Implementation 283 Verification Scenarios 182
Verification Environment Reusability Multi-Sided 182
Across Design Generations 14 Single-Sided 182
Across Design Stages 14 virtual 107
Verification Metrics 12 Virtual Classes, See Abstract Classes
Completeness 14 Virtual Interface 312
Effectiveness 14 Virtual Methods 105
Granularity 12 Relation to Abstract Classes 107
Manual Effort 13
Simulation Result Reusability 15 W
Verification Environment Reusability 14 Wait Statements 91
Verification Plan Completeness 14 White-Box Verification 10
Verification Plan
Build and Run Infrastructure 40 X
Capturing Attributes 41 XBar Class-Based VE Implementation
Capturing Features 41 Default Sequence Item 343
Creation and Execution Flow Modifying Default Sequencer
Diagram 35 Behavior 347
Development Cycle 36 mxbar_sequencer 353,354
Flow for Identifying Content 38 mxbar_sve 355
Structure 38 Top Level Component 324
Views 39 xbar_frame 339
Verification Plan Contents xbar_ivc 323
Implementation-Specific Features 34 xbar_packet 312,319,339
Product Features 34 xbarJcv_agent 321
5110

xbar rcv driver 320


xbar rcv monitor 317
xbar_rcv-packet 319
xbarJCv_sequencer 318, 344
xbar_scoreboard 324,325,334
xbar_sve 325,326,355
xbar testbench 327
xbar_test_default 326
xbar_test_Iauncher 327
xbar_wrapper 327
xbarJmt_agent 322
xbar_xmt-packet 320,344
xbar_xmt_sequencer 344, 346
xmt_agent 323
XBar Class-Based VE Implementation
Default Sequencer Behavior 345
XBar Data Model
XBar Frame 337
XBar Packet 337
XBar Transfer 337
XBar Design
Communication Protocol 338
DUV Wrapper 289
Receive Port Protocol 287
Specification 286
Transmit Port Protocol 287
XBar Module-BasedVE Implementation
Architecture 288
Library Package 290
xbar- driver- if 294
xbar ivc env 297
xbar-packet 292
xbar-pkg 291
xbarJcv_agent 296
xbarJcv_driver 295,300
xbarJcv_monitor 296,299
xbarJCv_sequencer 296, 301
xbar scoreboard 304
xbar sve 297
xbar test 306
xbar testbench 298
xbar_xmt_agent 297
XBar Verification Plan 459

You might also like