SVM Manual
SVM Manual
SVM Manual
Legal Notice
Copyright 2004-2005 Verisity Design, Inc. All rights reserved. Verisity was acquired by
Cadence Design Systems, Inc. in 2005.
Trademarks
Verisity, the Verisity logo, eAnalyzer, eCelerator, eRM, Invisible Specman, LicenseE,
Pure IP, Specman, Specman Elite, SpeXsim, SpeXtreme, SureCov, SureLint, SureSolve,
sVM, Verification Advisor, Verification Alliance, Verification Vault, Verification
Viewport, Visualization Toolkit, vManager, vPlan, Xbench, Xchange, Xcite, XoC, Xpert,
Xsim, and Xtreme are either trademarks or registered trademarks of Verisity Design, Inc.
in the United States and/or other jurisdictions. All other trademarks are the exclusive
property of their respective owners.
Confidentiality Notice
Verisity confidential; do not distribute. The contents of this document constitute valuable
proprietary and confidential property of Verisity Design, Inc. No part of this information
product may be reproduced, transmitted, or translated in any form or by any means,
electronic, mechanical, manual, optical, or otherwise without prior written permission
from Verisity Design, Inc.
Information in this product is subject to change without notice and does not represent a
commitment on the part of Verisity. The information contained herein is the proprietary
and confidential information of Verisity or its licensors, and is supplied subject to, and may
be used only by Verisitys customers in accordance with, a written agreement between
Verisity and its customers. Except as may be explicitly set forth in such agreement,
Verisity does not make, and expressly disclaims, any representations or warranties as to the
completeness, accuracy, or usefulness of the information contained in this document.
Verisity does not warrant that use of such information will not infringe any third party
rights, nor does Verisity assume any liability for damages or costs of any kind that may
result from use of such information.
Contents
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-1
1.1
1.2
1.3
1.4
2.2
Preface
2.1.1
2.1.2
2.1.3
2.1.4
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-1
Why SoC Verification Is Different . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-1
What This Document Tries to Do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-2
What Is in This Document? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-2
What Is an SoC? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-2
2.1.4.1 Not Everything Is an SoC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-3
2.1.4.2 SoCs Are Similar to the Corresponding Board-Level Systems . . . . . 2-3
2.1.5
Our Example: The RSoC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-3
2.1.5.1 Some Typical Specifications for the Various Sub-DUTs . . . . . . . . . 2-5
The RSoC HW . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-6
2.2.1
The CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-7
2.2.2
The CPU Bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-7
2.2.3
Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-7
2.2.4
The Bridge to the System Bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-8
2.2.5
The Peripheral Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-8
iii
Contents
2.2.6
2.2.7
2.2.8
2.2.9
2.2.10
2.3
2.4
3.2
iv
Contents
3.3
3.4
3.5
3.6
3.7
3.8
3.2.4
Stand-In Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-12
3.2.5
Reference Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-14
Integrating eVCs into an SVE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-15
3.3.1
Connecting eVCs with Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-16
3.3.2
Recent Improvements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-17
Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-17
3.4.1
Configuration Struct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-19
3.4.2
Configuration with Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-19
3.4.3
Extending Subtypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-19
3.4.4
Defining Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-20
3.4.5
SVE Configuration File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-20
Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-21
3.5.1
Reusing Sequence Drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-21
3.5.2
Reusing Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-21
3.5.3
Sequences: Module to System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-22
3.5.4
Reuse of Register Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-22
3.5.5
Organizing and Using Sequence Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-23
Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-23
3.6.1
Module eVC Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-24
3.6.2
System-Level Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-24
3.6.3
Reusing Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-24
3.6.4
Adding Coverage Definitions to System eVCs . . . . . . . . . . . . . . . . . . . . . . . 3-25
Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-26
3.7.1
Connecting the Scoreboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-26
Scalability Issues in System Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-27
3.8.1
Stimulus Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-28
3.8.1.1 Sequence Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-28
3.8.1.2 Generation Tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-29
3.8.2
Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-32
3.8.2.1 Preparing the eVC for Scalability . . . . . . . . . . . . . . . . . . . . . . . . . . 3-32
3.8.2.2 Grouping Checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-32
3.8.2.3 Determining the Appropriate Tradeoff . . . . . . . . . . . . . . . . . . . . . . 3-33
3.8.3
Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-33
3.8.3.1 Grouping Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-33
3.8.3.2 Determining the Appropriate Tradeoff . . . . . . . . . . . . . . . . . . . . . . 3-34
3.8.3.3 Minimizing Overhead with the Coverage API . . . . . . . . . . . . . . . . 3-34
3.8.4
Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-34
3.8.5
eVC-specific Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-34
Contents
3.8.5.1
3.8.5.2
3.8.5.3
3.8.5.4
4.2
4.3
4.4
4.5
4.6
vi
Contents
4.6.2
4.6.3
4.7
4.8
Reset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-33
Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-33
4.6.3.1 Coverage of Register Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-33
4.6.3.2 Adding or Modifying Coverage Definitions . . . . . . . . . . . . . . . . . . 4-34
4.6.3.3 Customizing Coverage Sampling . . . . . . . . . . . . . . . . . . . . . . . . . . 4-35
4.6.4
Backdoor Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-36
4.6.4.1 Setting the HDL Path of a Register . . . . . . . . . . . . . . . . . . . . . . . . . 4-36
4.6.4.2 Backdoor Accessing of Registers . . . . . . . . . . . . . . . . . . . . . . . . . . 4-36
4.6.4.3 Monitoring Internal Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-38
4.6.4.4 Overriding the Default Backdoor Implementation . . . . . . . . . . . . . 4-38
4.6.4.5 Backdoor Accesses and Update of the Address Map . . . . . . . . . . . 4-39
4.6.4.6 Backdoor-Related Fields and Methods . . . . . . . . . . . . . . . . . . . . . . 4-39
4.6.5
Indirect Addressing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-40
4.6.5.1 Driving Indirect Registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-41
4.6.5.2 Identifying and Handling Indirect Access . . . . . . . . . . . . . . . . . . . . 4-43
4.6.6
Changing the Register Field Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-44
4.6.7
Customizing Addressing Width . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-45
4.6.8
Register Sequences and End of Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-46
4.6.9
Controlling Message Verbosity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-46
4.6.10 Accessing Multiple Address Maps on the Bus . . . . . . . . . . . . . . . . . . . . . . . . 4-47
4.6.11 Disabling Comparison for Registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-48
4.6.12 Register Field Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-49
Sparse Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-50
4.7.1
Instantiating a Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-51
4.7.2
Accessing Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-51
4.7.2.1 write_mem and read_mem Macros . . . . . . . . . . . . . . . . . . . . . . . . . 4-52
4.7.3
Updating the Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-53
4.7.4
Memory Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-53
4.7.4.1 Reading from and Writing to a File . . . . . . . . . . . . . . . . . . . . . . . . . 4-54
4.7.4.2 Controlling Returned Data of Uninitialized Addresses . . . . . . . . . . 4-54
4.7.4.3 Side Effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-55
4.7.4.4 Memory Backdoor Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-56
4.7.5
Storing Complex Structures in Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-57
4.7.5.1 Creating Memory Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-57
4.7.5.2 Connecting Memory Objects to the Memory . . . . . . . . . . . . . . . . . 4-58
4.7.6
Accessing Objects in Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-60
4.7.6.1 write_mem_obj and read_mem_obj Macros . . . . . . . . . . . . . . . . . . 4-60
4.7.6.2 Methods for Accessing Objects in Memory . . . . . . . . . . . . . . . . . . 4-61
Register and Memory Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-61
4.8.1
Type Naming Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-62
vii
Contents
4.8.2
4.8.3
4.8.4
4.8.5
4.9
viii
Contents
4.9.5
release_item() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-102
4.9.6
Memory-Management Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-102
4.10 Registers Visualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-104
4.10.1 All Address Maps Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-105
4.10.2 Top-Level Address Map Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-105
4.10.3 Detailed Address Map Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-106
4.10.4 Register Files Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-107
4.10.5 Address Sets Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-108
4.10.6 Register File Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-108
4.10.7 Stripe Chart Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-109
4.11 Register and Memory Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-110
4.11.1 show map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-111
4.11.2 trace ad alloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-111
5.4
5.5
5.6
5.7
5.8
5.9
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-1
TLM System eVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-2
TLM Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-3
5.3.1
TLM Wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-3
5.3.2
TLM BFM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-3
5.3.3
TLM Monitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-4
Interfacing with SystemC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-6
Configuration for Working with SystemC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-6
Modifying the SystemC Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-7
Open TLM Verification Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-9
5.7.1
TLM BFM Methodology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-9
5.7.2
TLM Monitor Methodology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-10
5.7.3
Multiple Instances of Input Method Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-13
5.7.4
Method Port Location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-14
5.7.5
PV Mode Synchronization Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-14
5.7.6
Multi-Level Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-15
5.7.6.1 Multiple Bus Monitors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-15
5.7.6.2 Independent Agents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-15
5.7.6.3 Bridge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-15
The XBus TLM Demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-16
The XCore TLM Demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-18
ix
Contents
Introduction
This chapter includes a general introduction to System Verification Methodology (sVM) and to this
manual. It contains the following sections:
1.1
This manual is all about verification for System-on-a-Chip (SoC). System Verification Methodology
(sVM) defines how to verify systems. System verification deals with many different aspects of
verification, such as:
Module-to-system integration
Register and memory modeling
Hardware-software co-verification
Transaction-level modeling (TLM) and verification
Post-silicon verification
This books intended audience includes eVC developers, verification environment developers, and
technical managers responsible for these environments.
sVM is currently a beta release. It describes the best-known practice for system verification based on
Specman Elite version 5.0 and eRM 2.1.
1-1
Introduction
About the sVM Release Library (svm_lib)
This chapter
Chapter 3 From Module Level to System Level How to construct a system verification
1.2
The sVM library (svm_lib) contains all of the deliverables associated with sVMinfrastructure
utilities, examples, documentation, and more.
The library includes the following packages and subdirectories:
svm_util
1-2
This package includes the infrastructure syntax, types, and utilities needed
for writing eVCs. It is the core module of the sVM Library.
Introduction
The sVM Library
Documents
(in the svm_docs
directory)
This directory contains all of the main sVM documentation. It also contains
sVM presentations in PDF format.
Golden eVCs
These are example eVC packages, used to demonstrate modularity and reuse
of verification environments. For more information, see:
Each of these packages and subdirectories contains its own PACKAGE_README.txt or README.txt
file, giving details on the content.
1.3
The svm_lib has several examples of DUTs, eVCs, and SVEs (Simulation and Verification
Environments). They demonstrate the development of verification environments from module to system
level. Each higher level eVC reuses the lower level eVCs. Each eVC has a sample SVE, demonstrating
how the eVC can be integrated into the system verification environment.
The sVM library contains:
Registers and
Memory package
GSA
XCore eVC
XSerial_TLM eVC Verification environment for a TLM serial interface. (This extends the XSerial
eVC in the eRM library.)
XBus_TLM eVC
Verification environment for a TLM bus. (This extends the XBus eVC in the
eRM library.)
QSoC eVC
Verification environment for a system built of two XCore modules on the same
XBus.
QSoC_SW eVC
1-3
Introduction
Terminology
QSoC_d eVC
Verification environment for a system built from a QSoC and a software driver
(driving its registers).
XBridge eVC
RSoC eVC
Note Some of the examples are not fully developed and documented.
See Also
XCore eVC User Guide (svm_lib/vr_xcore/docs/vr_xcore_evc.pdf)
QSoC eVC User Guide (svm_lib/vr_qsoc/docs/vr_qsoc_evc.pdf)
1.3.1
Terminology
Table 1-1
sVM Terminology
Term
Explanation
eVC
eRM
SVE
RSD
VSD
Interface eVC
Module eVC
An eVC for checking a module. Usually it is integrated with at least one interface
eVC, as all modules have at least one interface.
1-4
Introduction
Visual Conventions in This Manual
Table 1-1
Term
Explanation
System eVC
An eVC for checking a system, typically an SoC. It includes several module and
interface eVCs.
External eVC
Internal eVC
Either a module eVC, a System eVC, or an interface eVC that verifies an internal
component of the device.
1.4
This manual uses visual cues to help you locate and interpret information easily. These cues are
explained in Table 1-2.
Table 1-2
Document Conventions
Visual Cue
Represents
courier font
courier bold
bold
The bold font indicates Specman Elite keywords in descriptive text. For
example, the following sentence contains keywords for the show ini
command and the get_symbol() routine:
You can display these settings with the show ini setting command or
retrieve them within e code with the get_symbol() routine.
italic
The italic font represents user-defined variables that you must provide.
For example, the following line instructs you to type the write cover as
it appears, and then the actual name of a file:
write cover filename
1-5
Introduction
Visual Conventions in This Manual
Table 1-2
Visual Cue
Represents
[ ] square brackets
[ ] bold brackets
Bold square brackets are required. For example, in the following construct
you must type the bold square brackets as they appear:
extend enum-type-name: [name,]
construct,
cmd-prompt
Denotes the prompt for the Cadence tool you are running, including
Specman Elite, vManager, SpeXsim, or SpeXtreme.
C1>, C2>,
>
1-6
by Yoav Hollander
2.1
Preface
2.1.1
You are a Specman user, and you know how to write, say, a protocol eVC. Easy, right? Start with an env
unit, put in it a few agent units with each one having a BFM and a sequence driver, write some monitors,
checkers, and coverage, add a touch of cinnamon and you are done.
OK, it may be a little more involved than that, but you get my drift.
But an SoC is a horse of a different color. Look at a block diagram and you get a little dizzy. Where do
you start? And there is a CPU involved, so probably well need to go into HW/SW co-verification.
Looks complex.
Relax. This document will tell you the basics.
2-1
2.1.2
It will tell you what it means to verify such a creature: what is a typical test, what are the data structures
for generation, how to do checking, what is interesting to cover, etc.
It will discuss the relation to module and protocol eVCs. SoCs are often constructed from mostly
pre-verified modules. How do you write the eVCs for the modules, and how do you reuse them for
the whole? An interesting question, and it even has an answer.
2.1.3
2.1.4
What Is an SoC?
2-2
2.1.4.1
SoC has become a trendy term. Everyones products are suddenly especially designed for SoC. In
fact, 56% of all trade journal articles now start like this:
1.
2.
3.
So, buy our new color monitors: They are bigger and have more colors in themjust right for SoC
design!
With this comes a more tolerant definition of SoC, namely a chip with lots of things in it. We are
going to use a somewhat more restrictive definition of an SoC (see below).
2.1.4.2
While an SoC with n components is harder to verify than a board with the same n components, many of
the same lessons apply.
The main differences, by the way, are:
2.1.5
2-3
Figure 2-1
Point of Sale
Let us start with the big picture: The RSoC you are about to verify is at the heart of a supermarket point
of sale (Figure 2-1). You need to verify the full (HW+SW) RSoC, just to keep the customers smiling.
Figure 2-2
2-4
Full RSoC
Figure 2-2 is a block diagram of the full RSoC. The full DUT to be verified contains the hardware, the
low-level (software) device drivers, and the complex supermarket point-of-sale application program.
In addition to the full RSoC, we should verify at least two additional sub-DUTs:
After reset, the RSoC accesses the central supermarket database via Ethernet, reading in crucial
updates such as the ever-changing price of onion.
Then, as the checkout clerk processes the next person in line, the RSoC gets barcode readings and
credit-card swipe information through the USB. Through one serial interface, it gets key clicks from
the cash register. Through another serial interface, it communicates via Ethernet with the central
credit-card validation system and sends output to the screen.
Note Things like the barcode reader are outside of the DUT.
2.1.5.1
Lets consider some examples of what should be checked in the various sub-DUTs.
An RSoC_HW check Corrupt packets should cause an interrupt
Check that whenever the Ethernet controller receives a corrupt packet from the outside world, it
interrupts the CPU.
A typical input scenario for this level is:
1.
2.
2-5
2.
2.2
The RSoC HW
2-6
Figure 2-3
RSoC Hardware
It represents a fairly typical (though somewhat simple) SoC, with all of the right ingredients. See below
for some possible variants.
Notes
If you are already familiar with what an SoC looks like inside, you can just skim through the following
description.
Many of the following ingredients (for example, the CPU and some of the peripheral controllers) can
be pre-existing, pre-verified modules.
2.2.1
The CPU
At the top there is the CPU, controlling it all. We always try to put the CPU on top, so we can later draw
the SW above it. If you see this drawn differently on a whiteboard, try rotating the whiteboard.
Without loss of generality, we will assume that the CPU is an ARM.
2.2.2
The CPU sits on the CPU bus. In our example, this will be the AHB.
2.2.3
Memory
On the same bus there is a boot ROM (for instructions) and a RAM (for instructions and data). For
simplicity we show a single Memory block in the Figure 2-3.
2-7
2.2.4
On the fast CPU bus, there is also a bridge connecting the fast CPU bus to the (slower) system bus (also
called the peripheral bus). Whenever the CPU comes out with a read, say, whose address is within the
range allocated to the peripheral bus (say 0x8000 to 0xafff), then the bridge notices this and create a
corresponding read transaction on the peripheral bus. When that transaction is complete (that is, the
correct peripheral has returned the data), the bridge returns the data to the CPU via the CPU bus.
The system bus of the RSoC is the XBus, our faithful golden eVC bus.
The reason we have chosen this bus for the example is that it (and the XCore that sits on it, see below)
are well known to anyone who has studied eRM.
2.2.5
There are several peripheral controller modules (also called devices) on the peripheral bus. Each
peripheral controller communicates with the outside world in its own unique way.
Specifically, there are two XCore serial interfaces, a USB interface, and an Ethernet interface.
2.2.6
Each peripheral controller contains several registers, and each such register is mapped to some address
in CPUs address space. This is called memory-mapped IO.
For example, the XCore tx_mode register (which controls the transmission of the next serial frame)
might be mapped to address 0x8000, while the XCore rx_mode register is at address 0x8001.
With two XCore modules in this SoC, the registers of XCore0 may be mapped starting at 0x8000, and
the registers of XCore1 may be mapped starting at 0x8040. In general, the CPU controls the peripherals
by writing to their control registers and then reading from their status/data registers. A peripheral might
have just a few bits of control and status, or it might have hundreds of flags, modes and internal tables.
Thus, the memory map (as it is called) might look something like this:
[0x0..0xff]:
...
[0x8000..0x803f]:
[0x8040..0x807f]:
...
0x10000..0xffffffff]:
Interrupt vectors
XCore1 registers
XCore2 registers
External memory
We will soon see how to handle this using the register package.
2-8
2.2.7
The DMA
A DMA (Direct Memory Access) is a device that can move data from one place to another. Each place
is usually a sequence of memory locations, but it could also be some peripheral device.
The DMA itself is programmed by the CPU. It has registers signifying, for example, the from and to
addresses, the number of elements to move, the size of each element, etc.
Thus, if the CPU wants to read data from the serial line, it has two choices:
2.2.8
Variations on a Theme
There are many variations on this picture. For the purposes of this introduction, we can safely ignore
these variations. However, the sVM cookbook will discuss them at length.
Here are some of the variations:
The CPU itself could actually reside outside the chip. This is not all that different from when it resides
inside.
Other than the plain DMA, there could be real data-crunching modules inside the SoC, which, for
example, take data from one memory, compress it, and put it in another memory. For now, you can
think of these as just glorified DMAs.
Many SoCs have a DSP in addition to the CPU. They also can have several more processors.
Most SoCs today have more than two buses. Those buses might not even share the same address
space. (For example, address 0x8000 on one bus might address the XCore, but address 0x8000 on
another bus might address some memory.)
The whole SoC design could be configurable. In other words, you could be called upon to build a
verification environment that works regardless of whether the device has one XCore or two, a 16-bit
bus or a 32-bit bus, etc. The most general case is called a platform, that is, a basic design to which
platform users can add their own extensions.
2-9
2.2.9
Perhaps we should first ask: What is it that should be verified in the SoCs HW?
We could spend a lot of time verifying the various pieces such as the CPU. But it (and most other pieces)
are supposed to come pre-verified, and in fact often come with their own eVCs.
So, what does verification mean in an SoC? The most common answer is:
Verify that the programmers model of the SoC is correct.
The programmers model of the SoC describes how a (SW) programmer should use the SoC. It says
(either explicitly or implicitly) things like:
To activate the DMA, write the appropriate control bits to the 16-bit DMA_CTRL register at
address 0x9000. The START bit (bit 0) should be set to 1. If you want an interrupt when the DMA
is done, write a 1 to the INT_AT_END bit (bit 5).
2.2.10
To help us understand what needs to be verified in an SoC, this section discusses some typical SoC bugs:
2-10
Can I write to two subsequent 8-bit registers using one 16-bit write, or must I do two writes?
Is it legal to read the status register of a peripheral before it was initialized?
Is it eRM compliant?
Can you rerun the tests?
What is covered?
In our RSoC, even if the you have previously verified that the Ethernet controller can write to the
internal DRAM and the DMA can read from the DRAM, strange and interesting bugs can appear
when the two activities are happening concurrently.
If two modules are trying to interrupt the CPU at the same cycle, will this work? Will there be a
reliable way for the programmer to determine the two causes of the interrupt?
2.3
In this section we will first discuss how to verify the XCore and then how to verify the full hardware.
It is vastly oversimplified, concentrating on driving input.
2-11
2.3.1
Figure 2-4
As you can see in Figure 2-4, we have created an XCore eVC that uses the interface eVCs of the two
interfaces of the XCore (namely XBus and XSerial).
We are using the register/address space package to define the register block containing the four XCore
registers. We further use this package to hook this block up to the XBus address space, so we can
generate register values and then drive them through the XBus to the correct registers.
On top of that, we build a specific Program-the-XCore sequence, which (given some parameters)
programs the XCore to do reception or transmission according to the parameters. This
Program-the-XCore sequence writes to the XCore registers, much like a (SW) device driver does.
Finally, there is a multi-channel sequence driver, which will call Program-the-XCore, and at the same
time send serial frames to the XCore through its serial interface.
There are many other important details regarding checking, coverage, and debugging, but we will skip
them for now.
2.3.2
Figure 2-5 shows the environment for driving inputs into the full RSoC hardware.
2-12
Figure 2-5
We assume that all modules come with their interface eVCs, complete with items, sequence drivers,
BFMs, and monitors.
We further assume that each module that has registers and can be programmed (XCore, USB, Ethernet,
DMA) already comes with a highly-parameterized Program-the-module sequence that programs the
corresponding module via the bus.
OK, so we have the raw materials. What do we do next?
2.3.2.1
2.3.2.2
Much of what happens next depends on having a bus eVC with a sequence driver.
While there is no real software in this picture, we are in fact going to mimic what a normal software
program would do with a bunch of bus sequences.
2-13
The Dispatcher
The dispatcher runs in a loop, doing the following:
2.3.2.3
While all of this good stuff is happening on the bus side, we must do things on the interface side (serial,
Ethernet, etc.).
Figure 2-5 shows a multi-channel sequence coordinating the two sides, but we could also let the
2.3.3
Checking
2-14
2.3.4
Coverage
2-15
Data flow (for example, from Ethernet input via memory to XCore output)
Throughput/utilization
Coverage of shared resource access
System-level coverage:
2.4
We now turn our attention to the tricky job of verifying the chip with the software. There are two layers
to be verified here. Each has different needs:
Verifying the chip with the device drivers is best done against the RTL simulation. It is accurate,
available early, and fast enough.
Verifying the chip with the post-silicon requires much more speed. Therefore, we do it on the real
hardware.
Figure 2-6 describes the various trade-offs regarding simulation modes and their speed. For the RSoC,
we developed no high-level model. We verified everything but the application SW against the RTL.
2-16
Figure 2-6
2.4.1
Figure 2-7
Simulation Modes
2-17
In Figure 2-7, the bottom part has not changed. We still send data to the various interfaces. But the top
part is different now. Essentially, we need some dispatcher sequence that will generate randomly (but
with some logic):
The identity of the next device driver routine to call (for example, ened_send_buffer())
The values for the various parameters
But how do we actually call the chosen C/C++ routine? In the rest of this chapter, I will discuss a few
possibilities.
2.4.1.1
This is the best approach. It is just now becoming available. It is also the approach depicted in Figure
2-7.
Essentially, after somebody has written the correct ESI adaptor, you just:
1.
2.
Then, at runtime, the sequence just needs to call the method port with the chosen parameters.
Note Method ports may eventually encapsulate some of the other options (for example, CVL).
2.4.1.2
2.
3.
4.
Load the resulting binary code into memory, ready for the CPU to execute it.
This works, but pre-generation is a pain. You can no longer control the timing according to the DUTs
behavior. Therefore, there are other options.
On the other hand, a conservative (in other words, realistic) verification engineer will insist on running
at least some of the tests this way. That is because the other options employ various degrees of trickery,
and we must also test the real thing.
2-18
2.4.1.3
This is like the above, but the C main program acts as a slave, performing calls to routines according to
orders coming from the Specman sequence via some mailbox in memory.
You can encapsulate all of that via method ports, in which case the C main program and the mailbox are
created automatically.
2.4.1.4
Mentors Seamless is the leading HW/SW co-verification tool (and about the only one left). It has
facilities to load the SW, run and debug it, while communicating with the HDL simulator and keeping
the two synchronized. You can also tell it, say, that only memory accesses to memory-mapped devices
will activate the simulator (and thus access to program memory will happen in zero simulation time).
It has a library of tens of specific CPUs and DSPs. For each, you get an instruction set simulator, a BFM
corresponding to that CPUs bus interface, and a debug environment.
Seamless is integrated with Specman in the sense that they can attach to the same HDL simulator and in
the stronger sense that Specman can access the SW state much like it can access the HW state. This is
good for coverage and checking.
Here too you can go for any of the three options:
Pre-generation
Communication with the Specman sequence driver via a mailbox
Encapsulating the mailbox in a method port
2.4.1.5
CVL
CVL (Co-Verification-Link) is the Specman built-in tool for HW/SW co-verification. It is not as
powerful as Seamless, but it comes free with Specman and is probably easier to operate.
It is basically a remote procedure call interface between an external C program and Specman. The C
program can call the AHB sequence driver directly and send it an AHB burst.
Here too you can go for the same three options.
2.4.2
Finally, we are left with verifying the full HW with the application software.
This is even trickier to do, because it needs both speed and control. Figure 2-8 shows one way to do it.
2-19
Figure 2-8
Full Hardware
The actual RSoC (either in ASIC form or some FPGA prototype) sits on a prototype board.
2.
Specman generates interesting multi-channel transactions to the serial, USB, and Ethernet interfaces
(but not to the bus/software the software is now free-running).
3.
The transactions are fed into the actual interfaces in the prototype board via some hardware interface
or test equipment.
This last, innocent step is the most problematic, of course. It can be done in one of the following ways:
Via eCelerator
By having Specman program, say, some Ethernet/USB test equipment
By having Specman (running, say, on a Linux machine) program directly some interface cards for
the needed protocol, which are connected to the board
Notes
Sometimes, for speed reasons, Specman must pre-create all of the inputs and then start feeding stuff
via the hardware interface.
The verification environment modeled in Specman must be at the corresponding (high) level of
abstraction. For example, remember that a typical scenario at this level is:
<scan-carrots> <scan-milk> <total> <swipe-credit-card>
<receive-bad-credit-response> <receive-corrupted-packet> <total>
2-20
This chapter uses a small example to show the relationship between module and system eVCs and to
explain the methodology of constructing a system verification environment while reusing module-level
components.
The example DUT, QSoC, contains two instances of the XCore on a single XBus. Each XCore is
connected to an XSerial port. The system is activated by software running on an XBus master.
To demonstrate the module-to-system methodology, we use the XCore as the module level example with
a corresponding module eVC. The QSoC is a hardware subsystem that has a corresponding QSoC eVC.
The QSoC_D, containing the software with the hardware, is the example for the system.
Figure 3-1
QSoC_D DUT
SW_driver
QSoC
XCore0
XCore1
3-1
A system in one verification environment can become a subsystem in a larger environment. The terms
module eVC and system eVC are interchangeable. A module eVC is a system eVC for a single module.
This small example is actually part of a more complete system named the RSoC, which serves as the
demo testbench for sVM.
Figure 3-2
RSoC DUT
CPU
DMA
Memory
AHB
Bridge
XBus
XCore
XCore
USB
Ethernet
3-2
3.1
System eVCs
A system eVC is a reusable verification environment for a system. It contains module/subsystem eVCs,
interface eVCs, and some additional verification components for the system level.
An interface eVC (for example, AHB, PCI, or Ethernet) is a generic eVC for verifying the protocol on
the interface.
System eVCs
Interface eVCs
Monitor the items (collecting the items and interpreting the related events)
for checking and coverage
3.1.1
Verifying Systems
The first step in system verification is integrating all of the components into a system verification
environment. This means instantiating the module, subsystem, and interface eVCs and connecting them.
Each component must be configured to create a consistent model of the actual system DUT.
In the RSoC example, the components are:
3-3
3.1.2
The role of the system eVC is to encapsulate all knowledge about its components and add the specific
verification components needed to verify the system. The main objective is wide-ranging reuse:
Topology
Configuration of sub-eVCs
Building a system eVC makes use of the following elements provided in the components:
3-4
Interface eVCs
Traffic generation
Data items
BFM and sequence driver
Basic sequences
Checks
Protocol checks
Scoreboard
Coverage definitions
3.1.3
The system eVC is developed and maintained as an eRM package with some additional subdirectories.
The objective is to encapsulate in a reusable package all necessary code, examples, and documentation
for verification of the system.
In particular, the eVC contains examples of how to reuse this system as a component in a larger system.
These examples are contained in one or more SVE (Simulation and Verification Environment)
directories, the content of which is described in Module and System eVC Architecture on page 3-6. An
SVE example should be used as the basis for integration of the system eVC in a larger system.
3-5
3.2
Figure 3-3
XCore
XSerial
XSerial eVC
The architecture of module/system eVCs is different from interface eVCs. The example used here is that
of the XCore DUT, which has a bus interface connected to the XBus and a serial interface connected to
an XSerial port. The XCore transfers streams of bytes in either direction.
When designing the verification environment for this example, the first considerations that must be
addressed are:
3-6
3.2.1
Figure 3-4
Reg File
VSD
seq
RSD
seq
Monitor
Scoreboard
ACTIVE
Stand-In Model
VSD
Ref Model
seq
A monitor for collecting and interpreting events and data going to/from the DUT
Events, checks, and coverage
(Optional) A scoreboard and a reference model
Memory blocks and register files
A Virtual Sequence Driver (VSD) and a Register Sequence Driver (RSD) instantiated in the SVE or
a higher-level system eVC that uses the module eVC as a component
3-7
3.2.2
Figure 3-5
VSD
Reg File
Reg File
seq
Monitor
RSD
Scoreboard
ACTIVE
Stand-In Model
Monitor
ACTIVE
Stand-In Model
Scoreboard
VSD
seq
Ref Model
seq
VSD
Ref Model
seq
VSD
seq
System eVCs are similar to module eVCs. In general a system eVC includes:
A Virtual Sequence Driver (VSD) and a Register Sequence Driver (RSD) instantiated in the SVE or
a higher-level system eVC that uses the system eVC as a component
3.2.3
The monitor in a system or module eVC is a critical component in the verification environment. It
obtains and maintains events and data related to the activity in the module or system. This information
is made available to checkers, scoreboards, and coverage collectors.
3-8
The monitor collects most of the information from the relevant interface eVCs. Some additional
information relating to the internal state of the module or system might be derived either from the DUT
(whitebox access) or from the reference model.
The connection of the monitor to the corresponding interface eVC monitors is done using either pointers
or ports. Using pointers limits the ability to eCelerate the monitor. If the required information is
available from the interface eVC through ports, use them instead.
This section contains:
3.2.3.1
When using pointers, the following connections are needed for the module/system eVC to the interface
eVC:
3-9
Figure 3-6
XBus eVC
ACTIVE
Master
PASSIVE
Slave
SD
seq
Mon
BFM
...
Mon
Monitor
Stand-In Model
Scoreboard
VSD
Ref Model
XSerial eVC
seq
Agent
SD
seq
Mon
3.2.3.2
Mon
BFM
When using ports to connect the system/module eVC to the interface eVCs, the interface eVC monitors
must export through ports:
Event ports for protocol events (for example, start and end transaction events)
Simple ports for current data items (for example, packet, burst, etc.)
Method ports to pass data to the scoreboards
3-10
Figure 3-7
XBus eVC
ACTIVE
Master
PASSIVE
Slave
SD
seq
Mon
BFM
...
Mon
Monitor
Stand-In Model
Scoreboard
VSD
Ref Model
XSerial eVC
seq
Agent
SD
seq
Mon
3.2.3.3
Mon
BFM
This code example taken from the XCore eVC shows how the module eVC monitor uses information
collected from other monitors. The example assumes pointer connection between the eVCs.
unit vr_xcore_monitor_u {
-- Pointers to the interface eVC monitors
!xbus_agent_monitor : vr_xbus_agent_monitor_u;
!xserial_rx_monitor : vr_xserial_monitor_u;
!xserial_tx_monitor : vr_xserial_monitor_u;
-- Info from the XBus monitor:
!cur_xbus_transfer : MONITOR vr_xbus_trans_s;
-- An in method port for getting XBus transfers
System Verification Methodology
3-11
3.2.4
Stand-In Mode
Module eVCs should be designed to support stand-in (active) mode. In stand-in mode the system eVC
drives the simulation by playing the role of the DUT. A reference model is used to emulate the device
behavior.
3-12
Figure 3-8
XBridge SVE
AHB eVC
ACTIVE
Master
Addr Map
PASSIVE
Slave
Bus
Monitor
SD
seq
RSD
Mon
BFM
Mon
seq
DUT
Reg File
VSD
seq
AHB
ACTIVE
Monitor
XBridge
Scoreboard
XBus
Ref Model
Addr Map
RSD
XBus eVC
ACTIVE
Master
seq
PASSIVE
Master
Passive
Slave
Bus
Monitor
SD
seq
Mon
BFM
Mon
Mon
When the module eVC becomes active, its corresponding agents in the interface eVCs become active as
well. The sequence driver in the active part of the module eVC is layered on top of the sequence drivers
in the interface eVCs. The built-in driving capabilities of the interface eVCs are used by the module eVC
to drive the simulation.
3-13
Figure 3-9
XBridge SVE
AHB eVC
ACTIVE
Master
Addr Map
RSD
Mon
seq
ACTIVE
Slave
SD
SD
seq
seq
BFM
Mon
Bus
Monitor
BFM
DUT
Reg File
VSD
AHB
Monitor
seq
ACTIVE
Scoreboard
XBridge
Stand-In Model
XBus
VSD
Ref Model
Addr Map
XBus eVC
ACTIVE
Master
RSD
seq
Mon
3.2.5
seq
ACTIVE
Master
SD
SD
seq
seq
BFM
Mon
BFM
Passive
Slave
Bus
Monitor
Mon
Reference Model
3-14
Transaction-Level Functionality
Cycle-Accurate
3.3
When building a system-level eVC, the first question is where to instantiate each lower level eVC. If a
module eVC such as the XCore needs services from an interface eVC such as the XBus, it might make
sense to instantiate the interface eVC in the module eVC. However, in that case, multiple instances of
the module eVC would end up with multiple copies of the same interface eVC, which is undesirable.
So the rule is to instantiate each eVC on the level that encapsulates all of the eVCs that depend on it.
The system eVC instantiates the eVCs (module, subsystem, or interface) that are fully encapsulated
in the system. (For example, the QSoC eVC has two instances of XCore.)
The SVE instantiates the eVCs that are not fully encapsulated in the eVCs. (In the QSoC example,
the XSerial and XBus eVCs are instantiated in the SVE.)
All eVCs and the connections between them are created during generation. This section deals with the
best known practice of generating and connecting eVCs, considering current limitations and future
improvements:
3-15
3.3.1
This section describes the best known practice with current technology. This is a temporary solution. It
will change once additional capabilities are available.
To connect eVCs with pointers:
1.
In the system eVC, define the pointers from the system eVC to the interface eVCs.
2.
3.
In both the eVCs and the SVE, connect the pointers procedurally.
Step 1: Define the pointers from the system eVC to the interface eVCs
unit vr_qsoc_env_u like any_env {
-- A pointer to the XBus eVC
!xbus_evc : vr_xbus_env_u;
-- Point to the two XSerial interfaces
!xserial_0_evc : vr_xserial_env_u;
!xserial_1_evc : vr_xserial_env_u;
};
Each system eVC connection (for example, the QSoC connections to the XBus) has a corresponding
interface eVC agent. You might need additional pointers to the agents and their components (for
example, the monitor).
Avoid pointers in the opposite direction. The interface eVC should be independent of the system eVCs
associated with it.
Note These pointers can be used only during run time, not during generation itself.
3-16
3.3.2
Recent Improvements
In Specman Elite 5.x, three new phases are added after the unit tree is fully generated. These subphases
allow procedural connection of pointers and ports.
The subphases are implemented as hook methods of any_unit. They are activated in a depth-first order
from sys.
check_generation() is a subphase for user-defined sanity checks, including the connection checks
done in the previous phases.
3.4
Configuration
The eVC configuration is a reusable aspect that must be encapsulated with the eVC. When an eVC
becomes part of a system, some of its configuration is fixed according to the specific use in the system.
For example, when the system integrator assigns an XCore to be slave S0 on the XBus, that part of the
configuration should be packaged into the system eVC.
The interface eVC developer creates a general purpose verification environment for a given protocol.
The configuration of the eVC is left to the integrator.
3-17
The module eVC developer creates a special purpose verification environment for a specific module.
The module eVC uses interface eVCs and imposes certain constraints on their configuration.
The system eVC developer creates a verification environment for a system. The system eVC uses
module and interface eVCs and imposes further constraints on the configuration.
Finally, the system integrator creates the simulation and verification environment for a project. The SVE
uses system, module, and interface eVCs and sets the final configuration.
Who knows what?
The interface eVC developer knows how to configure the interface eVC.
The module eVC developer knows how to configure the module eVC and might know something
about configuring the interface eVC.
The module eVC user (developing a module DUT or developing a system eVC) knows something
about the module, but probably knows very little about the interfaces and the interface eVCs.
The challenge for the system eVC user is to configure correctly all of the sub-eVCs.
The challenge for the eVC developer is to pre-configure the eVC to ease the process of integration while
maintaining the necessary flexibility.
eVC Configuration includes:
3.4.1
Configuration Struct
Each level in the unit hierarchy can have a configuration struct. Configuration is always projected top
down, typically using constraints from the parent.
The configuration of a component depends only on the configuration of the parent environment.
If the configuration of siblings is mutually dependent, the dependency must be generated in the parent
environment and propagated down to the siblings.
3.4.2
Each eVC defines a default configuration for each sub-eVC, either using soft constraints or by providing
the constraints in a file that can be imported either by the SVE or by a higher-level eVC but not by the
eVC top file.
In the SVE config file, you can override the soft constraints when necessary or decide which of the
configuration files to import.
extend vr_qsoc_env_u {
keep soft xcore_evc0.sig_reset == "xbus_reset";
keep soft xcore_evc0.monitor.sig_base_addr == "base_addr";
keep soft xcore_evc0.hdl_path() == "xcore_a_inst";
keep soft xcore_evc1.sig_reset == "xbus_reset";
keep soft xcore_evc1.monitor.sig_base_addr == "base_addr";
keep soft xcore_evc1.hdl_path() == "xcore_b_inst";
}; // extend vr_qsoc_env_u
3.4.3
Extending Subtypes
Each agent and env unit has a name field that can be assigned in the eVC. Unique names are used to
constrain the individual units as follows:
extend VR_XCORE vr_xserial_env_u {
keep agent.direction == TX_AND_RX;
};
This unique naming serves one aspect only. Occasionally you might find other uses for this naming. For
example, the XCore is used to build the QSoC eVC. In that case:
3-19
You might want to use the name to identify the XSerial port of XCore.
You might also want to identify the XSerial port of QSoC.
Each unit has only one name field. Therefore, you must choose a consistent naming convention. Naming
should be deferred to the instantiation. In the following example, the two XSerial eVCs are named in the
SVE where they are instantiated, rather than in the QSoC eVC.
unit vr_qsoc_sve_u {
qsoc_evc: VR_QSOC_SVE vr_qsoc_env_u is instance;
xbus_evc: VR_QSOC_XBUS vr_xbus_env_u is instance;
xserial_0_evc: VR_QSOC_XSERIAL_0 vr_xserial_env_u is instance;
xserial_1_evc: VR_QSOC_XSERIAL_1 vr_xserial_env_u is instance;
};
3.4.4
Defining Types
It is best to define some operation modes using types, for example the bus width vr_ahb_data_width.
While the type is defined in the eVC, occasionally it must be overridden in the SVE.
The actual bus width is determined only when the bus is instantiated. To allow overriding from the SVE,
the definition of the bus width in the eVC is done using define under #ifndef. For example:
#ifndef VR_AHB_DATA_WIDTH {
define VR_AHB_DATA_WIDTH 32;
};
It can be overridden in the SVE configuration (before importing the eVC) as follows:
define VR_AHB_DATA_WIDTH 64;
3.4.5
The SVE has a configuration similar to the configuration of the eVCs. Its role is to instantiate all
top-level units and to configure them.
Defining the top-level verification components includes:
3-20
3.5
Sequences
Driving the verification environment is done using sequences. Reusing sequences from the various
components of the environment saves effort when building the verification environment.
This section contains:
3.5.1
A virtual sequence driver is always instantiated on the SVE level. Its primary role is to drive coordinated
scenarios involving multiple inputs to the system. These scenarios are built from reusable sequences
from the lower-level eVCs.
Virtual sequence drivers from the module level can be reused (instantiated) on the system level. These
sequence drivers are:
3.5.2
Reusing Sequences
Module-level sequences are not always reusable on the system level. In general, mixing random
sequences can break a design. For example, inputs that are accessible at module level might not be
accessible at system level.
3-21
3.5.3
To facilitate module-to-system reuse, each eVC must provide some basic sequences that are reusable on
a higher level.
Each interface eVC must define the basic READ/WRITE access to the DUT.
The module eVC must provide a sequence library with:
Basic READ/WRITE for each of the interfaces, using the interface eVCs and register sequences.
Basic module control sequences.
Basic virtual sequences (activating multiple interfaces), using its own basic READ/WRITE
sequences and the control sequences.
3.5.4
Parameters should be used whenever appropriate. Parameterized sequences are more reusable.
The register sequences in the module eVC access the registers in the XCore.
To reuse in a multi-XCore environment, you can constrain the static_item field of vr_ad_sequence.
The XCore sequence library constrains static_item to the register file of the XCore.
The QSoC sequence library constrains static_item to be one of the two register files.
To make the sequence reusable, have xcore as a parameter.
extend VR_QSOC_WRITE vr_ad_sequence {
xcore
: vr_qsoc_xcore_name;
!regs_sequence : vr_ad_sequence;
body() @driver.clock is only {
do VR_XCORE_XBUS_WRITE regs_sequence keeping {
.static_item == driver.addr_map.
reg_file_list[xcore.as_a(uint)];
};
};
};
3-22
3.5.5
In general, sequences should be maintained in separate libraries. Each library is a file containing several
sequences sharing some common feature. Each library is separately loadable so that only the appropriate
sequences are loaded during simulation. Examples of sequence libraries are:
3.6
Coverage
Coverage definitions can be reused from module to system, while adding system-specific coverage
definitions.
Interface eVCs collect coverage of traffic on the specific protocol they monitor. Module eVC coverage
is collected in its monitor, based on information from interface eVCs and from the DUT/reference
model.
System-level coverage is based on information in all module and interface eVCs in the system.
Coverage results can be crossed to show interactions and combined states.
This section contains:
3-23
3.6.1
The module and system eVCs provide coverage data in different categories such as:
3.6.2
System-Level Coverage
3.6.3
Reusing Coverage
To facilitate reuse of existing coverage groups defined in the interface eVC, some modifications might
be needed. You should:
Define meaningful ranges for the system (might be different from the original ranges).
Ignore irrelevant groups or items.
Control coverage collection using the has_coverage flag, assuming it was implemented in the
sub-eVC.
3-24
When the XCore is reused on the system level, its coverage definition (as well as the coverage of its
registers) must be modified as follows:
extend vr_ad_reg {
cover reg_access (kind == VR_XCORE_RX_MODE) is also {
item direction using also ignore = (direction == WRITE);
};
};
extend vr_xcore_monitor_u {
post_generate() is also {
covers.set_cover(
vr_xbus_agent_monitor_u.agent_trans_end(name == NO_AGENT),
FALSE);
};
};
By default there is no coverage collection on active slaves. However, if a module eVC operating in
stand-in mode needs the coverage information from the corresponding agent in the interface eVC, it can
turn coverage collection on. For example:
extend S3 vr_ahb_slave {
keep has_coverage == TRUE;
};
3.6.4
3-25
3.7
Checking
In the context of sVM, checking includes both protocol and data checking.
Protocol checks are typically handled by interface eVCs.
Data checks are typically done using scoreboards. If the interface eVC provides a scoreboard, it can be
reused on the system level, assuming that the data item transformation in the DUT is minimal (for
example, a bridge). If the transformation is substantial, a new scoreboard might be required. Instantiate
it either in the eVC or the SVE under a flag.
This section contains:
3.7.1
Connect to the scoreboard via its method ports. The basic scoreboard supports two methods:
add() Add to the scoreboard (only if the data item is expected to be processed by the DUT).
match() Perform the required transformation. Then call the scoreboard with the transformed item.
The basic scoreboard is only responsible for logging and matching the items. Any processing related to
the DUT behavior must be done by the eVC before sending the item to the scoreboard. To assist in this
task, the eVC can use a reference model that predicts the result of the operation.
For example, you could hook the XCore scoreboard to the XSerial agent is done as follows:
unit vr_xcore_monitor_u {
add_to_scbd : out method_port of vr_xserial_add is instance;
find_match_in_scbd : out method_port of vr_xserial_match is instance;
};
extend has_checks vr_xcore_monitor_u {
-- When writing TX frame that the XCore is expected to transmit -- put it on the xbus_to_xserial_scoreboard.
on tx_frame_written {
add_to_scbd$(tx_frame);
message(MEDIUM, "XBus wrote TX frame to XCore: ", tx_frame);
}; -- on tx_frame_written
3-26
-- When reading a frame from the XCore - find its match in the
-- xserial_to_xbus_scoreboard
on rx_frame_read {
find_match_in_scbd$(rx_frame);
message(MEDIUM, "XBus read RX frame from XCore: ", rx_frame);
}; -- on rx_frame_read
}; -- extend vr_xcore_monitor_u
Note Specman Elite 5.x supports multiple binding of method ports. Multiple system-level scoreboards
can be connected using in-method ports, without modifying the module or subsystem eVCs.
3.8
A system verification environment is assembled from numerous lower level verification environments.
In large systems the performance of the verification environment might become an issue. The first thing
to do in case of performance problems is to compile the verification environment and use the profiler to
look for portions of the code that use too much CPU time or memory space.
If, after compilation and profiling, performance is still unsatisfactory, there are some general guidelines
to improve the performance. These guidelines suggest trading off some features provided by the various
components of the verification environment for better performance. Every eVC contains features needed
for thorough verification of modules. However, there is a performance cost associated with each such
feature.
This section describes the main features to examine for improving performance. It also describes
solutions that some eVCs provide to achieve better performance by giving up some of the power and
flexibility of the eVC.
This section contains:
3-27
3.8.1
Stimulus Generation
Sequences are a key component of the eVC, but loading large numbers of sequences can have a
considerable performance penalty when large verification environments are created using many eVCs.
The most effective guidelines in this area are:
Use sequence libraries, and load only what is needed for the simulation.
Use generation judiciously.
3.8.1.1
Sequence Libraries
Sequences should be implemented in sequence libraries. Each library is a separate file containing one or
more sequences and optionally some related code. Sequence libraries should be small and modular, one
or a few sequences in each library. This enables careful selection of just the right sequences for each
simulation.
Some basic sequences are provided as part of the eVC. Additional libraries might be offered as
examples. Finally, most sequence libraries are developed and maintained by the end user during the
verification process.
Sequence libraries may reside in various places.
Basic Library
One or more basic sequence libraries are provided with each eVC. A basic library contains the most
common sequences used in almost every test, such as:
User-Defined Libraries
Additional sequence libraries are developed and maintained end users in their verification environment
under their /seq_lib directory.
3.8.1.2
Generation Tips
Random generation is a very powerful tool for productivity (reducing the manual work of configuring
the environment or DUT and creating stimulus) and improved quality (hunting for bugs in areas that are
hard to foresee). However, excess use of generation might result in scalability problems. When building
a large verification environment, consider carefully how to avoid unnecessarily complex generation that
might slow down the entire simulation.
The following tips show how to avoid some common pitfalls related to large verification environments.
The overall effect depends on the size and complexity of the DUT.
Avoid temporal expressions in sequence items
Defining events or temporal expressions in sequences and sequence items is a major obstacle for
scalability. These structs are never collected by the garbage collection system, causing gradual slow
down of the simulation. Instead of defining an event in a sequence, you can define it in the driver and
access it as driver.event from within the sequence.
If temporal expressions or events are used in sequence items, they should be terminated using
struct.quit() once the item is fully processed. When appropriate you can use the auto_quit() method of
a sequence or sequence item. This will automatically quit the struct when the sequence mechanism does
not need it any more.
extend any_sequence_item {
auto_quit() : bool is only {
return TRUE;
};
};
3-29
Note If a sequence item is used by the verification environment after item_done, then automatic
quitting is not applicable. The program must issue quit() when done.
Simplify complex sequences by limiting the capabilities
Sequences and sequence items might require complex generation. Performance can often be improved
by simplifying the generation, while giving up some of the randomization that might not be necessary on
the system level.
In the following example, the ONLY_OK sequence simplifies the generation of the slave response. In
the general case, the response is a random list of SPLIT/RETRY terminated by OK. The ONLY_OK
sequence always generates a single OK response. You can run many useful system tests using this
simplified sequence, assuming that the full capabilities of the slave response were already exercised on
the module level.
extend SIMPLE_RESPONSE vr_ahb_slave_seq {
!resp_seq: ONLY_OK vr_ahb_slave_driven_burst_response;
body() @driver.clock is {
do resp_seq;
};
};
A more efficient way of implementing the write() method is to create the burst (using new) and then
constrain the do action to use the pregenerated struct:
write(address: vr_ahb_address, data: list of vr_ahb_data, b_kind:
vr_ahb_burst_kind, size: vr_ahb_transfer_size) @driver.clock is {
var new_burst := new vr_ahb_master_driven_burst with {
.kind = b_kind;
3-30
.direction = WRITE;
.first_address = address;
.data = data;
.size = size;
.transmit_delay = 0;
.delay = 0;
};
for i from 0 to size - 1 {
var trans := new vr_ahb_master_driven_transfer with {
.size = new_burst.size;
.direction = new_burst.direction;
.kind = (i == 0 ? NONSEQ : SEQ);
};
new_burst.transfers.add(trans);
};
do burst keeping {it == new_burst};
};
The second solution is clearly more complicated. Only apply it in cases where the profiler indicates that
the simple implementation is too expensive.
Minimize the number of generation roles
Every do statement creates a new generation role. By minimizing the generation roles, you can make
code more efficient. You can do that by encapsulating do operations in for loops, methods, or sequences.
Assume a device with four ports and multiple registers controlling each port. The simple way to
initialize the device would be as follows (where write_reg is a macro hiding a do statement):
extend vr_ad_sequence_kind : [INIT_PORTS];
extend INIT_PORTS vr_ad_sequence {
!port_reg : PORT_REG vr_ad_reg;
body() @driver.clock is {
write_reg port_reg {.port_num == 1;
write_reg port_reg {.port_num == 2;
write_reg port_reg {.port_num == 3;
write_reg port_reg {.port_num == 4;
};
};
.enable
.enable
.enable
.enable
=
=
=
=
TRUE};
TRUE};
TRUE};
TRUE};
A more efficient way to write the above sequence is to use a for loop, having a single role instead of the
four roles in the previous implementation:
extend vr_ad_sequence_kind : [INIT_PORTS];
extend INIT_PORTS vr_ad_sequence {
!port_reg : PORT_REG vr_ad_reg;
body() @driver.clock is {
3-31
for i from 1 to 4 {
write_reg port_reg {.port_num == i; .enable = TRUE};
};
};
};
Note gen has a similar effect to do.This suggestion applies to gen as well.
3.8.2
Checking
Most eVCs have a large set of checks prepared for various verification tasks. Depending on the nature of
the DUT, some of the checks might be irrelevant for certain tasks. For example, when the DUT is a
slave, the master checks in the eVC are not useful.
3.8.2.1
Every eVC agent must have a has_checks field that is set by default to FALSE for active agents and
TRUE for passive agents. A corresponding has_checks field in the monitor inherits the value from the
agent.
has_checks: bool;
keep active_passive == ACTIVE => soft has_checks == FALSE;
keep active_passive == PASSIVE => soft has_checks == TRUE;
keep monitor.has_checks == read_only(has_checks);
All checks in an eVC should be implemented under the has_checks subtype of the monitor.
extend has_checks MASTER vr_axi_agent_monitor {
...
3.8.2.2
Grouping Checks
The eVC developer should identify the checks needed for each verification goal. If the checks can be
grouped in a logical way, each group should have its own has_*_checks flag so that each group can be
turned off separately. For example, you could implement signal checks in a separate group under
has_signal_checks. Other groups could include data checks, latency checks, power checks, and so on.
In such cases, all subgroups are soft constrained to the main has_checks flag.
keep soft has_signal_checks == read_only(has_checks);
Checks can also be turned off individually using the set checks command:
set check @module IGNORE
3-32
3.8.2.3
The final decision as to which checks are active in system verification must be left to the end user.
Within the same eVC, some agents might be internal to the system (and hence their protocol checking
was already verified in various subsystems) while other agents represent external devices (and hence
require full protocol checking even at the system level).
Module and system eVCs have their own checkers responsible for system-level checking, such as
connectivity and interoperability. Implement these checks under has_checks but keep them active
during system verification. They can be turned off later, for example, for HW/SW co-verification.
3.8.3
Coverage
Coverage is handled similar to checking. While it serves an important role during module verification,
some coverage may be traded off for performance during system-level verification.
All coverage definitions and related code should be implemented under a has_coverage subtype of the
monitor. The value is inherited from a similar has_coverage flag in the agent.
unit vr_axi_slave like vr_axi_agent {
has_coverage: bool;
keep active_passive == ACTIVE => soft has_coverage == FALSE;
keep active_passive == PASSIVE => soft has_coverage == TRUE;
keep monitor.has_coverage == read_only(has_coverage);
}
In addition, when checking is turned off, coverage should be turned off as well. It makes no sense to
collect coverage when checking is off.
3.8.3.1
Grouping Coverage
As in checking, coverage definitions can also be structured in logical groups, under various
has_*_coverage flags, like has_whitebox_coverage or has_error_coverage. Error coverage and
whitebox coverage are important at the module level. On the other hand, for system-level verification,
they could create holes in the coverage. There might be no simple way to create all error cases or
activate all internal states in a specific system. Therefore, this kind of coverage should be implemented
under special subtypes so that it can be disabled at the system level.
All subgroups should be soft-constrained to the main has_checks flag. For example:
keep soft has_whitebox_coverage == read_only(has_coverage);
As a general rule, coverage subgroups should match the checking subgroups. Whenever a checking
subgroup is turned off, the corresponding coverage subgroup should be turned off as well.
keep soft has_whitebox_coverage == has_whitebox_checks;
System Verification Methodology
3-33
3.8.3.2
System verification has its own specific coverage goals implemented in the SVE and various subsystem
eVCs. These include connectivity and interoperability, which must be active during system verification,
but can be turned off, for example, for HW/SW co-verification.
3.8.3.3
The coverage API contains options to turn off coverage groups that are not appropriate at the system
level. For example:
cover cover_group using also no_collect = TRUE;
3.8.4
Messages
eVCs typically produce a lot of useful information for module-level verification. Some of that
information might be redundant in system verification.
Messages and message loggers that are not activated require minimal or no resources. eVC developers
should leave all message loggers in their default state to avoid an unnecessary performance penalty.
Loggers and messages in a large SoC environment should be activated only for debugging purposes.
Even then, they should be activated selectively, focusing on the area of interest.
The message facility in eRM lets you improve performance by turning off unnecessary messages. You
can lower message verbosity either globally (by controlling all messages from the system logger) or
selectively (by controlling the loggers in the env unit of each eVC). In addition, the message facility
provides more selective control over messages, based on the verification environment architecture,
message tags, and so on.
3.8.5
eVC-specific Solutions
eVC performance can typically be improved by profiling it for a specific DUT and tweaking the eVC
capabilities. Often, there is a tradeoff between high performance versus full flexibility and
debuggability. Performance can be improved by giving up some of the capabilities. This section
describes some performance improvements that eVC developers might provide.
3-34
3.8.5.1
The BFM and monitor typically access the signals with a high frequency, causing a large number of
context switches between Specman and the simulator. This can be significant in serial protocols, where
context switching is at the bit level rather than the packet level.
Implementing a small layer of the BFM and monitor in HDL can significantly decrease context
switching and thus improve performance. eVCs providing such an HDL layer as an optional component
let users choose between better performance and better visibility.
3.8.5.2
Sharing Monitors
In some environments, performance can be improved by having multiple components share a monitor.
In particular, in point-to-point interfaces, both agents representing an endpoint can use the same monitor.
In addition, when there are multiple agents on a bus, you can use a single bus monitor for all of the bus
agents rather than a separate monitor for each bus agent.
3.8.5.3
In some simple cases, the eVC developer can provide a deterministic alternative to the general-purpose,
sequence-based solution. A typical example would be a preprogrammed arbiter that performs a
predefined arbitration algorithm. Such an arbiter can be very efficient, having no need for sequence
generation. The cost is a loss of control by the sequence mechanism.
3.8.5.4
Typically, the clock in virtual sequence drivers is tied to sys.any. In some cases, this can cause an
unnecessary performance loss. In such cases, the clock can be emitted only when needed, thus
improving the performance of the driver.
In the following layering example, a low-level sequence obtains the next item from a higher-level driver.
Instead of running the clock of the high-level driver on sys.any, the low-level sequence emits the clock
in pre_do().
pre_do(is_item:bool)@sys.any is {
emit driver.upper_driver.clock; // Emit the clock for the
// high-level driver
upper_data = driver.upper_driver.get_next_item().data;
emit driver.upper_driver.item_done;
};
3-35
3-36
4.1
The register and memory package (vr_ad) models the behavior of memory and registers. It contains
some built-in mechanisms with predefined types for efficient modeling.
The package addresses three independent aspects: address management, register modeling, and memory
modeling.
This section contains:
4-1
4.1.1
Address management
4.1.2
Naming Conventions
Registers are entities in the global name space. When a system is created out of multiple subsystems,
register names might collide. Therefore, Cadence recommends using a unique prefix for all addressable
items names, following the unique naming conventions of eRM.
4.1.3
Compilation
The register and memory package (vr_ad) includes some macro definitions. Therefore, if you want to
compile the package with user code that relies on the package, you must compile the package first and
then compile the user code on top of the package.
4.2
This section presents an overview of the register and memory package components and the integration
of the package with an existing environment. For this purpose, we use the simple ex_c_bus environment
(see Figure 4-1). More complex examples can be found in the remaining sections. The following
components are introduced in addition to the basic eRM elements (env, agents, sequence drivers):
4-2
Register File Represents DUT agent registers. It contains a list of consecutive registers.
Address Map Represents the address space. It maps the register files and the memory blocks (if
any) in the address space, and it contains references to them. In a very simple environment with only
one register file, an address map may seem redundant. Address maps gain importance in environments
with multiple register files.
Register Sequence Driver (RSD) A dedicated sequence driver for register operations. The
functionality of the RSD resembles that of a virtual sequence driver.
Figure 4-1
ex_c_bus env
Register
File
RSD
Address
Map
Active Master
Passive Slave
SD
Mon
BFM
Monitor
4-3
Figure 4-2 shows the connection between the various elements of the register and memory model.
Figure 4-2
Memory
Address Map
Address Map
Register File
Register
Memory
block
Register
Static
information
For a detailed description of the data structures, see Register and Memory Data Structures on page
4-61.
To create and integrate the register model into your ex_c_bus environment:
1.
For details on defining a register file, see Defining a Register File on page 4-9.
2.
For details on defining registers, see Defining Registers with the reg_def Macro on page 4-9.
4-4
3.
For details on where and how to instantiate the register file, see Instantiating the Register File in the
Architecture on page 4-15.
4.
For details on instantiating the address map and the RSD, see Instantiating the Address Map and the
RSD on page 4-15.
5.
Allocate an address space for the register file by adding it to the address map at the desired offset.
For example, you could allocate addresses starting from 0x0 as follows:
extend ex_c_bus_env {
post_generate() is also {
addr_map.add_with_offset(0,reg_file);
};
};
For details on adding the register file to the address map, see Adding the Register File to the
Address Map on page 4-15.
6.
Implement the eVC BFM sequence drivers vr_ad_execute_op() method, which takes the contents
of the register operation and executes it.
This is necessary for layering the register sequences on top of the eVC. For example, your
implementation of vr_ad_execute_op might use the read() and write() sequence methods as
follows:
extend ex_c_bus_driver {
4-5
For details on integrating the register sequence driver with an eVC agent, see Integrating the RSD
with the BFM Sequence Driver on page 4-18.
7.
Bind the eVC monitor to the address map for updating the register model and collecting coverage.
For example, you could use the transfer_end event detected by the monitor. The update() method is
called for write operations. The compare_and_update() method is called for read operations.
extend vr_xbus_bus_monitor_u {
on transfer_end {
if transfer.read_write == WRITE {
get_enclosing_unit(vr_xbus_env_u).addr_map.update(
transfer.addr,pack(packing.low,transfer.data),{});
} else {
compute get_enclosing_unit(vr_xbus_env_u).\
addr_map.compare_and_update(transfer.addr,
pack(packing.low,transfer.data));
};
};
};
For details on updating the registers from a monitor, see Updating the Register Model Using an eVC
Monitor on page 4-20.
You can now create register sequences and access the registers using the write_reg and read_reg
macros. For example:
extend vr_ad_sequence_kind: [MY_SEQ];
extend MY_SEQ vr_ad_sequence {
!tx_data : EX_REG_TX_DATA vr_ad_reg;
body() @driver.clock is only {
write_reg tx_data;
read_reg tx_data;
};
};
For details on accessing the registers, see write_reg and read_reg Macros on page 4-24.
4-6
4.3
The next two sections use the Cadence XCore environment (vr_xcore) to demonstrate an integration of
the register model. The environment contains two instances of XCore, controlled from an XBus master
by writing to the XCore registers. As a result of the register operations, the XCore performs read or write
transactions on the XSerial port. Figure 4-3 shows the register and address management aspect layered
on top of the architecture, including the address map, register file, and register sequence driver.
Figure 4-3
Typically each XCore is represented by a module eVC. The best place to instantiate the register file
is in the module eVC. If your environment does not have a module eVC, you can instantiate the
register file in the XBus env or in the PASSIVE slave agent.
The XBus passive slave has a reference to the register file instantiated in the module eVC.
The XBus env has the address map and the register sequence driver (RSD).
The RSD is layered on top of the BFM sequence driver.
To create the register model:
1.
2.
Define the register types and optionally add instances of those registers into a register file (using the
reg_def macro).
4-7
3.
Instantiate the register file in the architecture (for example, in an agent) with no absolute address
assigned yet.
4.
Instantiate the address map and the register sequence driver in the environment.
5.
Add the register file to the address map (setting the absolute base address for the register file). This
can be done at runtime or at post-generate.
4.3.1
The address and data fields are unsigned integers with default size defined as follows:
define
define
VR_AD_ADDRESS_WIDTH
VR_AD_DATA_WIDTH
32;
32;
If your address or data width exceeds these default settings, you must change them to the highest value
to be used in your verification environment. As these statements define the actual types used in the
model, they must be set before the vr_ad package is loaded. For example, if your largest registers are 64
bits wide, you must define VR_AD_DATA_WIDTH to be 64, and your configuration file must start
with the following lines:
define VR_AD_DATA_WIDTH
import vr_ad/e/vr_ad_top;
64;
If your address space is a 64 bits wide, you must define VR_AD_ADDR_WIDTH to be 64, and your
configuration file must start with:
define VR_AD_ADDRESS_WIDTH
import vr_ad/e/vr_ad_top;
4-8
64;
4.3.2
Define the new kind of register file with its size, and reset all registers.
For example:
extend vr_ad_reg_file_kind : [XCORE];
extend XCORE vr_ad_reg_file {
keep size == 256;
post_generate() is also {
reset();
};
};
The size is in terms of how many addresses it occupies (and not the number of registers it contains). This
register file occupies 256 consecutive address entries when inserted into the memory map. By default
each address entry is a byte size, but you can change that. (See Adding the Register File to the Address
Map on page 4-15 and Appendix 4 The Register and Memory Model.)
4.3.3
The reg_def macro is provided for easy definition and instantiation of the registers. The macro defines
the register type and optionally instantiates it under a register file. If the register has a single instance in
the verification environment, provide the register file name and offset for this instance. If multiple
instances will be created, omit the register file name and offset and create the instances as described in
Defining Multiple Instances of a Register Type on page 4-12.
This section contains:
4-9
4.3.3.1
reg_def Macro
Purpose
Define a register type with the write mask and reset value defined for each register field.
Syntax
reg_def reg-name [register-file-name offset] {
reg_fld field-name : field-type : field-mask : field-reset-value [: cov];
};
Syntax Example
reg_def EX_REGS_TX_MODE XCORE 0x100 {
reg_fld dest
: uint(bits:2)
: RW
reg_fld frame_kind : [DATA,MESSAGE,A,B](bits:2) : RW
};
:
:
0x0 : cov;
DATA : cov;
Parameters
reg_name
Register nameMust follow eRM naming conventions and use a company prefix
register-file-name
offset
field-name
field-type
field-mask
field-reset-value
cov
Notes
The keyword reg_fld automatically defines the register field as a physical field (%).
The reset value must be a legal value for the type of the declared field. For example, if the field is
defined as Boolean, the reset value must be either TRUE or FALSE. Fields that are structs or lists
should get a number for the entire field.
4-10
You can specify only the field name and type. In that case, the default mask is RW, and the reset value
is 0.
4.3.3.2
4-11
The default field order of a register is from high bit position to low bit position. For example, the field
order of the EX_REGS_TX_MODE register is:
extend EX_REGS_TX_MODE vr_ad_reg {
%dest
: uint(bits:2);
%frame_kind : [DATA,MESSAGE,A,B](bits:2);
%resv
: uint(bits:4);
};
// Bits [7:6]
// Bits [5:4]
// Bits [3:0]
For instructions on changing the default field order, see Changing the Register Field Order on page
4-44.
Note The name of the register that is automatically added to the register file is the register kind in lower
case. So, with the above example, you can directly access the register xcore_reg_file.ex_regs_tx_mode.
See Also
Fields and Methods of vr_ad_reg on page 4-75
4.3.3.3
Create the register type using the reg_def macro, but do not specify a register file name or offset.
For example:
//
NAME
reg_def EX_REGS_PORT_CONTROL {
reg_fld control
: port_control_kind_t;
};
2.
Extend the register file to add instances of the register type using the add_with_offset() method.
For example:
extend vr_ad_reg_file_kind : [PORTS];
extend PORTS vr_ad_reg_file {
port_regs[4] : list of EX_REGS_PORT_CONTROL vr_ad_reg;
post_generate() is also {
for each (p) in port_regs {
add_with_offset(index*4, p);
};
};
};
Note You can also use the reg_list macro here (see reg_list Macro on page 4-13).
4-12
4.3.3.4
reg_list Macro
Purpose
Instantiate a register list inside a register file, and automatically call add_with_offset() for each register
in the register list.
Syntax
reg_list list-name[list-size] of reg-kind at offset;
Syntax Example
extend EX_FILE vr_ad_reg_file {
reg_list regs[128] of EX_REG at 0x1000;
};
Parameters
list-name
list-size
reg-kind
offset
Notes
The registers are not generated but rather created by a new action (for performance reasons).
Cadence recommends using this macro when you must generate a large list of registers.
4.3.3.5
Mirroring Registers
Occasionally, a register is mapped in multiple locations in the register file. In such cases, the register can
be accessed via multiple addresses.
To implement mirroring of registers:
4-13
//
NAME
FILE
OFFSET
reg_def EX_REGS_TX_MODE XCORE 8h03 {
// Custom Fields
reg_fld dest
: uint(bits:2)
: RW : 0 : cov;
reg_fld frame_kind : [DATA,MESSAGE,A,B](bits:2) : RW : A : cov;
reg_fld resv
: uint(bits:4)
: RW : 0 ;
};
// Add Mirror address
extend XCORE vr_ad_reg_file {
post_generate() is also {
add_with_offset(0x10,ex_regs_tx_mode);
// Note: ex_regs_tx_mode is automatically defined by the
// "reg_def" macro as a field of the XCORE register file.
};
};
See Also
add_with_offset() on page 4-79
4.3.3.6
Coverage per register field can be defined automatically by adding the attribute cov to the reg_fld
definition in the reg_def macro. This adds a coverage item of the field to the reg_access coverage
group.
Example
The following code would be automatically created by the register definition in the example found in
Mirroring Registers on page 4-13.
extend EX_REGS_TX_MODE vr_ad_reg {
cover reg_access(kind == EX_REGS_TX_MODE) is also {
item ex_regs_tx_mode_dest;
item ex_regs_tx_mode_frame_kind;
};
};
See Also
Coverage on page 4-33
4-14
4.3.4
The register file represents the DUT agent registers. Instantiate it in the module eVC or in the passive
agent of the interface eVC.
To instantiate the register file in a passive agent:
4.3.5
The address map represents the entire address space. It contains pointers to all register files in the
address space. It is used as a reference model. Instantiate it in the env corresponding to the address
space.
If you want to use register sequences, an RSD must also be instantiated at the same place and connected
to the address map. The RSD gets the information it requires for accessing registers from the address
map. The RSD should also be connected to a default BFM SD (for example, an xbus sequence driver) so
that it can perform the register operation according to the specific bus protocol.
To instantiate the address map and the RSD:
Extend the relevant env with the required address map and RSD instances.
For example:
extend vr_xbus_env_u {
addr_map : vr_ad_map;
reg_driver: vr_ad_sequence_driver is instance;
keep reg_driver.addr_map == value(addr_map);
keep reg_driver.default_bfm_sd == active_masters[0].driver;
};
4.3.6
The vr_ad_map.add_with_offset() method allocates address space for the register file and adds it to the
address map. Typically this is done in the post_generate() phase, but it can also be done during
simulation.
4-15
Use the add_with_offset() with the desired offset and register file.
For example:
extend vr_xbus_env_u {
post_generate() is also {
var reg_file = passive_slaves[0].
as_a(DUT_XCORE_0 vr_xbus_agent_u).reg_file;
addr_map.add_with_offset(0,reg_file);
};
};
See Also
add_with_offset() on page 4-83
4.3.6.1
Occasionally, a register file is mapped in multiple locations in the address space. In such cases, the
register file can be accessed via multiple addresses.
To implement mirroring of register files:
Use the add_with_offset() method to allocate another address space for the register file.
For example, you could define an XCORE register file for access via addresses 0x100 and 0x1000 as
follows:
extend vr_xbus_env_u {
reg_file : XCORE vr_ad_reg_file;
post_generate() is also {
addr_map.add_with_offset(0x100,reg_file);
addr_map.add_with_offset(0x1000,reg_file);
};
};
See Also
add_with_offset() on page 4-83
4-16
4.4
Connect the eVC monitor to the address map of the register model.
The monitor identifies DUT activity. For WRITE transactions, it updates the register model via the
update() method. For READ transactions it checks the received data via the
compare_and_update() method.
2.
Figure 4-1
4-17
The register operation flow can be divided into two parts as follows:
Driving Registers
Monitoring
1.
The RSD activates the appropriate BFM sequence driver by calling its
vr_ad_execute_op() method.
2.
The BFM sequence driver executes the operation using the associated
BFM.
3.
4.
5.
The monitor notifies the address map by calling the update() method.
6.
Integrating the RSD with the BFM Sequence Driver on page 4-18 (Steps 1-3)
Updating the Register Model Using an eVC Monitor on page 4-20 (Steps 4-6)
4.4.1
Register sequences are executed by the register sequence driver (RSD). When a do action is executed,
the RSD activates the destination BFM sequence driver by calling its vr_ad_execute_op() method and
forwarding the register operation to it. The vr_ad_execute_op() method must be implemented by the
eVC developer. It converts the protocol-independent operation into eVC-specific bus transactions.
vr_ad_execute_op(operation : vr_ad_operation) : list of byte @clock;
The information needed for creating eVC-specific register access is available through the operation
parameter. The returned value of the method is a list of byte, used only for READ operations.
Table 4-1 describes the information that is typically required from the operation parameter.
Table 4-1
Field/Method
Description
address
direction
addr_space
byte_enable
4-18
Table 4-1
Field/Method
Description
get_data()
get_lob_data()
get_num_of_bytes()
get_reg()
Example
You could implement the vr_ad_execute_op() method for the Cadence XBus eVC as follows:
extend vr_xbus_master_seq_kind : [REG_SEQ]
extend vr_xbus_master_driver_u {
// This example uses a dedicated sequence instead of MAIN for executing
// the register operation
reg_seq : REG_SEQ vr_xbus_master_seq;
keep reg_seq.driver == me;
vr_ad_execute_op(op: vr_ad_operation): list of byte @clock is {
if op.direction == WRITE {
reg_seq.write(op.get_num_of_bytes(),data_size_in_byte,
op.address, op.get_data())
} else {
result = pack(packing.low,
reg_seq.read(op.get_num_of_bytes(), op.address));
};
};
};
Note The above implementation uses the read and write methods of the native sequence. The
implementation of these methods is recommended by eRM. They are described in the section on
Enhancing the User Interface of the e Reuse Methodology (eRM) Developer Manual.
The read() and write() methods of the Cadence XBus eVC sequences are implemented as follows:
extend vr_xbus_master_sequence {
!trans: MASTER vr_xbus_trans_s;
// This method performs one write transfer on the bus.
write(size : uint, addr : uint(bits:16),
data : uint(bits:64)) @driver.clock is {
assert size in [1, 2, 4, 8];
do trans keeping {
.addr == addr;
.read_write == WRITE;
System Verification Methodology
4-19
.size == size;
.data == pack(packing.low, data[size*8-1:0]);
};
};
// This method performs one read transfer on the bus.
read(size : uint, addr : uint(bits:16)) :
uint(bits:64) @driver.clock is {
assert size in [1, 2, 4, 8];
do trans keeping {
.addr == addr;
.read_write == READ;
.size == size };
if trans != NULL {
result = pack(packing.low, trans.data);
} else {
out("trans is NULL");
};
};
};
See Also
Cadence XBus documentation
4.4.2
Typically, the register model is updated by hooking it to a monitor. When the monitor identifies bus
activity, it interprets the activity and updates the register model using the update() method (for WRITE
transactions), and the fetch() or compare_and_update() methods (for READ transactions). The
compare_and_update() method compares the data from the bus with the data stored in the e model and
issues a DUT error for any mismatch. See also Disabling Comparison for Registers on page 4-48.
To implement automatic update of the model:
};
};
Notes
Typically monitors contain an event (for example, transaction_end) that can be used for updating.
Monitors should have access to a register file or address map. In the above example, access is achieved
through a reference to the address map in the env. If the register file is local to the agent enclosing
the monitor, the monitor can access it directly.
4.5
4.5.1
The data item that represents a register access is vr_ad_operation. The vr_ad_operation struct carries
specific information about the actual operation to be performed on the register, such as the direction of
the operation, backdoor access, and so on. The same data item serves both register and memory
operations, implemented under different subtypes. The register-specific attributes are defined under the
REG subtype. The memory-specific attributes are defined under the MEM subtype.
The vr_ad_sequence is defined as a sequence of vr_ad_operation.
To perform a simple register access:
Create a new subtype of vr_ad_sequence, and use the predefined op field as the sequence item.
For example, you could access the EX_REGS_TX_MODE register as follows:
extend vr_ad_sequence_kind : [MY_SEQ];
extend MY_SEQ vr_ad_sequence {
body() @driver.clock is only {
do op keeping {
.reg is a EX_REGS_TX_MODE vr_ad_reg;
System Verification Methodology
4-21
.direction == WRITE;
};
};
};
As this syntax tends to get complicated when many constraints are needed or when indirect accessing is
required, Cadence recommends using the macros described in write_reg and read_reg Macros on page
4-24. The macros provide a more efficient implementation than the preceding example.
Table 4-2 and Table 4-3 list all controllable fields in vr_ad_operation and vr_ad_sequence. The rest of
this section provides more details on how and when to constrain these fields.
Table 4-2
Fields of vr_ad_operation
Field
When
Description
addr_space: vr_ad_space_kind
address: vr_ad_addr_t
backdoor: bool
byte_enable: vr_ad_data_t
dest_driver:
any_sequence_driver
direction: vr_ad_rw_t
op_mode: vr_ad_op_mode
reg: vr_ad_reg
4-22
REG
Table 4-2
Field
When
Description
static_item: vr_ad_base
Table 4-3
Fields of vr_ad_sequence
Field
When
Description
addr_space:
vr_ad_space_kind
backdoor: bool
dest_driver:
any_sequence_driver
indirect_seq: INDIRECT
vr_ad_sequence
mode: vr_ad_indirect_mode
DIRECT or INDIRECT
prevent_test_done: bool
reg: vr_ad_reg
static_item: vr_ad_base
INDIRECT
Note Fields that appears both in vr_ad_operation and in vr_ad_sequence, by default, inherit their
values from their patent sequence.
4-23
4.5.2
Purpose
Read and write registers.
Syntax
write_reg [{op-block-constraints}]
register-field [{register-block-constraints}]|[val[ue] register-value-exp]
read_reg [op-block-constraints] register-field
Parameters
op-block-constraints
register-field
Register instance
register-block-constraints
register-value-exp
Usage
To use the write_reg and read_reg macros:
Define a field of the appropriate register type, and apply the relevant macro to it.
For example:
extend vr_ad_sequence_kind : [MY_SEQ];
extend MY_SEQ vr_ad_sequence {
!tx_mode : EX_REGS_TX_MODE vr_ad_reg;
body() @driver.clock is only {
write_reg tx_mode {.field1 == 3; ,field2 == 4};
read_reg tx_mode;
};
};
Specify the value for the register in the write_reg value macro.
For example:
extend vr_ad_sequence_kind : [RWR];
extend RWR vr_ad_sequence {
!r1 : R1 vr_ad_reg;
body() @driver.clock is {
read_reg r1;
write_reg r1 value 0xaa;
read_reg r1;
};
};
For an example of using op-block-constraints, see Writing to a Specific Instance of a Register Type on
page 4-27.
4.5.3
4-25
For example, you could have a configuration sequence that sets the command and base address registers
of the PCI device and reads the status register after a short delay as follows.
extend vr_ad_sequence_kind: [VR_PCI_CONFIG];
extend VR_PCI_CONFIG vr_ad_sequence {
!command_reg : VR_PCI_COMMAND_REG vr_ad_reg;
!base_addr
: VR_PCI_BASE_ADDR vr_ad_reg;
!status
: VR_PCI_STATUS vr_ad_reg;
body() @driver.clock is only {
write_reg command_reg {
.io == FALSE;
.mem == TRUE;
.fast_back_to_back == FALSE
};
write_reg base_addr {
.offset == 0x10;
.mem_kind == MEM;
.base_addr == 0x30000
};
wait [10];
read_reg status;
if status.signalled_serr {
out("signal_serr is enabled in ",status);
};
};
};
4.5.4
To modify a register field (doing read-modify-write using the value stored in the e model):
1.
2.
Create a copy of the register using the copy() method and cast it to the exact register subtype.
3.
4.
Write the copy of the register back using the write_reg macro.
Example
extend MAIN vr_ad_sequence {
!xmode : EX_REGS_TX_MODE vr_ad_reg;
body() @driver.clock is only {
xmode = driver.addr_map.get_reg_by_kind(EX_REGS_TX_MODE).copy()\
.as_a(EX_REGS_TX_MODE vr_ad_reg);
4-26
xmode.dest = 2;
// Modify field value
write_reg xmode {it == xmode}; // Write back the modified register.
};
};
4.5.5
Occasionally the same register type is used in several places. For example, a device with multiple ports
controlled by identical registers, one for each port, can be modeled using multiple instances of the same
register type. You might want to access a specific register, for example, the register of port number 2.
To write to a specific instance of a register type:
Example 2
In the following example, the register file contains a list of registers of the same type. The register type
is not unique inside the register file. Therefore, you must constrain the address of the operation to the
right register address. Alternatively, you can constrain the static_item field to the specific register
instance.
// Define EX_PORT_CTL register type.
reg_def EX_PORT_CTL {
System Verification Methodology
4-27
Note If more than one instance exists and no static_item or address is specified, an error message is
issued.
See Also
write_reg and read_reg Macros on page 4-24
add_with_offset() on page 4-79
4.5.6
4-28
4.5.6.1
Accessing a random register can be done using the predefined SIMPLE vr_ad_sequence. With the
SIMPLE sequence you can control the register kind and the data to be written. If you do not constrain
those fields, the kind of the register is generated as one of the register kinds that exist in the address map.
The data is generated randomly. If multiple registers of the same kind exist in the address map, the
SIMPLE sequence accesses one of them. Table 4-4 describes the controllable fields of SIMPLE
vr_ad_sequence.
Table 4-4
Field
Description
reg_kind : vr_ad_reg_kind
Register kind
reg_addr : vr_ad_addr_t
reg_data : vr_ad_data_t
direction : vr_ad_rw_t
READ or WRITE
Example 2
Writing 0xaaaa to EX_REGS_TX_MODE register:
extend MAIN vr_ad_sequence {
!simple : SIMPLE vr_ad_sequence;
body() @driver.clock is only {
do simple keeping {
.reg_kind == EX_REGS_TX_MODE;
System Verification Methodology
4-29
.direction == WRITE;
.reg_data == 0xaaaa;
};
};
};
4.5.6.2
For accessing all registers in a register file, you must implement a dedicated sequence. For example:
extend EX_FILE_ACCESS vr_ad_sequence {
body() @driver.clock is {
// Get a reference to the register file in the e model
var ex_file = driver.addr_map.get_reg_file_by_kind(EX_FILE)
for each (e_model_reg) in ex_file.get_all_regs() {
do op keeping {
.direction
== WRITE;
.static_item == e_mode_reg;
};
};
};
};
4.6
4.6.1
A post_access() hook is provided to implement side effects triggered by accessing a register. This hook
is activated only after completion of an update(), fetch(), or compare_and_update() operation;
however, it does provide access to the original value of the register before the operation and to the
parameters of the operation itself.
The post_access() hook method can be used for checking the legality of the register values. For
example, you could verify that a reserved field (resv) of EX_REGS_TX_MODE is always zero as
follows:
extend EX_REGS_TX_MODE vr_ad_reg {
post_access(direction : vr_ad_rw_t) is only {
check that resv == 0 else
dut_error("resv value must be 0 in ", me);
};
};
You can also use the post_access() hook to implement side effects such as clear on read:
extend EX_REGS_TX_MODE vr_ad_reg {
// Clear on Read
post_access(direction : vr_ad_rw_t) is {
if direction == READ {
write_reg_val(0x0);
};
};
};
The pre_access() hook method provides similar capabilities for actions needed just before the register is
accessed.
Table 4-5 describes the information that you can get inside the pre_access() and post_access() methods.
Table 4-5
Field / Method
Struct
Description
get_access_data() :
list of byte
vr_ad_reg /
vr_ad_reg_file /
vr_ad_map
4-31
Table 4-5
Field / Method
Struct
Description
get_access_path() : list
of vr_ad_group
vr_ad_reg /
vr_ad_reg_file /
vr_ad_map
get_access_kind() :
vr_ad_acess_t
vr_ad_reg /
vr_ad_reg_file /
vr_ad_map
get_access_offset() :
vr_ad_addr_t
vr_ad_reg /
vr_ad_reg_file /
vr_ad_map
get_prev_value() :
vr_ad_data_t
vr_ad_reg
get_cur_value() :
vr_ad_data_t
vr_ad_reg
For a full list of the fields of static_info, see vr_ad_reg_static_info on page 4-77.
These values can be used to update the corresponding value in the agent. For example you could update
the value of cur_reg_value in the agent based on the previous value and the value written into
EX_REGS_TX_MODE as follows:
extend EX_REGS_TX_MODE vr_ad_reg {
post_access(direction : vr_ad_rw_t) is only {
if static_info.written_data ==
0x1 and static_info.prev_value == 0x3 {
get_enclosing_unit(vr_abus_agent).cur_reg_value =
static_info.cur_value;
};
};
};
See Also
Fields and Methods of vr_ad_reg on page 4-75
write_reg_val() on page 4-65
4-32
4.6.2
Reset
Each register has a reset value specified in the reg_def macro. This value is restored automatically
whenever the reset() method is called for any register. Typically, reset() is called for register files or
address maps. In that case, the appropriate reset value is assigned to each contained register instance. For
example:
extend XCORE vr_ad_reg_file {
post_generate() is also {
reset();
};
};
4.6.3
Coverage
Coverage information is collected on each register access, that is, each time the update(), fetch(), or
compare_and_update() method is called.
The coverage group vr_ad_reg.reg_access is predefined in the package as follows:
cover reg_access is {
item kind using per_instance;
item direction : vr_ad_rw_t = static_info.direction;
cross kind, direction;
};
4.6.3.1
{
: 0 : cov;
: 0 : cov;
: 0 ;
4-33
Coverage information is collected only on the marked fields: dest, frame_kind, and valid_frame. The
following coverage group would be created for the preceding example. Each coverage item name
contains a prefix of the register kind to avoid name conflicts.
extend EX_REGS_RX_MODE vr_ad_reg {
cover reg_access(kind == EX_REGS_RX_MODE) is also {
item ex_regs_rx_mode_dest : uint(bits:2) = dest;
item ex_regs_rx_mode_frame_kind : uint(bits:2) = frame_kind;
item ex_regs_rx_mode_valid_frame : uint(bits:1) = valid_frame;
};
};
See Also
reg_def Macro on page 4-10
4.6.3.2
Sometimes you might want to ignore some of the predefined coverage values, for example, when a
status register can only be read and should never be written.
To ignore predefined coverage values:
4-34
Extend the definition of the coverage item under the appropriate per-instance subgroup with ranges.
For example:
extend VR_AD_TX_DATA vr_ad_reg {
cover reg_access(kind == VR_AD_TX_DATA) is also {
item vr_ad_tx_data_data using also ranges = {
range([0],"0");
range([1..254],"1..254");
range([255],"255");
};
};
};
4.6.3.3
You might want to disable coverage sampling of a register file or to manually activate coverage
sampling of a specific register.
To disable coverage sampling of a register file:
4-35
4.6.4
Backdoor Operations
The backdoor feature lets you access DUT registers directly in zero time without resorting to bus
transactions. You can run any register sequence in backdoor mode by simply turning on the backdoor
flag of the sequence. You can also monitor internal changes in the DUT registers by setting a callback on
a register change. Backdoor accesses are done using the HDL path of the registers. Therefore, you must
specify that information for every register to be backdoor accessed.
This section contains:
4.6.4.1
Backdoor access is done using the HDL path of the register. Therefore, specifying the HDL path is a
precondition for using this mechanism.
To specify the HDL path of the register:
See Also
vr_ad_reg_static_info on page 4-77
4.6.4.2
See Also
write_reg and read_reg Macros on page 4-24
Examples in the vr_ad/examples/backdoor directory
System Verification Methodology
4-37
4.6.4.3
In many cases DUT registers are changed internally and not as a result of an external register access.
Those changes are invisible to the verification environment. This can create inconsistency between the
vr_ad model and the DUT registers. You can monitor all internal changes of a register by activating the
backdoor automatic update mechanism. This mechanism uses change() @sim to set a callback on
register changes. For every change in the DUT register, the vr_ad_reg.dut_reg_changed event is
emitted, and the vr_ad model is updated.
To monitor internal changes of a register:
4.6.4.4
When the RSD executes a backdoor register operation at the lowest level, it calls the following
write_reg_val_backdoor() and read_reg_val_backdoor() hook methods. The default implementation
is as follows:
extend VR_AD_TX_MODE vr_ad_reg {
write_reg_val_backdoor(data : vr_ad_data_t) is only {
'(static_info.backdoor_path)' = data;
};
read_reg_val_backdoor() : vr_ad_data_t is only {
result = '(static_info.backdoor_path)'
};
};
You might require a non-standard implementation, for example, when your register has fields with a
different backdoor_path.
To override the default backdoor implementation:
4-38
See Also
Fields and Methods of vr_ad_reg on page 4-75
vr_ad_reg_static_info on page 4-77
4.6.4.5
Updating the address map is usually a monitor job, but monitors cannot recognize backdoor activities.
To keep the address map updated, each register backdoor sequence updates also the address map by
default. (Keep in mind that the address map might not be updated if a register was changed internally by
the DUT.) RSD has another flag that controls whether the result of a backdoor read access will be taken
directly from the address map instead of reading it from the DUT. This feature can be useful when you
want to check your register sequences but the DUT is not ready yet.
To change read access results to be taken directly from the address map:
4.6.4.6
Table 4-6
Field / Method
Struct
Description
backdoor : bool
vr_ad_operation
read_reg_val_backdoor() :
vr_ad_data_t
vr_ad_reg
set_backdoor_auto_update(
bool)
vr_ad_reg
set_backdoor_path(
path: string)
vr_ad_reg
set_field_backdoor_path(
field_name: string,
path: string)
vr_ad_reg
4-39
Table 4-6
Field / Method
Struct
Description
write_reg_val_backdoor(
vr_ad_data_t)
vr_ad_reg
backdoor : bool
vr_ad_sequence
4.6.5
Indirect Addressing
Some registers and register files are not mapped in the address map and can only be accessed indirectly.
In other words, reading and writing of unmapped registers is done through mapped registers. For
example, you could read from an unmapped register by writing the address into a mapped address
register and then reading the data from a data register.
Figure 4-2
Address Map
Mapped File
Unmapped File
0x0
mapped_file
addr_reg
0x3
data_reg
0x8
0x1
map_addr_reg
0x2
map data_reg
0x3
0x8
0x4
0x5
0x6
Do not assign an address to the unmapped register file (in other words, do not use
vr_ad_map.add_with_offset()).
4-40
Driving Driving a register indirectly must be translated into a sequence of accesses to the
corresponding address and data registers (see Driving Indirect Registers on page 4-41).
Monitoring Any access to the mapped registers must be recognized by the monitor and passed to
the unmapped register file (see Identifying and Handling Indirect Access on page 4-43).
See Also
vr_ad_map on page 4-82
4.6.5.1
Accessing an unmapped register typically requires several accesses to mapped registers. The logic of
which register to access and how, must be encapsulated in a special INDIRECT sequence. This
sequence should be used to perform the indirect access. You can use the write_reg and read_reg macros
to hide the indirection, making accesses to mapped and unmapped registers looks the same. This section
explains how to create an INDIRECT sequence and activate it automatically via the write_reg and
read_reg macros.
The mode field in vr_ad_sequence determines whether the sequence is an INDIRECT sequence or not.
INDIRECT is a subtype of another when determinant. Therefore, to create a user-defined indirect
sequence, you must write:
extend MY_SEQ INDIRECT vr_ad_sequence {...
Table 4-7 describes the fields of the INDIRECT subtype.
Table 4-7
Field
Description
direction: vr_ad_rw_t
reg: vr_ad_reg
Typically, with indirect registers, the offset of the register in the unmapped register file is taken as data
to the mapped address register. Table 4-8 on page 4-42 describes the method used for this purpose.
4-41
Table 4-8
Method
Struct
Description
get_indirect_addr(map :
vr_ad_map)
vr_ad_reg
Create a dedicated INDIRECT sequence that implements the logic required for accessing an
unmapped register file.
For example:
extend vr_ad_sequence_kind : [ACTIVE_XCORE];
extend ACTIVE_XCORE INDIRECT vr_ad_sequence {
!map_addr : VR_AD_MAP_ADDR vr_ad_reg;
!map_data : VR_AD_MAP_DATA vr_ad_reg;
body() @driver.clock is only {
if direction == WRITE {
write_reg map_addr {.addr ==
reg.get_indirect_addr(driver.addr_map)};
write_reg map_data {.data == reg.read_reg_val()};
} else {
write_reg map_addr {.addr ==
reg.get_indirect_addr(driver.addr_map)};
read_reg map_data;
// Important: Update the returned register
reg.write_reg_val(map_data.read_reg_val());
};
};
};
2.
Specify the name of the sequence to be executed when doing an indirect access by calling
vr_ad_reg_file.set_indirect_seq_name().
3.
Add the unmapped register file to the address map using add_unmapped_item() (instead of
add_with_offset()).
For example:
extend ex_c_bus_env {
indirect_reg_file : XCORE vr_ad_reg_file;
post_generate() is also {
indirect_reg_file.set_indirect_seq_name(ACTIVE_XCORE);
4-42
addr_map.add_unmapped_item(indirect_reg_file);
};
};
See Also
write_reg and read_reg Macros on page 4-24
vr_ad_reg_file on page 4-78
4.6.5.2
To keep an unmapped register file updated, the unmapped register file must be notified when any access
to the corresponding (mapped) data register occurs.
To facilitate automatic update of unmapped registers:
1.
Connect the unmapped register file to the appropriate mapped register using the attach() method
with the unmapped register file as a parameter (mapped_reg.attach(unmapped_reg_file)).
The mapped register adds the register file to an internal list of items to be notified upon access.
For example:
extend ex_c_bus_env {
unmapped_reg_file : XCORE vr_ad_reg_file; // Unmapped reg file
mapped_reg_file : MAP_REGS vr_ad_reg_file; // Mapped reg file
post_generate() is also {
// Add the mapped reg file to the address map
addr_map.add_with_offset(0x0,mapped_reg_file);
// Add the unmapped reg file to the address map
addr_map.add_unmapped_item(unmapped_reg_file);
// Connect the the unmapped reg file to the (mapped) register
// VR_AD_MAP_DATA.
mapped_reg_file.vr_ad_map_data.attach(unmapped_reg_file);
};
};
2.
Implement the indirect_access() method of the unmapped register file to define its behavior upon
notification.
For example:
extend XCORE vr_ad_reg_file {
// References to the mapped registers
!mapped_addr : VR_AD_MAP_ADDR vr_ad_reg;
!mapped_data : VR_AD_MAP_DATA vr_ad_reg;
// On WRITE operation, update the relevant unmapped register.
4-43
See Also
Indirect access example in the vr_ad/examples directory
4.6.6
The default field order of a register is packing.high, from a high bit position to a low bit position. The
first field declared in the register holds the highest bits and the last field declared in the register holds the
lowest bits. For example, the default field order of the EX_REGS_RX_MODE register is:
//
NAME
FILE
OFFSET
reg_def EX_REGS_RX_MODE XCORE 32'h00000003 {
reg_fld dest
: uint(bits:2);
//
reg_fld frame_kind : uint(bits:2);
//
reg_fld par_err
: uint(bits:1);
//
reg_fld valid_frame : uint(bits:1);
//
reg_fld resv
: uint(bits:2);
//
};
Bits
Bits
Bits
Bits
Bits
[7:6]
[5:4]
[3:3]
[2:2]
[1:0]
Some specs use the opposite field order (packing.low). They interpret the same register definitions as:
//
4-44
NAME
FILE
OFFSET
reg_def EX_REGS_RX_MODE
reg_fld dest
reg_fld frame_kind
reg_fld par_err
reg_fld valid_frame
reg_fld resv
};
XCORE 32'h00000003 {
: uint(bits:2);
//
: uint(bits:2);
//
: uint(bits:1);
//
: uint(bits:1);
//
: uint(bits:2);
//
Bits
Bits
Bits
Bits
Bits
[1:0]
[3:2]
[4:4]
[5:5]
[7:6]
You can change the register packing option for a register file from packing.high to packing.low. The
change affects the packing option for all registers in the file. You cannot change the order for only some
of the registers in a register file.
To change the default packing option of a register file from packing.high to packing.low:
See Also
vr_ad_reg_file on page 4-78
4.6.7
The default addressing width for a register file is BYTE, which means every address entry contains one
byte only. Typically, memories are presented by address (offset) entries, where each line contains 4 bytes
and the offset jumps by 4 as follows:
byte4
byte3
byte2
byte1
0x0
byte8
byte7
byte6
byte5
0x4
byte12
byte11
byte10
byte9
0x8
Sometimes the required address width is Halfword or Word. In that case, each address entry will contain
two or four bytes instead of one byte as follows:
halfword2
halfword1
0x0
halfword4
halfword3
0x2
4-45
halfword6
halfword5
0x4
word1
0x0
word2
0x1
word3
0x2
4.6.8
Register sequences contain a flag that controls whether the sequence will have an objection to end of test
and will automatically raise and drop an objection to TEST_DONE. By default this flag is turned off. In
other words, by default, register sequences do not influence the end of test decision.
To involve register sequences in the end of test decision:
See Also
Basic Register Operations on page 4-21
4.6.9
By default, the register and memory package prints only messages on activity in the registers. You can
have additional messages relating to the register files and address map by changing the logger verbosity
level to high.
4-46
To turn on all messages coming from the register and memory package:
Or:
To filter out all messages coming from the register and memory package:
Or:
The preceding examples assume that the messages are handled by sys.logger. For more information, see
the section on messaging in the e Reuse Methodology (eRM) Developer Manual.
4.6.10
Some protocols (like the PCI) uses multiple address spaces (MEMORY, IO, CONFIG) on the bus. The
control signals determine which address space to access. The default behavior of vr_ad_sequence_driver
is to work with a single address map specified by a constraint on the addr_map field. Therefore, by
default, sequences can access only registers from one address map.
4-47
Label each address map with a different enumerated value, and use
vr_ad_sequence_driver.add_space() to add each address map with its label to the RSD map list.
For example:
extend vr_ad_space_kind : [EX_IO, EX_CONFIG1];
extend ex_c_bus_env {
addr_map1 : vr_ad_map;
addr_map2 : vr_ad_map;
rsd
: vr_ad_sequence_driver;
post_generate() is also {
// Two address spaces can be accessed through the bus
rsd.add_space(addr_map1, EX_CONFIG1);
rsd.add_space(addr_map2, EX_IO);
};
};
2.
Use the label in the sequence to specify which address map to access.
For example:
extend MAIN vr_ad_sequence {
!r2 : EX_R2 vr_ad_reg;
// By default, register accesses use the IO space
keep soft addr_space == EX_IO;
body() @driver.clock is only {
...
// Use the CONFIG1 space for this access
write_reg {.addr_space == EX_CONFIG1 } r2 keeping
{.data1 == 3};
};
};
3.
4.6.11
In read transactions, the e model compares the data captured from the bus with the information stored in
model. This is done when an eVC monitor calls the compare_and_update() method. Comparison of
registers is done via a comparison mask. By default, the value of the mask is 0xffffffff, meaning compare
all register bits. Change the mask value lets you control which bits to compare.
To disable comparison for a register (or for part of a register):
Call the vr_ad_reg.set_compare_mask() method with the appropriate bits of the parameter set to 0.
4-48
For example, you could disable comparison of the VR_AD_RX_MODE register as follows:
extend VR_AD_RX_MODE vr_ad_reg {
set_static_info() is {
set_compare_mask(0);
};
};
4.6.12
Table 4-9
Attributes of
Attribute
Description
RW
Read Only field. Write access does not change the field
Write Only field. Read access always returns the same value. By
default, the reset value is returned. You can control this with
set_rmask_returned_value()
RC
RW1C
For example:
reg_def EX_REG {
reg_fld fld1
reg_fld fld2
reg_fld fld3
reg_fld fld4
reg_fld fld5
};
:
:
:
:
:
uint(bits:2):
uint(bits:2):
uint(bits:1):
uint(bits:1):
uint(bits:2):
RW
R
W
RC
RW1C
:
:
:
:
:
0;
0;
0;
0;
0;
Table 4-10 describes the methods you can use to override the field attributes by applying a different
mask value to the register.
4-49
Table 4-10
Method
Description
set_wmask(vr_ad_data_t)
Changes the write mask. 0 bits in the mask represent Read Only bits
that do not change by writing to the register
set_rmask(vr_ad_data_t)
Changes the read mask. 0 bits in the mask represent Write Only bits
that can not be read and return always default values no matter what
value is stored in the register
set_rcmask(vr_ad_data_t)
set_rw1cmask(vr_ad_data_t)
For example:
reg_def EX_REG {
set_static_info() is also {
set_wmask(0xff);
set_rmask(0xff00);s
};
};
4.7
Sparse Memory
The vr_ad_mem struct enables modeling of sparse memory. The sparse memory can be used as a
reference model to a real memory located in the DUT or as an external memory implemented solely in e
(for example, the memory of an eVC slave). Memory that is a reference model is instantiated in the
module eVC or in the passive agent corresponding to the DUT. Memory that is an external memory in e
is instantiated in the corresponding active agent.
This section contains:
4.7.1
Instantiating a Memory
To instantiate a memory:
1.
Extend vr_ad_mem_kind, and add a new kind (according to eRM naming conventions).
For example, you could add the kind EX_MEM as follows:
extend vr_ad_mem_kind : [EX_MEM];
2.
Extend the relevant unit by adding a field of the specific vr_ad_mem subtype, and constrain the
memory to the appropriate size.
For example, you could set a 1Kx64 memory as follows:
extend ACTIVE SLAVE vr_xbus_agent {
mem : EX_MEM vr_ad_mem;
keep mem.size == 0x1024;
keep mem.addressing_width_in_bytes == 8;
};
3.
Add the memory to the address space using add_with_offset() (same as adding a register file)
For example, assuming that the memories of the slaves are mapped in consecutive addresses, you
could map all active XBus slaves as follows:
extend vr_xbus_env {
post_generate() is also {
for each (slave) in active_slaves {
addr_map.add_with_offset(index*1000,slave.mem);
};
};
};
4.7.2
Accessing Memory
As explained in Basic Register Operations on page 21, the same vr_ad_operation is used for register
operations (REG vr_ad_operation) and memory operations (MEM vr_ad_operation). The predefined
field mem_op (of type MEM vr_ad_operation) can be used as a sequence item for doing memory
operations. The memory operations are more general and can also access registers (by specifying a
register address).
4-51
To access a memory:
Use the predefined field mem_op as a sequence item and constrain its address, data, and direction.
For example:
extend MAIN vr_ad_sequence {
body() @driver.clock is only {
do mem_op keeping {
.direction == WRITE;
.address == 0x10;
.data == {1;2;3;4;5;6;7;8};
};
};
};
Note The above code has an abbreviated form using the write_mem and read_mem macros. See
write_mem and read_mem Macros on page 4-52.
4.7.2.1
Purpose
Read and write memories.
Syntax
write_mem [{op-block-constraints}] address write-data-exp [mask]
read_mem [{op-block-constraints}] address read-list length
Syntax Example
var data : list of byte;
read_mem 0x100 data 4;
write_mem 0x110 data;
Parameters
op-block-constraints
address
Address to be accessed
write-data-exp
Data to be written
mask
4-52
read_list
length
Example
Writing and reading from a memory.
extend vr_ad_sequence_kind : [EX_SEQ1];
extend EX_SEQ1 vr_ad_sequence {
body() @driver.clock is only {
var rdata : list of byte;
write_mem 0x10 0xaabbccdd; // Write to address 0x10
read_mem 0x10 rdata 4;
// Read 4 bytes from address 0x10
};
};
4.7.3
Once you add the memory to the address map and connect the address map to an eVC monitor, the
memory is updated and automatically compares read data.
For example:
extend vr_xbus_bus_monitor_u {
on transfer_end {
if transfer.read_write == WRITE {
env.address_map.update(transfer.addr,%{transfer.data},{});
} else {
compute env.address_map.compare_and_update(
transfer.addr,%{transfer.data});
};
};
};
See Also
Updating the Register Model Using an eVC Monitor on page 4-20.
4.7.4
Memory Features
4-53
4.7.4.1
You can initialize a memory by reading its content from a file. You can also dump a memory content into
a file. The format of the data file is the format which is supported by verilog.
To initialize the memory from a data file:
Use the method readmemh() (for hex format) or readmemb() (for binary format):
For example:
extend ACTIVE SLAVE vr_xbus_agent {
post_generate() is also {
mem.readmemh("mem_init.txt",
0, MAX_UINT, // Address range of memory to update
0, MAX_UINT); // Address range of file to read
};
};
Use the method dumpmemh() (for hex format) or dumpmemb() (for binary format):
For example:
extend ACTIVE SLAVE vr_xbus_agent {
finalize() is also {
mem.dumpmemh("mem_data.txt",
0, 0x1000) // Address range of the memory to dump
};
};
4.7.4.2
When the memory is used as an eVC slave memory, you need the ability to control the value of returned
data when reading from uninitialized addresses. By default, reading from an uninitialized address
returns zero. You can change the default behavior to return a random value or a specific value. For more
complex behavior, you can override the memory get_data() method.
4-54
Extend the vr_ad_mem.get_data() method for a specific memory subtype, and implement your own
logic in it.
For example:
extend vr_ad_mem_kind : [EX_MEM];
extend ACTIVE SLAVE vr_xbus_agent {
mem : EX_MEM vr_ad_mem;
};
extend EX_MEM vr_ad_mem {
get_data(addr : vr_ad_addr_t, size : uint) : list of byte is only {
if address < 0x100 {
// Set the size of the list and fill it with 0xbb
result.resize(size,TRUE,0xbb,TRUE);
} else {
// Set the size of the list and fill it with 0xaa
result.resize(size,TRUE,0xaa,TRUE);
};
};
};
4.7.4.3
Side Effects
Like any addressable item, the pre_access() memory method is called before an access to the memory,
and the post_access() memory method is called after an access to the memory. You can use these hooks
to implement side effects.
See Also
Implementing Side Effects on page 4-31
4-55
4.7.4.4
Memory backdoor access lets you directly access a memory implemented in Verilog or VHDL. Like
register access, you must specify the memory backdoor_path and access is done using the read_mem
and write_mem macros with the backdoor flag turned on.
To set the memory backdoor_path:
1.
2.
Use the read_mem and write_mem macros with the backdoor flag.
For example:
extend vr_ad_sequence_kind : [EX_SEQ2];
extend EX_SEQ2 vr_ad_sequence {
body() @driver.clock is only {
var rdata: list of byte;
write_mem {.backdoor} 0x10 0xaabbccdd; // Write to address 0x10
read_mem {.backdoor} 0x10 rdata 4;
// Read 4 bytes from
// address 0x10
};
};
See Also
Backdoor Operations on page 4-36
Examples in the vr_ad/examples/memory directory
4-56
4.7.5
Sometimes you must store complex structures like packets or descriptors in the memory. You might
want to have both a high level (packet) and a low level (list of byte) representation. The following
capabilities are required:
4.7.5.1
Memory objects are created with the mem_obj_def macro.The mem_obj_def macro works almost the
same as the reg_def macro.
Syntax
mem_obj_def obj-name {
mem_fld field-name : field-type [: cov];
};
Syntax Example
mem_obj_def EX_DESC {
mem_fld kind
mem_fld data[16]
};
: uint(bits:2) : cov;
: list of byte : cov;
4-57
Parameters
obj_name
Memory object name. Must follow eRM naming conventions and use a company
prefix
field-name
field-type
cov
4.7.5.2
After defining a memory object type, you can generate its content and connect it to the memory by
calling one of the memory methods described in Table 4-11. This allocates (generates) an address space
from the free memory areas using the default memory set (the mem_set field of vr_ad_mem). The
allocation prevents subsequent allocations from using the same address space.
When an object is connected to the memory:
The memory content can be updated using object methods (see Methods for Accessing Objects in
Memory on page 4-61).
The object will be updated automatically on any change in the corresponding memory area.
Table 4-11
Method
Description
alloc_area(min_addr : vr_ad_addr_t,
max_addr : vr_ad_addr_t) : vr_ad_set
alloc_obj(addr : vr_ad_addr_t,
mem_obj : vr_ad_mem_obj) : bool
alloc_obj_rnd(min_addr : vr_ad_addr_t,
max_addr : vr_ad_addr_t, mem_obj :
vr_ad_mem_obj, alignment : uint) :
vr_ad_addr_t
alloc_obj_from_set(addr : vr_ad_addr_t,
mem_obj : vr_ad_mem_obj, set :
vr_ad_set) : bool
4-58
Table 4-11
Method
Description
alloc_obj_rnd_from_set(min_addr :
vr_ad_addr_t, max_addr : vr_ad_addr_t,
mem_obj : vr_ad_mem_obj, set :
vr_ad_set, alignment : uint) :
vr_ad_addr_t
dealloc_area(min_addr : vr_ad_addr_t,
max_addr : vr_ad_addr_t)
dealloc_obj(mem_obj : vr_ad_mem_obj)
dealloc_obj_to_set(mem_obj :
vr_ad_mem_obj, set : vr_ad_set)
get_mem_obj_by_address(addr :
vr_ad_addr_t)
Example
You could allocate address space for a list of descriptors as follows:
extend ex_c_bus_env {
mem : vr_ad_mem;
keep mem.mem_size == 1000;
post_generate() is also {
addr_map.add_with_offset(0x100,mem);
};
};
extend MAIN vr_ad_sequence {
descriptors[16] : list of EX_DESC vr_ad_mem_obj;
body() @driver.clock is only {
// Reference to the memory
var mem : vr_ad_mem = driver.addr_map.get_mem_by_address(0x100);
for each (desc) in descriptors {
// Allocate a space from the desriptor
desc_addr = mem.alloc_obj_rnd(0, 1000, desc, 1);
};
};
};
4-59
4.7.6
4.7.6.1
Purpose
Read and write objects in memory.
Syntax
write_mem_obj [{op-block-constraints}] object
read_mem_obj [{op-block-constraints}] object
Parameters
op-block-constraints
object
Syntax Example
read_mem_obj desc;
write_mem_obj desc;
Example
You could read a descriptor from the memory, modify it, and write it back to the memory as follows:
extend vr_ad_sequence_kind : [EX_SEQ1];
extend EX_SEQ1 vr_ad_sequence {
!cur_desc : EX_DESC vr_ad_mem_obj;
body() @driver.clock is only {
// Reference to the memory
var mem : vr_ad_mem =
driver.addr_map.get_mem_by_address(0x100);
var desc_addr : vr_ad_addr_t;
4-60
gen cur_desc;
// Allocate random space in range [0..200] with alignment of 4
desc_addr = mem.alloc_obj_rnd(0, 200, cur_desc, 4);
// Read the content of the descriptor from the memory, modify
// it, and write it back to the memory
read_mem_obj cur_desc;
cur_desc.kind = B;
write_mem_obj cur_desc;
};
};
4.7.6.2
When you change a memory object field, the memory does not update automatically. You must
manually update the memory. You can use the write_mem_obj macro described in write_mem_obj and
read_mem_obj Macros on page 4-60 to update the memory using bus transactions. The backdoor
method for doing this depends on whether the memory is an HDL memory or an e model
Table 4-12 describes the methods that let you access the e model or HDL memory after connecting the
vr_ad_mem_obj Methods
Method
Description
update_model()
write_obj_val_backdoor()
read_obj_val_backdoor()
See Also
Examples in the vr_ad/examples/memory directory
4.8
4-61
4.8.1
Table 4-13 shows the type naming conventions used in the register and memory package.
Table 4-13
Type Name
Prefix
vr_ad
Registers only
vr_ad_reg
Memory only
vr_ad_mem
4.8.2
Type Hierarchy
The addressable items that are implemented in this package are vr_ad_reg, vr_ad_reg_file, and
vr_ad_map. A common base type vr_ad_base provides common interfaces for all of the addressable
items.
Address maps and register files contain a list of addressable items (of type vr_ad_base). Therefore, they
can hierarchically contain other address maps, register files, and registers. The following methods are
common to all addressable items:
4-62
4.8.3
Register-related attributes are sorted into three structs according to their purpose. This can facilitate
generation of registers on the fly.
For example, an attribute like the write mask is relevant only when you update the e reference model and
should be generated only once, at the beginning of the test. Therefore, it is located in the
vr_ad_reg_static_info struct, which is generated only once.
An attribute like direction (read or write) describes an operation done on a register. It is not an integral
part of a register. Therefore, it is best to locate it in the vr_ad_operation struct.
Table 4-14 describes the structs used to implement the various aspects of registers.
Table 4-14
Register-Related Structs
Struct
Description
vr_ad_operation
vr_ad_reg
vr_ad_reg_static_info
Figure 4-1 shows how the register attributes are divided. (Only the most important fields are listed.)
Figure 4-1
Operation (REG
vr_ad_operation):
reg: vr_ad_reg
dest_sd:
any_sequence_driver
direction: vr_ad_rw_t
backdoor_access: bool
Register (vr_ad_reg):
kind: vr_ad_reg_kind
!static_info:
vr_ad_reg_static_info
%field1:
%field2:
...
Static information
(vr_ad_reg_static_info):
offset: vr_ad_addr_t
size: uint
reset_value: vr_ad_data_t
wmask: vr_ad_data_t
compare_mask: bool
See Also
vr_ad_operation on page 4-64
vr_ad_reg on page 4-64
vr_ad_reg_static_info on page 4-77
4-63
4.8.4
vr_ad_operation
vr_ad_operation is the data item (sequence item) for register and memory accesses. It encapsulates all
attributes that describe the operation to be done on the register (like direction). For more information,
see Basic Register Operations on page 4-21.
4.8.5
vr_ad_reg
The different register types are modeled using when inheritance. For each register type, a new subtype
of vr_ad_reg_kind is created and the physical fields are added to the specific subtype of vr_ad_reg.
Typically, the definition of a new register is done with the reg_def macro described in Defining
Registers with the reg_def Macro on page 4-9.
Table 4-15
Method
Operation
write_reg_rawval() on page
4-67
Set a write mask (mainly for registers with multiple write masks)
Set a read mask (mainly for registers with multiple read masks)
set_compare_mask() on page
4-71
set_backdoor_path() on page
4-71
set_field_backdoor_path() on
page 4-72
compare_and_update() on
page 4-74
Read from the e model and compare with a given list of byte
4-64
Table 4-15
Method
Operation
For a list of all fields and methods of vr_ad_reg, see Fields and Methods of vr_ad_reg on page 4-75.
4.8.5.1
write_reg_val()
Purpose
Update register content.
Syntax
write_reg_val(data : vr_ad_data_t)
Parameters
data
Value to write
Description
The parameter for this method is the vr_ad_data_t type value to be updated (as opposed to a list of bytes
in the update() method). The data in this method is anded with the write mask before it is written to the
register. When using this method, the post_access() hook for side effect is not called, and no coverage is
collected.
Example
extend EX_TX_MODE vr_ad_reg {
post_access(direction : vr_ad_rw_t) is {
write_reg_val(0xffffffff);
};
};
4-65
4.8.5.2
read_reg_val()
Purpose
Return the current value of the register.
Syntax
read_reg_val(): vr_ad_data_t
Description
The returned value is of type vr_ad_data_t. When using this method, the post_access() hook for side
effect is not called, and no coverage is collected.
Example
extend ex_c_bus_driver {
reg_seq : REG_SEQ ex_c_bus_sequence;
keep reg_seq.driver == me;
vr_ad_execute_op(op_item : vr_ad_operation) : list of byte
if op_item is a REG vr_ad_operation (reg_op) {
if op_item.direction == WRITE {
reg_seq.write(op_item.address,
reg_op.reg.read_reg_val());
}
};
};
};
4.8.5.3
@clock is {
write_reg_raw()
Purpose
Update register content.
Syntax
write_reg_raw(offset : vr_ad_addr_t, data : vr_ad_data_t, mask : vr_ad_data_t)
4-66
Parameters
offset
Bit offset
data
mask
Description
The data is shifted with the offset before being written. The method does not consider the static_info
write mask, but it is masked with the mask parameter. If no mask is needed use 0xffffffff (or ~0). When
using this method, the post_access() hook for side effect is not called, and no coverage is collected.
Example
This example shows how to clear register VR_AD_R2 when the clear_r2 field of register VR_AD_R1 is
set to 1.
extend VR_AD_R1 vr_ad_reg {
post_access(direction : vr_ad_rw_t) is {
if clear_r2 == 0x1 {
// Locate target register
var my_reg_file : SIDE_EFFECT_FILE vr_ad_reg_file =
get_parents()[0].as_a(SIDE_EFFECT_FILE vr_ad_reg_file);
// Set the value of the target register
my_reg_file.vr_ad_r2.write_reg_raw(0,0x0,0xffffffff);
};
};
};
4.8.5.4
write_reg_rawval()
Purpose
Update register content with specific data.
Syntax
write_reg_rawval(data : vr_ad_data_t)
4-67
Parameters
data
Description
This method updates register content with the specified data. It does not consider any mask the data
is written as is.When using this method, the post_access() hook for side effect is not called, and no
coverage is collected.
Example
This example shows how to clear register VR_AD_R2 when the clear_r2 field of register VR_AD_R1 is
set to 1.
extend VR_AD_R1 vr_ad_reg {
post_access(direction : vr_ad_rw_t) is {
if clear_r2 == 0x1 {
// Locate target register
var my_reg_file : SIDE_EFFECT_FILE vr_ad_reg_file =
get_parents()[0].as_a(SIDE_EFFECT_FILE vr_ad_reg_file);
// Set the value of the target register
my_reg_file.vr_ad_r2.write_reg_rawval(0x0);
};
};
};
4.8.5.5
set_static_info()
Syntax
set_static_info()
Description
The reg_def macro sets only the write mask and reset value. Other optional static information like
backdoor path and description must be set by the set_static_info() method.
Example
reg_def R1 {
reg_fld data : uint(bits:32) : RW : 0;
4-68
set_static_info() is {
set_backdoor_path("r1");
};
};
4.8.5.6
set_write_mask()
Purpose
Set the write mask of a register.
Syntax
set_write_mask(wmask : vr_ad_data_t, file_or_map : vr_ad_group)
Parameters
wmask
Write mask
file_or_map
Description
Sometimes the write-mask value of a register depends on the address space where the register is
accessed. It could be R (read only) when accessing from one address space or RW (read-write) when
accessing from another address space. The set_write_mask() method lets you specify a write-mask
value for a specific address map. If no map is specified, then the write-mask value is fixed and does not
depend on the address map from which it is accessed.
Example
reg_def EX_R1 EX_FILE 0x0 {
reg_fld data2 : uint(bits:8) : RW : 0;
// [23:16]
reg_fld data1 : uint(bits:8) : RW : 0;
// [15: 8]
reg_fld data0 : uint(bits:8) : RW : 0;
// [ 7: 0]
};
extend ex_c_bus_env {
addr_map1 : vr_ad_map;
addr_map2 : vr_ad_map;
ex_file
: EX_FILE vr_ad_reg_file;
post_generate() is also {
// EX_FILE register file is mapped on both address maps with
4-69
// different offsets.
addr_map1.add_with_offset(0x00000, ex_file);
addr_map2.add_with_offset(0x01000, ex_file);
// EX_R1 has 2 masks:
// RW - when accessed from addr_map1 (as specified by the macro).
// RO - when accessed from addr_map2.
ex_file.ex_r1.set_write_mask(0x0,addr_map2);
};
};
4.8.5.7
set_read_mask()
Purpose
Set the read mask of a register.
Syntax
set_read_mask(rmask : vr_ad_data_t, file_or_map : vr_ad_group)
Parameters
rmask
Read mask
file_or_map
Description
This method sets the registers read mask. If the read-mask value is applicable only when accessing the
register through a specific address map (or register file), you must use the address map (or register file)
for the file_or_map parameter. Otherwise, use NULL.
Example
reg_def EX_R1 EX_FILE 0x0 {
reg_fld data2 : uint(bits:8) : RW : 0;
// [23:16]
reg_fld data1 : uint(bits:8) : RW : 0;
// [15: 8]
reg_fld data0 : uint(bits:8) : RW : 0;
// [ 7: 0]
set_static_info() is also {
set_read_mask(0xff00ff,NULL);
// Note that the above setting is equivalent to writing:
// reg_fld data1 : uint(bits:8) : W : 0;
// in the reg_def macro.
4-70
};
};
4.8.5.8
set_compare_mask()
Purpose
Exclude some fields from comparison.
Syntax
set_compare_mask(compare_mask : vr_ad_data_t)
Parameters
compare-mask
Compare mask
Description
This method excludes some fields from comparison.
Example
reg_def R1 {
reg_fld status : uint(bits:32) : R : 0;
set_static_info() is {
set_compare_mask(0);
};
};
4.8.5.9
set_backdoor_path()
Purpose
Set the backdoor path of a register.
Syntax
set_backdoor_path(path : string)
4-71
Parameters
path
Description
This method sets the backdoor path of a register. The register backdoor mechanism uses this string to
write (write_reg_val_backdoor()) or to read (read_reg_val_backdoor()).
Example
reg_def R1 {
reg_fld status : uint(bits:32) : RW : 0;
set_static_info() is {
set_backdoor_path(r1);
};
};
4.8.5.10 set_field_backdoor_path()
Purpose
Set the backdoor path of a single register field.
Syntax
set_field_backdoor_path(field_name : string, path : string)
Parameters
field_name
Field name
path
Description
This method sets the backdoor path of a single register field. It is useful when each register field has a
different HDL path.
4-72
Example
reg_def R1 {
reg_fld ctrl1 : uint(bits:8) : RW : 0;
reg_fld ctrl2 : uint(bits:8) : RW : 0;
set_static_info() is {
set_field_backdoor_path("ctrl1","~/top/ctrl1");
set_field_backdoor_path("ctrl2","~/top/ctrl2");
};
};
4.8.5.11 update()
Purpose
Update a specific e model register.
Syntax
update(addr : vr_ad_addr_t, data : list of byte, mask : list of byte)
Parameters
addr
data
mask
List of byte to be used as masks. Each element in the list masks the equivalent element
in the data list. 0x0 masks out a byte, and 0xff writes the data as is. An empty list {}
indicates that no masking is applied
Description
This method updates a specific e model register. Usually, you use the address 0, meaning write from the
beginning of the register. You can also use non-zero offsets if the register is in a register file. If you
update a register and you specify a mask, the mask will be anded with the wmask of the register.
Whenever this method is called, the pre_access() and post_access() hooks are activated, and coverage
of the register is collected with direction WRITE.
Example
extend vr_xbus_bus_monitor_u {
on transfer_end {
System Verification Methodology
4-73
if transfer.read_write == WRITE {
reg_file.tx_data.update(0,%{transfer.data},{});
};
};
};
4.8.5.12 compare_and_update()
Purpose
Compare a specific register with given data.
Syntax
compare_and_update(addr : vr_ad_addr_t, expected_data : list of byte): bool
Parameters
address
expected_data
Description
This method compares a specific register with given data. Typically, you use the address 0, meaning
comparing from the beginning of the register. You can also use non-zero offsets if the register is in a
register file. Comparison is done only for the number of bytes given as the expected data. An error is
issued only for a mismatch in a register whose compare_mask bits are set. Whenever this method is
called, the pre_access() and post_access() hooks are activated, and coverage of registers is collected
with direction READ.
Example
extend vr_xbus_bus_monitor_u {
on transfer_end {
if transfer.read_write == READ {
compute p_xbus_env.addr_map.compare_and_update(transfer.addr,
%{transfer.data});
};
};
};
4-74
4.8.5.13 post_access()
Purpose
Implement side effects.
Syntax
post_access()
Description
This method is a user hook for implementing features like clear on read and clear on write.
Example
extend EX_REGS_TX_MODE vr_ad_reg {
// Clear on Write
post_access(direction : vr_ad_rw_t) is {
if direction == write{
write_reg_raw(0x0,0x0,0xffffffff);
};
};
};
See Also
Implementing Side Effects on page 4-31
vr_ad_reg Fields
Field
Description
kind: vr_ad_reg_kind
Register kind
4-75
Table 4-16
vr_ad_reg Fields
Field
Description
static_info:
vr_ad_reg_static_info
Table 4-17
vr_ad_reg Methods
Method
Description
compare_and_update(addr :
vr_ad_addr_t, data : list of byte): bool
get_indirect_addr(map : vr_ad_map)
get_packing_option(): pack_options
get_size(): int(bits: *)
post_access(direction : vr_ad_rw_t)
read_reg_rawval(): vr_ad_data_t
read_reg_val(): vr_ad_data_t
read_reg_val_backdoor():
vr_ad_data_t:
uint(bits:VR_AD_DATA_WIDTH)
reset()
set_static_info()
set_write_mask(
wmask : vr_ad_data_t,
map : vr_ad_group)
update(addr : vr_ad_addr_t,
data : list of byte,
mask : list of byte)
4-76
Table 4-17
Method
Description
write_reg_rawval(data :
vr_ad_data_t)
write_reg_val(data : vr_ad_data_t)
write_reg_val_backdoor(data :
vr_ad_data_t:
uint(bits:VR_AD_DATA_WIDTH))
4.8.6
vr_ad_reg_static_info
The vr_ad_reg_static_info struct contains register attributes that should not be generated on the fly when
doing register operations. The static information struct is created only when you add a register to a
register file or to an address map (by add_with_offset()). You can set some fields using
set_static_info() or the reg_def macro. Other fields are automatically updated by the e model.
Table 4-18
vr_ad_reg_static_info Fields
Field
Description
backdoor_path: string
compare_mask: vr_ad_data_t
cur_value: vr_ad_data_t
description: string
direction: vr_ad_rw_t
prev_value: vr_ad_data_t
reset_value: vr_ad_data_t
rmask: vr_ad_data_t
size: uint
4-77
Table 4-18
Field
Description
wmask: vr_ad_data_t
4.8.7
vr_ad_reg_file
The register file contains a list of registers (or register files) referenced by vr_ad_node. The
implementation assumes that a register can be accessed through several addresses, and therefore the
address is stored in address nodes rather than the register itself.
Figure 4-1
Register File
ad_nodes : list of vr_ad_node
offset 0x0
ad_item
vr_ad_tx_mode
register
offset 0x1
ad_item
vr_ad_tx_data
register
offset 0x2
ad_item
vr_ad_rx_mode
register
offset 0x3
ad_item
vr_ad_rx_data
register
Each vr_ad_node contains an address and a reference to a register, a register file, or a memory block.
Table 4-19
vr_ad_node Fields
Field
Description
ad_item: vr_ad_base
offset: vr_ad_addr_t
size: uint
4-78
All offsets are relative to the base address of the containing object. The absolute address is calculated by
totaling all offsets from the addressable item to the top address map.
Table 4-20
Method
Operation
For a list of all fields and methods of vr_ad_reg_file, see Fields and Methods of vr_ad_reg_file on page
4-80.
4.8.7.1
add_with_offset()
Purpose
Create an address node with a reference to the register and hold the address of the item.
Syntax
add_with_offset(offset : vr_ad_addr_t, ad_item : vr_ad_base)
Parameters
offset
ad_item
Description
This method creates an address node with a reference to the register and holds the address of the item.
The parameters for this method are the offset of the register and the register to be added. The reg_def
macro uses this method to automatically add a register to a register file. Explicitly use this method when
a register file contains multiple instances of a register type.
Example
extend XCORE vr_ad_reg_file {
%tx_mode_regs[4] : list of EX_REGS_TX_MODE vr_ad_reg;
post_generate() is also {
for each (reg) in tx_mode_regs {
add_with_offset(index*4,reg);
System Verification Methodology
4-79
};
};
};
4.8.7.2
vr_ad_reg_file Fields
Field
Description
addressing_width_in_bytes
has_checks
has_coverage
indirect_seq_name:
vr_ad_sequence_kind
kind: vr_ad_reg_file_kind
offset: vr_ad_addr_t
size: vr_ad_addr_t
Table 4-22
vr_ad_reg_file Methods
Method
Description
add_with_offset(offset : vr_ad_addr_t,
ad_item : vr_ad_base)
compare_and_update(addr :
vr_ad_addr_t, data : list of byte): bool
Compares the given data read on the bus with the e model.
Updates the e model if needed
4-80
Table 4-22
Method
Description
get_address_by_instance(ad_item :
vr_ad_base): list of vr_ad_addr_t
get_all_nodes_in_range(min_addr :
vr_ad_addr_t, max_addr :
vr_ad_addr_t): list of vr_ad_node
get_all_reg_files(): list of
vr_ad_reg_file
get_by_address(addr : vr_ad_addr_t):
vr_ad_base
get_reg_by_address(addr :
vr_ad_addr_t): vr_ad_reg
get_reg_by_kind(kind :
vr_ad_reg_kind): vr_ad_reg
get_reg_file_by_address(addr :
vr_ad_addr_t): vr_ad_reg_file
get_reg_file_by_kind(kind :
vr_ad_reg_file_kind): list of
vr_ad_reg_file
get_reg_files_by_kind(kind :
vr_ad_reg_file_kind): list of
vr_ad_reg_file
get_regs_by_kind(kind :
vr_ad_reg_kind): list of vr_ad_reg
4-81
Table 4-22
Method
Description
read_backdoor(offset : vr_ad_addr_t,
len : uint) : list of byte
remove(ad_item : vr_ad_base)
reset()
report()
set_indirect_seq_name(kind :
vr_ad_sequence_kind)
write_backdoor(offset : vr_ad_addr_t,
data : list of byte)
4.8.8
vr_ad_map
The address map has all of the functionality of a register file (see vr_ad_reg_file on page 4-78). In
addition, it has address management capabilities (allocation and release of address ranges). The address
management capabilities are discussed in Chapter 4 The Register and Memory Model.
The address map contains a list of register files and memory banks with their absolute addresses. The
address map also manages the unmapped (indirect) register maps, even though they are not mapped in
the address space.
Table 4-23
Method
Operation
4-82
Table 4-23
Method
Operation
For a full list of all register-related fields and methods of vr_ad_map, see Fields and Methods of
vr_ad_map on page 4-88.
4.8.8.1
add_with_offset()
Purpose
Add an addressable item to the address map at a specific offset.
Syntax
add_with_offset(offset : vr_ad_addr_t, ad_item : vr_ad_base)
Parameters
offset
ad_item
Description
This method adds an addressable item to the address map at the specified offset. It allocates the required
address space from the free_addrs set and creates an address node with the specified address and a
reference to the addressable item. If the method fails to add the addressable item, an error message is
issued.
Example
extend ex_c_bus_env {
xcore_reg_file : XCORE vr_ad_reg_file;
post_generate() is also {
// Add a reg-file to the address map
addr_map.add_with_offset(0x0,xcore_reg_file);
};
4-83
};
4.8.8.2
add_to_addr_map()
Purpose
Add an addressable item to the address map.
Syntax
add_to_addr_map(min_addr : vr_ad_addr_t, max_addr : vr_ad_addr_t,
ad_item : vr_ad_base, alignment : uint): vr_ad_addr_t
Parameters
min_addr
max_addr
ad_item
alignment
Alignment of the allocated address space. For example, 2 aligns to halfword and
4 aligns to word
Description
This method adds an addressable item to the address map (similar to add_with_offset()). It lets you
specify the alignment and a range from which to allocate the addresses for the addressable item. The
allocated size will be the size of the addressable item. It creates an address node with the allocated base
address and a reference to the addressable item. The returned value is the allocated base address. If
allocation fails, the returned value is UNDEF.
Example
extend ex_c_bus_env {
xcore_reg_file : XCORE vr_ad_reg_file;
!xcore_base_address : vr_ad_addr_t;
post_generate() is also {
// Add a reg-file to the address map
xcore_base_address=
addr_map.add_to_addr_map(0x0,0x1000,xcore_reg_file,4);
};
};
4-84
4.8.8.3
add_unmapped_item()
Purpose
Add a register file to the address map.
Syntax
add_unmapped_item(reg_file : vr_ad_reg_file)
Parameters
reg_file
Description
This method adds the reg_file and its registers into the reg_list and reg_file_list of the address map lists.
All unmapped register files must be registered in the address map by using this method.
Example
extend ex_c_bus_env {
ycore_reg_file : YCORE vr_ad_reg_file;
post_generate() is also {
// Add the unmapped reg file to the address map
addr_map.add_unmapped_item(ycore_reg_file);
};
};
4.8.8.4
update()
Purpose
Update the e model.
Syntax
update(addr : vr_ad_addr_t, data : list of byte, mask : list of byte)
4-85
Parameters
addr
data
mask
List of byte to be used as masks. Each element in the list masks the equivalent element
in the data list. 0x0 masks out a byte, and 0xff writes the data as is. An empty list {}
indicates that no masking is applied
Description
This method updates the e model. For example, it can update on the basis of information gathered by the
monitor. If you update a register and you specify a mask, the mask will be anded with the wmask of the
register. Whenever this method is called, the pre_access() and post_access() hooks are activated, and
coverage of registers is collected with direction WRITE.
Example
extend vr_xbus_bus_monitor_u {
on transfer_end {
if transfer.read_write == WRITE {
reg_file.update(transfer.addr,%{transfer.data},
{0x0;0xff;0xff;0xff});
// The update() mask is:
0xffffff00
// Wmask can be, for example:
0x00ffffff
// The actual mask that will be used is: 0x00ffff00
};
};
};
4.8.8.5
fetch()
Purpose
Read a specific number of bytes from the e model starting from a specific address.
Syntax
fetch(addr :vr_ad_addr_t, size : vr_ad_addr_t): list of byte
4-86
Parameters
addr
size
Description
This method reads the specified number of bytes from the e model starting from the specified address.
The address does not have to be aligned to the register boundaries. So, for example, it is legal to start
reading from a second byte of a register. The method recursively calls the fetch methods of the
addressable items inside the address map until it gets to a register that returns its packed value. If there
is a gap between two registers, the gap will be padded with 0s. Whenever this method is called, the
pre_access() and post_access() hooks are activated, and coverage of registers is collected with direction
READ.
Example
extend vr_xbus_bus_monitor_u {
on transfer_end {
if transfer.read_write == READ{
read_data = p_xbus_env.addr_map.fetch(transfer.addr,4);
};
};
};
4.8.8.6
compare_and_update()
Purpose
Compare the data captured on the bus with the data that is stored in the e reference model.
Syntax
compare_and_update(addr : vr_ad_addr_t, expected_data : list of byte): bool
Parameters
address
expected_data
4-87
Description
This method can be used to automatically compare the data captured on the bus with the data that is
stored in the e reference model. The comparison is done only for the number of bytes given as the
expected data. It does not have to be aligned to the register boundaries. An error is issued only for a
mismatch in a register whose compare_mask bits are set. Whenever this method is called, the
pre_access() and post_access() hooks are activated, and coverage of registers is collected with direction
READ.
Example
extend vr_xbus_bus_monitor_u {
on transfer_end {
if transfer.read_write == READ {
compute p_xbus_env.addr_map.compare_and_update(transfer.addr,
%{transfer.data});
};
};
};
4.8.8.7
Field
Description
addr_alignment: uint
free_addrs: vr_ad_set
kind: vr_ad_map_kind
occupied_addrs: vr_ad_set
4-88
Table 4-24
Field
Description
size: vr_ad_addr_t
Table 4-25
Method
Description
add_to_addr_map(addr : vr_ad_addr_t,
ad_item : vr_ad_base, alignment : uint):
vr_ad_addr_t
add_unmapped_item(reg_file :
vr_ad_reg_file)
add_with_offset(offset : vr_ad_addr_t,
item : vr_ad_base)
compare_and_update(addr :
vr_ad_addr_t, data : list of byte): bool
get_address_by_instance(ad_item :
vr_ad_base): list of vr_ad_addr_t
get_all_nodes_in_range(min_addr :
vr_ad_addr_t, max_addr : vr_ad_addr_t):
list of vr_ad_node
get_by_address(addr : vr_ad_addr_t):
vr_ad_base
4-89
Table 4-25
Method
Description
get_mem_by_address(addr :
vr_ad_addr_t)
get_reg_by_address(addr : vr_ad_addr_t):
vr_ad_reg
get_reg_by_kind(kind : vr_ad_reg_kind):
vr_ad_reg
get_reg_file_by_address(addr :
vr_ad_addr_t): vr_ad_reg_file
get_reg_file_by_kind(kind :
vr_ad_reg_file_kind): list of vr_ad_reg_file
get_reg_files_by_kind(kind :
vr_ad_reg_file_kind): list of vr_ad_reg_file
get_regs_by_kind(kind : vr_ad_reg_kind):
list of vr_ad_reg
release_item(item : vr_ad_base)
remove(ad_item : vr_ad_base)
reset()
write_backdoor(offset : vr_ad_addr_t,
data : list of byte)
See Also
Address Management (Sets) on page 4-96
4-90
4.8.9
vr_ad_mem
Method
Operation
4.8.9.1
readmemh()
Purpose
Load values into memory.
Syntax
readmemh(file_name : string, start_addr : vr_ad_addr_t, end_addr : vr_ad_addr_t,
file_start_addr : vr_ad_addr_t, file_end_addr : vr_ad_addr_t)
Parameters
file_name
start_addr
Data read from file will be written to memory starting from this
address. Typically, the start address is 0.
end_addr
file_start_addr
file_end_addr
Description
This method loads values from a file into the memory. It is equivalent to the readmemh Verilog task or
VHDL procedure. The format of the file must be according to the standard supported by Verilog or
VHDL. In general, it will have the following structure:
4-91
Example
extend ex_c_bus_env {
run() is also {
mem.readmemh("data_file.txt",0,1000,0,1000);
};
};
4.8.9.2
dumpmemh()
Purpose
Dump the memory content to file.
Syntax
dumpmemh(file_name : string, start_addr : vr_ad_addr_t, end_addr : vr_ad_addr_t)
Parameters
file_name
start_addr
end_addr
Description
This method dumps the memory content to file. It is equivalent to the dumpmemh Verilog task or VHDL
procedure. The format of the file must be according to the standard supported by Verilog or VHDL (see
readmemh() on page 4-91).
Example
extend ex_c_bus_env {
finalize() is also {
4-92
mem.dumpmemh("data_file.txt",0,100);
};
};
4.8.9.3
get_data()
Purpose
Customize behavior when reading an uninitialized area in the memory.
Syntax
get_data(addr :vr_ad_addr_t, size : vr_ad_addr_t): list of byte
Parameters
addr
size
Description
This method is called each time an uninitialized area is read in the memory. By default, the returned data
is determined according to the policy in read_default_value. The size of the returned data is according
to the parameter size. If you need a different behavior, you can override this method and implement your
own logic. If you override this method, you must do it only under a specific memory subtype.
Example
extend EX_MEM vr_ad_mem {
get_data(addr : vr_ad_addr_t, size : uint) : list of byte is only {
result.resize(size,TRUE,0,TRUE);
for each (b) in result {
b = addr + index;
};
};
};
4-93
Table 4-27
vr_ad_mem Fields
Field
Description
addressing_width_in_bytes: uint
backdoor_path: string
has_checks: bool
kind: vr_ad_mem_kind
Memory kind
read_default_value: [ZERO,
RANDOM]
size: vr_ad_addr_t
Table 4-28
vr_ad_mem Methods
Method
Description
write_backdoor(offset :
vr_ad_addr_t, data : list of byte)
read_backdoor(offset :
vr_ad_addr_t, len : uint) : list of byte
dumpmemh(file_name : string,
start_addr : vr_ad_addr_t,
end_addr : vr_ad_addr_t)
dumpmemb(file_name : string,
start_addr : vr_ad_addr_t,
end_addr : vr_ad_addr_t)
4-94
Table 4-28
Method
Description
readmemh(file_name : string,
file_type : string,
start_addr : vr_ad_addr_t,
end_addr : vr_ad_addr_t,
file_start_addr : vr_ad_addr_t,
file_end_addr : vr_ad_addr_t)
readmemb(file_name : string,
file_type : string,
start_addr : vr_ad_addr_t,
end_addr : vr_ad_addr_t,
file_start_addr : vr_ad_addr_t,
file_end_addr : vr_ad_addr_t)
4.8.10
vr_ad_sequence_driver
The RSD is a dedicated sequence driver for register and memory operations.
vr_ad_sequence_driver Fields
Field
Description
addr_map : vr_ad_map
default_bfm_sd :
any_sequence_driver
bd_write_to_addr_map : bool
bd_read_from_addr_map : bool
4-95
Table 4-30
vr_ad_sequence_driver Methods
Method
Description
add_space(space : vr_ad_group,
name : vr_ad_space_kind)
4.9
The address map (vr_ad_map) provides address management functionality. It lets you define a name
for a group of addresses and then allocate generated addresses from each group of addresses.
For example, assume that you have a slave DUT that responds to several nonconsecutive addresses as
follows [0x0..0x500, 0x2000..0x3000, 0x3500..0x7000].
Figure 4-1
slave0_set
0x0
3500..7000
2000..3000
0..500
To randomly access the slave DUT, you would want to generate random areas of various sizes inside the
slave DUT address space. A possible request might be to generate a 50-byte area but not beyond address
0x6000. The address map lets you define a set of slave DUT addresses and then allocate random regions
in it.
To define an address set:
1.
2.
Use the add_segment() method to initialize the new field with the desired regions.
For example, you could add the field slave0_set to your address map and then initialize it with addresses
[0x0..0x500, 0x2000..0x3000, 0x3500..0x7000] as follows:
extend MY_MAP vr_ad_map {
slave0_set : vr_ad_set;
post_generate() is also {
// Initialize slave0_set
add_segment(slave0_set,0x0,0x500);
add_segment(slave0_set,0x2000,0x3000);
add_segment(slave0_set,0x3500,0x7000);
};
};
4-96
The slave0_set is now initialized with regions: [ 0..0x500, 0x2000..0x3000, 0x3500..0x7000 ]. You can
now allocate random regions from it. Allocation removes the allocated region from the set, making it
unavailable for subsequent allocations. If, for example, you allocated a 40-byte region [0x52..0x91],
then:
slave0_set before allocation: [0
..
0x500, 0x2000..0x3000,
0x3500..0x7000]
slave0_set after allocation : [0..0x51, 0x92..0x500, 0x2000..0x3000,
0x3500..0x7000]
4-97
Note Address maps use predefined sets to allocate addresses for the addressable items. Each time you
add a register file to an address map (with add_with_offset() or add_to_addr_map()), the address map
allocates a region from the free_addrs set and adds a region to the occupied_addrs set:
extend vr_ad_map {
free_addrs
: vr_ad_set;
occupied_addrs : vr_ad_set;
};
Table 4-31 describes the main address set operations.
Table 4-31
Method
Operation
For a full list of the memory-management methods, see Memory-Management Methods on page 4-102.
4.9.1
add_segment()
Purpose
Initialize a set, and add a specific region to it.
Syntax
add_segment(set: vr_ad_set, min_addr: vr_ad_addr_t, max_addr: vr_ad_addr_t)
Parameters
set
min_addr
max_addr
4-98
Description
This method initializes a set and adds the specified region to it. You can add several segments to a set. If
you add contiguous segments, they are automatically combined into one segment in the set.
Example
extend EX_MAP vr_ad_map {
slave0_set : vr_ad_set;
post_generate() is also {
add_segment(slave0_set,0x0,0x150);
add_segment(slave0_set,0x220,0x350);
};
};
4.9.2
alloc_from_set()
Purpose
Allocate a region in a set.
Syntax
alloc_from_set(set: vr_ad_set, min_addr: vr_ad_addr_t, max_addr: vr_ad_addr_t, min_size:
vr_ad_addr_t, max_size: vr_ad_addr_t, alignment: uint): vr_ad_set
Parameters
set
min_addr
max_addr
min_size
max_size
alignment
4-99
Description
This method allocates a region in a set. The allocation is done randomly between the specified range of
min_addr and max_addr. The size of the region is also randomly generated between min_size and
max_size. The returned value is a new set created from the allocated region. You can get the region base
address and size by the methods vr_ad_set.get_address() and vr_ad_set.get_size(). To allocate the
whole address space (the range 0x0 to 0xffffffff, by default definitions), set min_size and max_size to
zero.
Example
extend MAIN vr_ad_sequence {
body() @driver.clock is only {
// Allocate 10 - 50 bytes between addresses 0 - 0x100 using
// alignment 2
var allocated_set : vr_ad_set =
map.alloc_from_set(map.slave0_set,0,0x100,10,50,2);
var allocated_addr : vr_ad_addr_t = allocated_set.get_address();
};
};
4.9.3
dealloc_to_set()
Purpose
Deallocate a set.
Syntax
dealloc_to_set(dest_set: vr_ad_set, alloc_set: vr_ad_set)
Parameters
dest_set
alloc_set
Set to be deallocated
Description
This method deallocates a set that was previously allocated by the method alloc_from_set().
4-100
Example
extend MAIN vr_ad_sequence {
body() @driver.clock is only {
var allocated_set :=
map.alloc_from_set(map.slave0_set,0,0x1000,10,50,2);
// Do something
map.deallocate_to_set(map.slave0_set, allocated_set);
};
};
4.9.4
lock_item()
Purpose
Prevent other sequences from accessing an address.
Syntax
lock_item(ad_item: vr_ad_base): bool
Parameters
ad_item
Description
When you have a sequence that needs exclusive access to a register or a register file, you do not want
other sequences to interfere as long as the sequence is not finished. The address map contains a
predefined set for that purpose. The method lock_item() removes the relevant addresses from that
predefined set, preventing other sequences from locking the same addresses.
Example
extend MAIN vr_ad_sequence {
body() @driver.clock is only {
var my_reg = driver.addr_map.get_reg_by_address(0x100);
var item_is_locked = lock_item(my_reg);
if item_is_locked {
// Do something
};
};
4-101
};
4.9.5
release_item()
Purpose
Release a locked address.
Syntax
release_item(ad_item: vr_ad_base)
Parameters
ad_item
Description
This method releases an addressable item that was previously locked by lock_item().
Example
extend MAIN vr_ad_sequence {
body() @driver.clock is only {
var my_reg = driver.addr_map.get_reg_by_address(0x100);
var item_is_locked = lock_item(my_reg);
if item_is_locked {
// Do something
release_item(my_reg);
};
};
4.9.6
Memory-Management Methods
4-102
Table 4-32
Method
Description
add_unmapped_item(reg_file: vr_ad_reg_file)
release_item(item: vr_ad_base)
Table 4-33
vr_ad_set Methods
Method
Description
get_address()
get_size()
set_policy(policy : vr_ad_set_policy_t)
4-103
Table 4-33
Method
Description
segments_to_list()
For an example of AHB eVC sequences that use sets, see vr_ad/examples/vr_ad_ahb_sets.e.
At the Specman> prompt in Specview, issue the show map -win command.
Or:
Top-Level Address Map Page on page 4-105 Top-level content of the address map
4-104
4.10.1
Serial number
Map
Top-Level
Button that opens the Top-Level Address Map Page on page 4-105
Detailed
Button that opens the Detailed Address Map Page on page 4-106
Figure 4-2
4.10.2
The top-level address map page lists the highest level of hierarchy of the address map.
The table, shown in Figure 4-3, contains two columns:
Address
Item
4-105
Figure 4-3
4.10.3
The address map page displays a table of all addressable items (registers, register files, and memory
banks) of the selected address map.
The table, shown in Figure 4-4, contains five columns:
Index
Serial number
Address
Item
Value
Stripe Chart
Opens a page with the content of the item shown as a stripe chart.
In case of hierarchical addressable items (for example, a register file containing a register or another
register file), all items are displayed.
4-106
Figure 4-4
4.10.4
The Register Files page displays a table of all register files related to the address map. This list contains
mapped and unmapped register files. The columns are the same as in the address map page.
Figure 4-5
4-107
4.10.5
The Address Sets page shows all memory sets instantiated under the address map. The table, shown in
Figure 4-6, contains two columns:
Name
Content
Figure 4-6
4.10.6
The Register File page displays the content of the register file in a stripe chart view.
4-108
Figure 4-7
4.10.7
The Seq button on the toolbar of address map pages opens a stripe chart window for the corresponding
RSD sequences. The Stripe Chart page shows all register sequences created during the simulation.
4-109
Figure 4-8
4-110
4.11.1
show map
Purpose
Produce address map reports.
Syntax
sh[ow] map [map-index] [-win]
Usage
Four variations of the command are provided:
show map
Produces a report for the specified address map. The index is taken
from the show map command.
4.11.2
trace ad alloc
Purpose
Turn on the trace messages from address allocation and deallocation.
Syntax
trace ad al[loc]
Usage
The following vr_ad_map methods produce trace messages after the trace command is issued:
alloc_from_set()
dealloc_to_set()
dealloc_addr_to_set()
4-111
4-112
TLM Verification
5.1
Overview
There are three modes of connection between Specman Elite and external agents (simulators, SystemC,
and so on).
Table 5-1
Mode
Description
Cycle accurate
This is the typical mode for working with simulators. In this mode,
Specman Elite monitors and reacts to changes that occur every
clock cycle. This is the most accurate connection mode, but it is
also the most time-consuming connection mode.
5-1
TLM Verification
TLM System eVC
Table 5-1
Mode
Description
Decoupled timing
This connection mode lets you divide the environment into levels
that run according to different clocks. For example, with the
eCelerator, you can accelerate the low level of your environment
and still run high-level sequences.
Transaction-level modeling
(TLM)
5.2
The only parts of the verification environment that are dependant on the abstraction level are the lower
level BFM and monitor. For interface eVCs (vr_xbus and vr_xserial from the eRM package), these
are the only units that must be modified. For details on how to do that, see TLM Mode on page 5-3.
5-2
TLM Verification
TLM Mode
For module and system eVCs (XCore, QSoC, XBridge, and RSoC in the sVM package), only a few
changes are required. The system eVC monitor is based on events and data items emitted and collected
by the monitors in the interface eVCs. The system monitor does not care how those data items were
collected. The required changes in the system eVC are:
The configuration file must define the abstraction level for each interface eVC.
extend vr_xbus_env_u {
keep abstraction_level == TRANSFER;
};
Events and checks that depend on internal information must be modified. For example, the XCore
eVC has some events and checks based on an overflow indication from the DUT. The TLM model
of the XCore does not have such indications, so the overflow event is overridden when in TLM mode.
5.3
TLM Mode
Adding TLM functionality requires definition of a new type to control the abstraction level of the device
under test. For example:
type vr_xbus_abstraction_level_t : [SIGNAL, TRANSFER];
Units that must be subtyped on the basis of this type are the BFMs and monitors that get the additional
TLM functionality. Sometimes you also must subtype the env and agent units to propagate the value (for
example, when the agent controls the generation of the BFM and monitor under it).
5.3.1
TLM Wrapper
As each TLM model might supply a different API, it is better not to connect the eVC directly to the
model, but rather to create a wrapper between the BFM/Monitor and the model API. When a model has
a different API, the verification engineer can extend the wrapper and add data conversion.
For example, if the eVC BFM expects to get a response but the model does not implement that, the
wrapper could create a fake response and pass it to the eVC BFM.
For examples of wrapper code, see TLM BFM on page 5-3 and TLM Monitor on page 5-4.
5.3.2
TLM BFM
When verifying a TLM model, the injection mechanism becomes simple. The BFM does not have to
implement the bus protocol. It only has to transport the transfer to the model.
Following are some parts of the vr_xbus_tlm_master_bfm.e file that describe how to write a TLM BFM
and connect it to the SystemC model.
5-3
TLM Verification
TLM Monitor
The BFM has an instance of the output port of the transport method.
transport_transfer : out method_port of vr_xbus_transport_transfer is
instance;
The BFM driving TCM is extended so that instead of performing the bus protocol it passes the transfer
to the wrapper.
private drive_transfer(t : MASTER vr_xbus_trans_s) @synch.clock_rise is {
var cur_resp : vr_xbus_tlm_response_s;
//..
cur_resp = transport_transfer_wrapper(t);
//...
};
The wrapper transports the transfer to the model via the output method port.
transport_transfer_wrapper(t : MASTER vr_xbus_trans_s):
vr_xbus_tlm_response_s @synch.clock_rise is {
result = transport_transfer$(t);
return result;
};
The BFM output method port is bound to the SystemC model (bound to external), and its hdl_path is
constrained to the model API method name.
extend TRANSFER MASTER vr_xbus_bfm_u {
connect_ports() is also {
do_bind(transport_transfer, external);
};
};
extend TRANSFER MASTER vr_xbus_bfm_u {
keep transport_transfer.hdl_path() ==
"~/top->Master0.xbus_tlm_master_trans";
};
5.3.3
TLM Monitor
The traffic in the SystemC model is implemented using the methods of the various entities, unlike RTL
models where there are shared signals that can be monitored. For the eVC monitor to know about passed
traffic, it must get the required information from the SystemC model. The eVC bus monitors implement
5-4
TLM Verification
TLM Monitor
methods that get a new data item, analyze it, do required updates to the env, emit relevant events, and so
on. The monitors have an instance of an input method port for these methods. The SystemC bus must
call these update methods after each transfer for the monitor to work.
The eVC user guide should list the API provided by the monitor so that the verification engineer can
connect the model methods to the appropriate methods and events in the eVC.
With the various Specman-C interface capabilities, other monitoring techniques can be used, based on
the API provided by the SystemC model. For example, the SystemC channel can provide events
indicating activities on the bus.
Following are some parts of the vr_xbus_bus_monitor_tlm.e file that describe how to write a TLM
monitor and connect it to the SystemC model.
A method_type is declared.
method_type vr_xbus_log_new_transfer(transfer : MONITOR vr_xbus_trans_s,
response: vr_xbus_tlm_response);
The eVC monitor has an instance of an input method port of the logging method. This instance is a
wrapper. It gets the new transfer from the SystemC model and passes it to the monitors main logging
method.
log_new_transfer_wrapper : in method_port of vr_xbus_log_new_transfer
is instance;
log_new_transfer_wrapper(new_transfer : MONITOR vr_xbus_trans_s,
new_response: vr_xbus_tlm_response_s) is {
log_new_transfer(new_transfer, new_response);
//...
};
The eVC monitor defines and implements the main logging method.
private log_new_transfer(new_transfer : MONITOR vr_xbus_trans_s,
new_response: vr_xbus_tlm_response_s) is {
transfer = new_transfer;
//...
};
5-5
TLM Verification
Interfacing with SystemC
The monitor input method port is bound to the SystemC model (bound to external), and its hdl_path is
constrained to the model API method name.
extend TRANSFER vr_xbus_bus_monitor_u {
keep log_new_transfer_wrapper.hdl_path() == "xbus_log_new_transfer";
};
extend TRANSFER vr_xbus_bus_monitor_u {
connect_ports() is also {
do_bind(log_new_transfer_wrapper, external);
};
};
5.4
For interfacing with SystemC, each interface eVC must include a convertor file that enables conversion
of data items from e to C and vice versa. An example of such a file is vr_xbus/sysc/conv_transfers.h. For
more information on writing convertors, see vr_sc2e/docs/sc2e_user_spec.pdf under svm_lib.
5.5
To work with SystemC, you must make some modifications to the default configuration file. Following
are some parts of the vr_xcore/tlm_sve/vr_xcore_sve_config.e file that describe the necessary changes.
Define the simulator as SYSTEMC.
keep agent() == "SYSTEMC";
Add required code to the stub file, specman.cpp. It must include the model header file and the
convertors. It must also define a pointer to the top module in SystemC.
keep agent_code() == {"#include 'xcore_top.h'";
"#include 'conv_transfers.h'";
"#include 'conv_frames.h'";
"using namespace xbus;";
"using namespace xserial;";
"using namespace xcore;";
"extern Xcore_Top *top;" };
5-6
TLM Verification
Modifying the SystemC Model
See Also
vr_xbus_tlm/tlm_sve/vr_xbus_tlm_config.e
5.6
When integrating your SystemC model with your e code, you must make some modifications to the
SystemC code.
For example, these changes were made to the XCore SystemC model:
Changes to sc_main(): the sVM golden eVCs work in master mode. This means that the SystemC
design is fully responsible for time management and for running the simulation. In consequence, the
following changes were made to the SystemC code:
src/snsc_user was added to the SystemC model so that it can call Specman methods.
Before starting the run, sc_main() must activate Specman Elite by calling
specman_init(started_mode).
sc_main() also calls specman_command() both before and after the run to enable issuing of
pre-run commands (for example, to load the test file) and post-run commands (for example, to
perform post-run analysis).
Data injection is now controlled by the eVC BFM. The main SC_THREAD supplied with the
SystemC model is no longer required, and the file that contains the SystemC thread is no longer
included in the verification environment.
<iostream>
<string>
"systemc.h"
"xbus_top.h"
"src/snsc_user.h"
5-7
TLM Verification
Modifying the SystemC Model
When running in slave mode, the run is initiated and controlled by Specman Elite. Therefore, sc_main()
must not issue sc_start(). For example:
#include
#include
#include
#include
<iostream>
<string>
"systemc.h"
"xbus_top.h"
#include "src/snsc_user.h"
using namespace xbus;
xbus_top* top;
int sc_main(int argc, char* argv[])
{
// Bus
top = new xbus_top("Top");
// Slave mode - Specman controls the run.
// Run must not be started by SystemC
return 0;
}
5-8
TLM Verification
Open TLM Verification Issues
5.7
The TLM eVCs are at a beta level of maturity. Some methodology is still under consideration.
This section contains:
5.7.1
The eVC BFM calls the method implemented in the TLM model with the next data to send. Table 5-2
describes the different ways this can be done.
Table 5-2
Methodology
eVC BFM waits
for the activity to
end
Implementation
SystemC
value-returning
output method
port
SystemC void
output method
port
SystemC events
Description
The eVC BFM calls the TLM model method and waits
for its returned value. This methodology is
implemented in the sVM eVCs. For example:
cur_resp =
transport_transfer$(cur_transfer);
5-9
TLM Verification
TLM Monitor Methodology
5.7.2
In TLM mode, the eVC monitor gets all required information from the TLM model. Table 5-3 describes
the different ways this can be done. In each case, the TLM model must implement some API for the eVC
to use.
Table 5-3
Methodology
TLM model controls
monitoring
Implementation
Specman Elite
input method
port
Description
The TLM model calls a method of the eVC
monitor, passing to it all required data (for
example, the transfer struct after each transfer).
Note If you want to run the TLM model in
standalone mode, you must include a dummy
monitor method to stand in for the eVC method.
eVC controls
monitoring
SystemC output
method port
SystemC
non-blocking
method
Output method
port
SystemC events
In the sVM eVCs, monitoring is implemented using input method ports. This is because currently it is
the safest methodology for synchronization. When working in PV mode, you must ensure that events are
handled in the eVC in the same order as they are emitted by the model. If the model supports the output
method port API, you can use the eVC-controlled approach, but make sure that the eVC monitors call
the TLM model in the same order as the events are emitted.
The advantage of letting the eVC control monitoring is that you can easily disable monitoring by
changing the eVC configuration.
Whichever technique is chosen, the eVC manual should document the supplied API so that the
verification engineer can connect the model methods to the appropriate methods and events in the eVC.
5-10
TLM Verification
TLM Monitor Methodology
Example 1
SystemC
The XBus has a monitor that is sensitive to the event transaction_complete. When this event is emitted,
XBus::Monitor() is activated. XBus::Monitor() calls a method implemented in the eVC.
// The constructor of Xbus
XBus(sc_module_name name)
: sc_module(name)
{
SC_THREAD(Monitor);
sensitive << transaction_complete;
}
// The XBus::Monitor() implementation
void XBus::Monitor(){
while(true){
wait(); // for transaction_complete notification
xbus_log_new_transfer(cur_req, cur_rsp);
};
};
eVC code
The eVC monitor has an input method port. It gets called by the wrapper, which has its own
implementation of this method. The wrappers method port HDL path matches the name of the method
called by the model.
// The eVC monitor method
log_new_transfer(transfer : MONITOR vr_xbus_trans_s,
response: vr_xbus_tlm_response) is {
current_transfer = transfer;
current_response = response;
update_transfer();
};
// The wrapper - connecting to Model, bridging from model to monitor
log_transfer_from_model : in method_port of vr_xbus_log_new_transfer
is instance;
log_transfer_to_monitor : out method_port of vr_xbus_log_new_transfer
is instance;
keep soft log_transfer_from_model.hdl_path() == "xbus_log_transfer";
log_transfer_from_model(transfer : MONITOR vr_xbus_trans_s,
response: vr_xbus_tlm_response_s) is {
log_transfer_to_monitor$(transfer, response);
5-11
TLM Verification
TLM Monitor Methodology
};
Example 2
SystemC
The SystemC model implements a transaction-completion indicating event and a method that returns the
current transfer and its response.
sc_event transaction_complete; // A master has completed a transaction
void xbus_tlm_mon_trans(xbus_tlm_request& req, xbus_tlm_response& rsp)
{
req = cur_req;
rsp = cur_rsp;
}
eVC
The eVC monitor uses the SystemC event as a sampling event of its main method. Upon event emission,
the eVC monitor calls the SystemC monitor method.
// Define an event and connect to SystemC event
package bus_transfer_end : in event_port is instance;
keep bus_transfer_end.hdl_path() == "transaction_complete";
// Define an output method port and connect to SystemC method
get_info_from_sc_bus : out method_port of xbus_tlm_transaction is instance;
keep get_info_from_sc_bus.hdl_path() == "xbus_tlm_mon_trans";
update_transfer() @bus_transfer_end$ is {
while TRUE {
// Get transfer and response:
get_info_from_sc_bus$(current_transfer, current_response);
//... Update data
};
};
Example 3
SystemC code
The SystemC model has a method that waits for the next transaction. It then returns the current
transaction and the response to it.
void xbus_tlm_mon_trans_blocking(xbus_tlm_request& req,
xbus_tlm_response& rsp) {
wait(transaction_complete);
5-12
TLM Verification
Multiple Instances of Input Method Ports
req = cur_req;
rsp = cur_rsp;
}
eVC code
The eVC calls the systemC monitor in a while TRUE loop.
poll_info_from_sc_bus : out method_port of vr_xbus_poll_info_from_sc
is instance;
keep soft poll_info_from_sc_bus.hdl_path() ==
"xbus_tlm_mon_trans_blocking";
poll_for_transfers() @sys.any is {
while TRUE {
poll_info_from_sc_bus$(current_transfer, current_response);
update_transfer();
};
};
5.7.3
When using input method ports, the HDL path of the eVC method is the method name used by the
SystemC code. This name is written to the stub header file with the Specman write stub command. If
there are multiple instances of the unit, there will be two methods with same name. That is illegal, and
the stub file will not compile.
In the XSerial eVC, there are two monitors: RX and TX. Both are instances of the same unit
(vr_xserial_monitor_u). The HDL path of the monitors input method must be defined so that there is
no duplication of names.
The solution in the XSerial eVC is adding an ID to each monitor. The integrator must verify that the IDs
of the agents in the SystemC model match the IDs of the eVC monitors.
eVC code
monitor_id : uint;
new_monitor_frame : in method_port of vr_xserial_log_new_frame
is instance;
keep soft new_monitor_frame.hdl_path() ==
append("xserial_log_new_frame", monitor_id);
SystemC code
void XSerialChannel::Monitor(){
System Verification Methodology
5-13
TLM Verification
Method Port Location
while(true){
wait();
if (id == 0) {
xserial_log_new_frame0(cur_frame);
};
if (id == 1) {
xserial_log_new_frame1(cur_frame);
};
};
};
5.7.4
The unit that implements the method (when you have an input method port)
The unit that uses the method (when you have an output method port)
A general unit at a higher level (typically, the env)
A signal map
In the XBus and XSerial eVCs, ports are instantiated in the unit that uses them: the output method port
instantiated in the BFMs and the input method port instantiated in the monitors.
In other packages, the output method ports are instantiated in the env unit.
5.7.5
When the TLM model runs in PV mode, pay special attention to synchronization:
Artificial time: If the whole simulation runs in one cycle, all Specman events have the same time
stamp. This causes ordering issues, false DUT errors, and meaningless test information. To avoid
these problems, the TLM BFM has a wait cycle before each transmission to the TLM model. If there
are 10 transfers in the run, the test will last 10 Specman ticks.
Use of events: When more than one event is emitted by the SystemC model, you risk a
synchronization problem among the various eVC monitors. The system eVC monitor uses events
emitted by the interface monitors. Correctness of the checks depends on the system monitor getting
the events in the same order emitted by the TLM SystemC model. But the order is not always
guaranteed. Events defined using event is are handled by the Specman Elite scheduler, and
5-14
TLM Verification
Multi-Level Environments
their order is not predictable. For example, in the following code, if events frame_started and
frame_ended are emitted at the same time (which can happen in PV mode), the order of the events
if_frame_started and if_frame_ended is not predictable:
event if_frame_started is cycle @interface.frame_started;
event if_frame_ended is cycle @interface.frame_end_ed;
5.7.6
Multi-Level Environments
In the sVM examples, the whole environment is either RTL or TLM. When verifying a mixed
environment, some modifications must be made. For example, consider a system with two devices
connected to a bus, with one device implemented in Verilog and the other in SystemC. At what level
should the bus monitor work? How can it serve both agents?
There are several approaches to these questions, depending on the eVC architecture and the complexity
of the protocol.
Note In all of the following cases, coverage collected via the bus monitor represents activity on both
RTL and TLM. To see specific coverage for each agent, look at the coverage collected via the agent
monitor.
5.7.6.1
A simplified solution is to instantiate the bus monitor several times, one for each level. Each agent
would be connected to the appropriate monitor.
5.7.6.2
Independent Agents
This approach requires each agent to be fully independent. Each agent has its own monitor. All agent
monitors monitor the bus. Each monitor is bound to the appropriate device through ports.
5.7.6.3
Bridge
This approach implements a bridge to connect several abstraction levels. The eVC is defined to work at
one level, and any part of the system that is implemented at another level is connected to the eVC with a
bridge.
5-15
TLM Verification
The XBus TLM Demo
5.8
The XBus TLM demo, implemented in vr_xbus_tlm/*, demonstrates a simple XBus TLM test. The
demo can run on NCSC and OSCI simulators. The scenario created by the demo is a sequence of 10
write transfers, each write followed by a read of the same address.
Note As the XBus eVC employs procedural binding, the demo requires Specman Elite 5.0 or higher.
To run the demo:
1.
(Only when working with the OSCI simulator) Define the environment variables: SYSTEMC and
SYSTEMC_LIB_DIR.
2.
Source Specman-home-dir/env.[c]sh.
3.
The demo script launches Specman Elite and SystemC, loads the test file
vr_xbus_tlm/tlm_sve/tests/vr_xbus_tlm_test, and issues the test command.
4.
5-16
TLM Verification
The XBus TLM Demo
Figure 5-1
5-17
TLM Verification
The XCore TLM Demo
5.9
The XCore TLM demo, implemented in vr_xcore/tlm_sve/*, demonstrates a simple XCore TLM test.
The demo can run on NCSC and OSCI simulators. The scenario created by the demo programs the
XCore, by writing its registers, to transmit 5 frames on the XSerial.
Note As the XCore eVC employs method ports, the demo requires Specman Elite 4.3 or higher.
To run the demo:
1.
(Only when working with the OSCI simulator) Define the environment variables: SYSTEMC and
SYSTEMC_LIB_DIR.
2.
Source Specman-home-dir/env.[c]sh.
3.
The demo script launches Specman Elite and SystemC, loads the test file
vr_xcore/tlm_sve/tests/vr_xcore_program_to_tx.e, and issues the test command.
4.
5-18
TLM Verification
The XCore TLM Demo
Figure 5-2
5-19
TLM Verification
The XCore TLM Demo
5-20
Hardware-Software
Co-Verification
Embedded systems contain some software as part of the DUT. This software must be verified with the
hardware to ensure proper interfacing and co-operation of the software with the hardware.
The first and most important task is verifying the low-level software drivers. As these are the software
methods that directly interface with the hardware, their co-operation must be verified thoroughly.
Occasionally additional levels of the software or even full software applications might be co-verified
with the hardware. In all cases, verification is based on a software eVC and the Generic Software
Adaptor (GSA).
The verification of software presents some additional challenges to the already complex task of
functional verification.
Interfacing with the software requires additional infrastructure such as the GSA.
The visibility of software internals is minimal, which makes monitoring difficult. There is no way
to generate events on method calls or value change without modifying the code.
Software requires many simulation cycles, limiting the simulation to a few seconds of real time. To
overcome this limitation, the software is often run on the host or on an instruction set simulator.
The memory associated with the software can be simulated with various memory models, and the
model could change over the life of a project.
Additional complexity results when the software is a multi-tasking application running on top of an
operating system.
The GSA overcomes these difficulties by providing a generic interface between the verification
environment and the software.
This chapter contains:
6-1
Hardware-Software Co-Verification
Architecture of Software eVCs
6.1
A software eVC is a verification component for embedded software. It drives software by invoking
software methods. It monitors software behavior by collecting information on software variables.
The software eVC contains:
sequence driver activates concurrent sequences in the software eVC and other eVCs.
6-2
Hardware-Software Co-Verification
Ports for Software eVCs
Figure 6-1
6.2
Connect to software via ports to the Generic Software Adaptor. Use method ports to activate software
methods. Use simple ports to retrieve the values of software variables.
The ports are defined in the env. The various components of the eVC use a parent pointer to access the
ports in the env.
method_type read_str (xcore: int) : string @sys.any;
extend vr_qsoc_sw_env_u {
// Define e method port and connect it to the "read_str" c routine
read_str: out method_port of read_str is instance;
keep bind(read_str, external);
keep read_str.hdl_path() == "read_str";
// Define simple port and connect it to the "POS_status" c variable
c_status: in simple_port of status_t is instance;
keep bind(c_status, external);
keep c_status.hdl_path() == "POS_status";
};
6-3
Hardware-Software Co-Verification
BFMs for Software eVCs
6.3
Software sequence items are executed by the BFM in the software eVC. The BFM is typically very
simple. It gets an item and executes it. The BFM itself has no specific knowledge of the software or of
the interface to it.
extend vr_sw_bfm_u {
!driver: sw_sequence_driver;
driver_call() @sys.any is {
var sitem: sw_sequence_item;
while TRUE {
sitem = driver.get_next_item();
sitem.do_me();
emit driver.item_done;
};
}; // driver_call()
run() is also {
start driver_call();
};
};
Using the BFM mechanism lets you add software access just by including another sequence and the
corresponding port in the env.
6.4
For each software interface you must define both a sequence and a sequence item. The sequence is
needed to enable activation from a virtual sequence driver. The sequence item is needed for the various
visualization tools such as stripe charts.
To use sequences for activation of software:
1.
6-4
Hardware-Software Co-Verification
Sequences for Software eVCs
2.
3.
4.
Add a parent pointer to the env that contains the method ports.
For example:
extend sw_sequence_driver {
!p_env: vr_qsoc_sw_env_u; -- access to ports in env
event clock is only @sys.any;
};
Activation Example
The sequence item encapsulates the knowledge of how to invoke the software in a method. The BFM is
responsible to activate that method when the item is sent to the BFM.
extend READ_STR sw_sequence_item {
str: string;
core: int;
do_me() @driver.clock is {
-- Activate the software method
me.str = driver.p_env.read_str$(me.core);
};
nice_string(): string is also {
result = "read_str()";
};
};
The software sequence creates a sequence item and propagates the parameters to it.
extend sw_sequence_kind: [READ_STR];
extend READ_STR sw_sequence {
dr_call: READ_STR sw_sequence_item;
str: string;
core: int;
body() @driver.clock is only {
6-5
Hardware-Software Co-Verification
The Monitor of the Software eVC
6.5
The monitor collects information on software method calls and software variables.
Typically, information on method calls and their parameters is collected from the sequence driver.
Collecting this kind of information from the software side requires modifications to the software, which
is not recommended.
To collect the value of a software variable, you must access a port. You need a sampling event, because
software cannot initiate events on change of value.
A simple solution is to sample the software variables each time that a software method is activated.
on sw_api_called {
POS_status = p_env.c_status$;
};
6-6
This chapter discusses the requirements for developing a uniform and high-standard eVC. The tables
that follow list the various sVM compliance checks.
In the compliance check tables, the column that reads RQ/RC/ST indicates the nature of the check:
RQ = Required
RC = Recommended
ST = Statistical
The compliance check tables are:
7-1
7.1
Table 7-1
Index
Check
RQ/RC/
ST
PKCN
RQ
PKDM
RQ
PKDO
RQ
PKEX
RQ
PKNM
RQ
PKSV
RQ
PKTS
RQ
PKVM
RQ
7.2
Table 7-2
Comments/
Information
Comments/
Information
RQ
ARCP
RQ
ARIN
RQ
Index
Check
ARAP
7-2
Table 7-2
Comments/
Information
RC
ARMN
RQ
ARMS
RQ
ARNM
RQ
ARRF
RC
ARSC
RC
ARSI
RQ
ARVD
RQ
Index
Check
ARMM
7.3
Table 7-3
Index
Check
RQ/RC/
ST
Comments/
Information
RMAM
RQ
RMBF
RQ
RMME
RC
7-3
Table 7-3
Index
Check
RQ/RC/
ST
Comments/
Information
RMSD
RQ
7.4
Table 7-4
Comments/
Information
RQ
CHDF
RQ
CHST
RQ
Index
Check
CHCB
7.5
Table 7-5
Comments/
Information
RQ
RQ
Index
Check
CVDF
CVST
7-4
page 3-24
7.6
Table 7-6
Comments/
Information
RQ
RQ
Index
Check
SQLB
SQRW
7.7
Table 7-7
Index
Check
HSAR
RC
HSEV
RC
Comments/
Information
See Chapter 6
Hardware-Software
Co-Verification
7-5
7-6
Index
backdoor
access 4-39
defaults, overriding 4-38
operations 4-36
HDL path, setting 4-36
registers, access 4-36, 4-38
bad module 2-11
badly wired module 2-11
BFM 5-3
TLM 5-3
BFM sequence driver
integrating the register sequence driver 4-18
BFMs for software eVCs 6-4
bridge to the system bus 2-8
bugs in new modules 2-10
bugs, SoC, typical 2-10
bus, CPU 2-7
bus, system 2-8
C
checking 2-14, 3-26
scoreboard 3-26
sVM standardization, developer comments
7-4
checkout station 2-3
commands 4-110
compare_and_update() 4-74, 4-87
Index-1
Index
D
data structure, register 4-63
data structures 4-61
def_reg macro
defining registers with 4-9
defining types 3-20
Detailed Address Map page 4-106
deterministic configuration sequences, creating
4-25
dispatcher 2-14
DMA 2-9
document
content 2-2
example (the RSoC) 2-3
purpose 2-2
document conventions 1-5
driver and hardware verification 2-17
dumpmemh 4-92
E
end of test 4-46
eRM architecture 4-7
eVC standardization 7-1
checking 7-4
coverage 7-4
developer comments
architecture 7-2
name space 7-2
packaging 7-2
reset 7-2
sequences 7-5
reset 7-3
ex_c_bus environment 4-3
example software eVC architecture 6-3
extending subtypes 3-19
F
features, main, package 4-2
fetch() 4-86
fields
coverage, defining 4-14
Index
L
layering, register sequence drivers 4-17
G
generation, stimulus 3-28
get_data 4-93
golden example QSoC_D DUT 3-1
H
hardware-software
sVM standardization, developer comments
7-5
hardware-software co-verification 6-1
has_checks 3-32
has_coverage 3-33
hierarchy, types 4-62
HW, RSoC 2-6
I
indirect registers
access handling 4-43
addressing 4-40
addressing model 4-40
driving 4-41
identifying 4-43
init sequence 2-14
in-method ports, multiple instance 5-13
instances, register, defining 4-11
integrating eVCs into an SVE 3-15
System Verification Methodology
macros
def_reg
defining registers with 4-9
read_reg 4-24, 4-52
reg_def 4-10
reg_list 4-13
write_reg 4-24, 4-52
main features, package 4-2
map, address 4-82
instantiating 4-15
register files, adding 4-15
updating 4-39
match() 3-26
mem_def 4-57
memory 2-7, 4-50
addresses, uninitialized 4-54
backdoor access 4-56
instantiating 4-51
objects 4-57
accessing in memory 4-60
connecting to memory 4-58
creating 4-57
objects, accessing in memory
methods 4-61
reading from file 4-54
returned data of uninitialized addresses,
controlling 4-54
side effects 4-55
sparse memory 4-50
sVM standardization, developer comments
7-3
updating 4-53
writing to file 4-54
memory model 4-1
message verbosity, controlling 4-46
Index-3
Index
N
names
name space, eVC standardization, developer
Index-4
comments 7-2
naming conventions 4-2
types 4-62
nodes, register files 4-78
not everything is an SoC 2-3
O
order, register fields 4-11, 4-44
order, register fields, changing 4-44
organizing and using sequence libraries 3-23
overview 4-2
P
package, register
main features 4-2
packages
sVM standardization, developer comments
7-2
peripheral controllers 2-8
point of sale 2-3
ports for software eVCs 6-3
ports,in-method, multiple instances 5-13
post_access() 4-75
preface 2-1
problems arising from the interaction of modules
2-11
PROXY 5-3
purpose of document 2-2
PV mode, synchronization issues 5-14
Q
QSoC eVC 3-8
QSoC_D DUT 3-1
R
random registers, accessing 4-28, 4-29
read_mem_obj 4-60
read_reg macro 4-24, 4-52
read_reg_val() 4-66
readmemh 4-91
reference model 3-14
Index
identifying 4-43
internal changes, monitoring 4-38
layering, sequence driver 4-17
methods, backdoor-related 4-39
mirroring 4-13
model, creating 4-7
model, integrating 4-7, 4-17
random, accessing 4-28, 4-29
sequence driver layering 4-17
sequences 4-46
sVM standardization, developer comments
7-3
using 4-21
writing to specific instance of register type
4-27
registers and the magic of memory-mapped IO
2-8
reuse of register sequences 3-22
reusing coverage 3-24
reusing sequence drivers 3-21
reusing sequences 3-21
RSD
fields, register-related 4-95
instantiating 4-15
integrating with BFM SD 4-18
layering 4-17
methods, register-related 4-95
RSoC 2-3
RSoC DUT 3-2
RSoC hardware 2-7
RSoC hardware verification 2-13
RSoC HW 2-6
S
sc_main.cpp 5-7
scalability 3-27
checking 3-32
grouping checks 3-32
preparing for scalability 3-32
tradeoff 3-33
coverage 3-33
grouping coverage 3-33
overhead, minimizing with coverage
Index-5
Index
API 3-34
tradeoff 3-34
eVC-specific 3-34
context switching, minimizing 3-35
deterministic behavior, implementing
3-35
monitor sharing 3-35
sequence drivers, optimizing 3-35
messages 3-34
stimulus generation 3-28
scoreboard
connecting 3-26
Seamless and such 2-19
sequence driver
BFM, integrating with register sequence
driver 4-18
register
instantiating 4-15
integrating with BFM sequence driver
4-18
sequence libraries 3-28
sequences 3-21
configuration, deterministic, creating 4-25
libraries 3-28
module to system 3-22
register 4-46
sVM standardization, developer comments
7-5
sequences for software eVCs 6-4
Sequences page 4-110
set_compare_mask() 4-48
set_read_mask() 4-70
set_static_info() 4-68, 4-69, 4-71
side effects, implementing 4-31
simulation modes 2-17
SoC 2-2
HW verification 2-10
SoC bugs, typical 2-10
SoC HW verification 2-10
SoC verification
HW 2-10
why it is different 2-1
Index-6
T
terminology 1-4
TLM
BFM 5-3
BFM methodology 5-9
monitor 5-4
monitor methodology 5-10
open issues 5-9
overview 5-1
system eVC 5-2
Index
U
update() 4-73, 4-85
updating, register model with monitor 4-20
V
variations on a theme 2-9
verbosity, message, controlling 4-46
verifying
full RSoC hardware environment 2-12
hardware with the application software 2-19
HW with the SW 2-16
HW with the SW device drivers 2-17
SoC HW 2-11
XCore 2-12
verifying systems 3-3
visual conventions in this manual 1-5
visualization 4-104, 4-105, 4-106, 4-107, 4-108
Address Map page 4-105, 4-106, 4-107
Address Sets page 4-108
All Address Maps page 4-105
All Maps page 4-105
Detailed Address Map page 4-106
Register File page 4-108
Register Files page 4-107, 4-109
Sequences page 4-110
Stripe Chart page 4-109
System Verification Methodology
W
width, addressing, customizing 4-45
write_mem_obj 4-60
write_reg macro 4-24, 4-52
write_reg_rawval() 4-67
write_reg_val() 4-65
writing to specific instance of register type 4-27
wrong assumptions about some module 2-11
X
XBus
TLM demo 5-16
XCore
TLM demo 5-18
XCore DUT 3-6
Index-7
Index
Index-8