Cookbook Systemverilog Uvm Coding Performance Guidelines Verification Academy
Cookbook Systemverilog Uvm Coding Performance Guidelines Verification Academy
Cookbook
Online Methodology Documentation from the
Mentor Graphics Verification Methodology Team
Contact VMDOC@mentor.com
http://verificationacademy.com
SV/Guidelines
SV/Guidelines
Mentor Graphics SystemVerilog Guidelines
SystemVerilog Do's
SystemVerilog Don'ts
The SystemVerilog coding guidelines and rules in this article are based on Mentor's experience and are
designed to steer users away from coding practices that result in SystemVerilog that is either hard to
understand or debug.
Please send any suggestions, corrections or additions to ?subject=SV/Guidelines vmdoc@mentor.com
[1]
SV/Guidelines
Not Recommended
// Variable definition:
logic enable;
logic completed;
logic in_progress;
// Variable definition:
logic enable, completed, in_progress;
// Statements:
if(enable == 0) in_progress = 1; else in_progress = 0;
// Statements:
// (See next Guideline for the use of begin-end pairs
// with conditional statements)
//
if(enable == 0)
in_progress = 1;
else
in_progress = 0;
Not Recommended
SV/Guidelines
Not Recommended
Recommended
SV/Guidelines
This makes it clearer what the name is, as opposed to other naming styles such as CamelCase which are
harder to read.
Recommended
Not Recommended
axi_fabric_scoreboard_error
AxiFabricScoreboardError
_e
_h
_m
_cfg
_ap
_group
1.10Guideline:Use the end label for classes, functions, tasks, and packages
This forces the compiler to check that the name of the item matches the end label which can trap cut and
paste errors. It is also useful to a person reading the code.
// Using end labels
package my_pkg;
//...
class my_class;
SV/Guidelines
// ...
function void my_function();
//...
endfunction: my_function
task my_task;
// ...
endtask: my_task
endclass: my_class
endpackage: my_pkg
SV/Guidelines
rand bit valid;
rand raw_sample_t raw_audio_sample;
rand processed_sample_t processed_sample;
// function: new
// Constructor - initializes valid
extern function new();
// function: compress_sample
// Applies compression algorithm to raw sample
// inputs: none
// returns: void
extern function void compress_sample();
// function: set_new_sample
// Set a new raw sample value
// inputs:
//
raw_sample_t new_sample
// returns: void
extern function void set_new_sample(raw_sample_t new_sample);
endclass: audio_compress
function audio_compress::new();
valid = 0;
iteration_limit = $bits(processed_sample_t);
endfunction
function void audio_compress::compress_sample();
for(int i = 0; i < iteration_limit; i++) begin
processed_sample[i] = raw_audio_sample[((i*2)-1):(i*2)];
end
valid = 1;
endfunction: compress_sample
function void audio_compress:set_new_sample(raw_sample_t new_sample);
raw_audio_sample = new_sample;
valid = 0;
endfunction: set_new_sample
File Naming
3.1Guideline:Use lower case for file and directory names
Lower case names are easier to type.
3.2Guideline:Use .sv extensions for compile files, .svh for `include files
The convention of using the .sv extension for files that are compiled and .svh for files that get included
makes it easier to sort through files in a directory and also to write compilation scripts.
For instance, a package definition would have a .sv extension, but would reference `included .svh files:
SV/Guidelines
3.3Guideline:`include .svh class files should only contain one class and be named after
that class
This makes it easier to maintain the code, since it is obvious where the code is for each class.
Directory Names
Testbenches are constructed of SystemVerilog UVM code organized as packages, collections of
verification IP organized as packages and a description of the hardware to be tested. Other files such as
C models and documentation may also be required. Packages shoudl be organized in a hierarchy.
For a complex package (such as a UVC) that may contain tests, examples and documentation, create
subdirectories:
SV/Guidelines
abc_pkg/examples
abc_pkg/docs
abc_pkg/tests
abc_pkg/src/abc_pkg.sv
Using Packages
3.7Rule:Import packages to reference their contents
When you use a function or a class from a package, you import it, and `include any macro definitions.
If you `include the package source, then you will be creating a new namespace for that package in every
file that you `include it into, this will result in type matching issues.
import abc_pkg::*;
`include "abc_macros.svh"
SV/Guidelines
To compile code that uses the package, you also use a +incdir to reference the source directory if a
macro file needs to be `included.
vlog +incdir+$ABC_PKG/src tb_top.sv
10
SV/Guidelines
// How to check that a $cast has worked correctly
function my_object get_a_clone(uvm_object to_be_cloned);
my_object t;
if(!$cast(t, to_be_cloned.clone()) begin
`uvm_error("get_a_clone", "$cast failed for to_be_cloned")
end
if(t == null) begin
`uvm_fatal("get_a_clone", "$cast operation resulted in a null handle, check to_be_cloned handle")
end
return t;
endfunction: get_a_clone
Constructs to be Avoided
The SystemVerilog language has been a collaborative effort with a long history of constructs borrowed
from other languages. Some constructs have been improved upon with newer constructs, but the old
constructs remain for backward compatibility and should be avoided. Other constructs were added
before being proven out and in practice cause more problems than they solve.
11
SV/Guidelines
most cases, an index type of [int] is sufficient. For example, a foreach loop requires a fixed type to
declare its iterator variable.
string names[*]; // cannot be used with foreach, find_index, ...
string names[int];
...
foreach (names[i])
$display("element %0d: %s",i,names[i]);
final
program
Legacy from Vera, alters timing of sampling, not necessary and potentially confusing
Coding Patterns
Some pieces of code fall into well recognized patterns that are know to cause problems
4.8Rule:Do not rely on static variable initialization order, initialize on first instance.
The ordering of static variable initialization is undefined. If one static variable initialization requires the
non-default initialized value of another static variable, this is a race condition. This can be avoided by
creating a static function that initializes the variable on the first reference, then returns the value of the
static variable, instead of directly referencing the variable.
typedef class A;
typedef class B;
A a_top=A::get_a();
B b_top=B::get_b();
class A;
static function A get_a();
if (a_top == null) a_top =new();
return a_h;
endfunction
endclass : A
12
SV/Guidelines
class B;
A a_h;
protected function new;
a_h = get_a();
endfunction
static function B get_b();
if (b_top == null) b_top =new();
return b_top;
endfunction
endclass : B
Covergroups
4.9Guideline:Create covergroups within wrapper classes
Covergroups have to be constructed within the constructor of a class. In order to make the inclusion of a
covergroup within a testbench conditional, it should be wrapped within a wrapper class.
13
SV/Guidelines
address = new_address;
detail_group.sample();
endfunction: sample
Coverpoint sampling may not be valid in certain situations, for instance during reset.
// Using iff to turn off unnecessary sampling:
// Only sample if reset is not active
coverpoint data iff(reset_n != 0) {
// Only interested in high_end values if high pass is enabled:
bins high_end = {[10000:20000]} iff(high_pass);
bins low_end = {[1:300]};
}
Collecting Coverage
4.11Guideline:Use the covergroup sample() method to collect coverage
Sample a covergroup by calling the sample routine, this allows precise control on when the sampling
takes place.
14
SV/Guidelines
[2]
SV/PerformanceGuidelines
SV/PerformanceGuidelines
These guidelines are aimed at enabling you to identify coding idioms that are likely to affect testbench
performance. Please note that a number of these guidelines run counter to other recommended coding
practices and a balanced view of the trade off between performance and methodology needs to be made.
Whilst some of the code structures highlighted might be recognized and optimized out by a compiler,
this may not always be the case due to the side effects of supporting debug, interactions with PLI code
and so on. Therefore, there is almost always a benefit associated with re-factoring code along the lines
suggested.
SystemVerilog shares many common characteristics with mainstream software languages such as C,
C++ and Java, and some of the guidelines presented here would be relevant to those languages as well.
However, SystemVerilog has some unique capabilities and short-comings which might cause the unwary
user to create low performance and memory hungry code without realising it.
Tuning the performance of a testbench is made much easier the use of code profiling tools. A code
profile can identify 'hot-spots' in the code, and if these places can be refactored the testbench is almost
invariably improved. In the absence of a profiling tool, visual code inspection is required but this takes
time and concentration. These guidelines are intended to be used before coding starts, and for reviewing
code in the light of code profiling or by manual inspection.
Code Profiling
Code profiling is an automatic technique that can be used during a simulation run to give you an idea of
where the 'hot-spots' are in the testbench code. Running a code profile is a run time option, which if
available, will be documented in the simulator user guide. See the "Profiling Performance and Memory
Use" chapter in the Questa User Guide for more information.
When your testbench code has reached a reasonable state of maturity and you are able to reliably run
testcases, then it is always worth running the profiling tool. Most code profilers are based on sampling;
they periodically record which lines of code are active and which procedural calls are in progress at a
given point in time. In order to get a statistically meaningful result, they need to be run for a long enough
time to collect a representative sample of the code activity.
In a well written testbench with no performance problems, the outcome of the sampling will be a flat
distribution across the testbench code. However, if the analysis shows that a particular area of the
testbench is showing up in a disproportionate number of samples then it generally points to a potential
problem with that code.
Profiling is an analysis technique and the results will be affected by:
The characteristics of the testcase(s) that are being analysed
Random seeding - causing different levels of activity in the testbench
15
SV/PerformanceGuidelines
Dominant behaviour in your testbench - some areas of the testbench may simply be doing more work
DUTcoding style
The sample interval
The length of the simulation time that the profile is run for
What is going on in the simulation whilst the profile is being run
With constrained random testbenches it is always worth running through alternative testcases with
different seeds whilst analysing the profiling report since these may throw light on different coding
issues.
Loop Guidelines
Loop performance is determined by:
The work that goes on within the loop
The checks that are made in the loop to determine whether it should be active or not
The work that goes on within the loop should be kept to a minimum, and the checks made on the loop
bounds should have a minimum overhead. Here are some examples of good and bad loop practices:
Lower Performance Version
// dynamic array, unknown size
int array[];
int total = 0;
for(int i = 0; i < array.size(); i++) begin
total += array[i];
end
Setting a variable to the size of the array before the loop starts saves the overhead of calculating the
array.size() on every iteration.
16
17
SV/PerformanceGuidelines
int
int
int
int
foreach(decision_weights[i]) begin
total_weights += decision_weights[i] *
case_exponents["high"];
end
decision_weights[string]; // Assoc
case_exponents[string];
total_weights;
case_exponent;
case_exp = case_exponents["high"]
foreach(decision_weights[i]) begin
total_weights += decision_weights[i] *
case_exp;
end
The foreach()loop construct is typically higher performance than for(int i = 0; i < <val>; i++) for
smaller arrays.
The lookup of the exponent value in the associative array on every loop iteration is uneccessary, since it
can be looked up at the beginning of the loop.
Lower Performance Version
int an_array[50];
int indirection_index;
int to_find = 42;
int an_array[50];
int indirection_index;
int to_find = 42;
indirection_index = -1;
indirection_index = -1;
In this example, an array with unique entries is being searched within a loop for a given value. Using
break in the second example terminates the evaluation of the loop as soon as a match is found.
Decision Guidelines
When making a decision on a logical or arithmetic basis there are a number of optimisations that can
help improve performance:
18
SV/PerformanceGuidelines
With an AND evaluation, if the the first term of the expression is untrue, the rest of the evaluation is
skipped:
if(A && B && C) begin
// do something
end
With an OR evaluation, if the first term of the expression is true, then the rest of the evaluation is
skipped:
if(A || B || C) begin
// do something
end
If the terms in the expression have a different level of "expense", then the terms should be ordered to
compute the least expensive first:
Lower Performance Version
If the inexpensive expression A evaluates untrue, then the other expensive conditional tests do not need
to be made.
Lower Performance Version
A slightly less obvious variant, which saves the computation required to arrive at a decision if C is not
true.
19
SV/PerformanceGuidelines
In the above example, refactoring the boolean condition removes one logical operation, using A as a
short-circuit potentially reduces the active decision logic
Priority encoding
If you know the relative frequency of conditions in a decision tree, move the most frequently occurring
conditions to the top of the tree. This most frequently applies to case statements and nested ifs.
Lower Performance Version
// Case options follow the natural order:
case(char_state)
START_BIT: // do_something to start tracking the char (once per word)
TRANS_BIT: // do something to follow the char bit value (many times per word)
PARITY_BIT: // Check parity (once per word, optional)
STOP_BIT: // Check stop bit (once per word)
endcase
20
SV/PerformanceGuidelines
STOP_BIT: // Check stop bit (once per word)
PARITY_BIT: // Check parity (once per word, optional)
endcase
Most of the time, the case statement exits after one check saving further comparisons.
Lower Performance Version
In the higher performance version of this example, if ready is not valid, the rest of the code does not get
evaluated. Then the read_cycle check is made, which removes the need for the write_cycle check.
SV/PerformanceGuidelines
function void do_it(input int q[$], input string name);
int m_i;
string m_s;
m_s = name;
m_i = q.pop_front();
$display("string = %s, value = %0d", m_s, m_i);
q.push_front(m_i);
endfunction: do_it
21
22
SV/PerformanceGuidelines
bus_txn.opcode = bus_state.opcode;
bus_txn.data = bus_state.data;
return bus_txn;
end
return null;
endfunction: get_next
task handle_bus_write;
bus_object write_req =
bus_object::type_id::create("write_req");
task handle_bus_write;
bus_object write_req;
write_bus_req_fifo.get(write_req);
// do something with the write_req;
endtask: handle_bus_write
write_bus_req_fifo.get(write_req);
// do something with the write_req;
endtask: handle_bus_write
Constructing the write_req object is redundant since its handle is re-assigned by the get from the
bus_write_req_fifo.
23
SV/PerformanceGuidelines
int A;
int A;
endclass: any_thing
endclass: any_thing
any_thing m;
int V;
any_thing m;
int V;
initial begin
m = new();
V = 1;
repeat(10) begin
m.set_A(V);
V = V + m.get_A();
end
end
initial begin
m = new();
V = 1;
repeat(10) begin
m.A = V;
V = V + m.A;
end
end
Making an assignment to the data variable within the class using its hierarchical path is more efficient
than calling a method to set()/get() it. However, if the set()/get() method does more than a simple
assignment - e.g. a type conversion or a checking operation on the arguments provided, then the method
approach should be used.
Note that: this guideline is for performance and flouts the normal OOP guideline that data variables
within a class should only be accessible via methods. Using direct access methods to get to variables
improves performance, but comes at the potential cost of making the code less reuseable and relies on
the assumption that the user knows the name and type of the variable in question.
24
SV/PerformanceGuidelines
The second implementation extends the mailbox directly and avoids the extra layer in the first example.
25
SV/PerformanceGuidelines
class multi_method;
int i;
endclass: multi_method
function void m2();
m3();
endfunction: m2
function void m3();
i++;
endfunction: m3
endclass: multi_method
In the first example, a function call is implemented as a chain, whereas the second example has a single
method and will have a higher performance. Your code may be more complex, but it may have method
call chains that you could unroll.
Array Guidelines
SystemVerilog has a number of array types which have different characteristics, it is worth considering
which type of array is best suited to the task in hand. The following table summarises the considerations.
Array Type
Characteristics
Memory
Performance Impact
Impact
Static Array
int a_ray[7:0];
Index is by integer.
Dynamic Array
int a_ray[];
simulation.
Least
Less
Index is by integer.
Queues
int a_q[$];
Self-managed sizing
More
arrays
More
int a_ray[string];
can be deleted
with use
For example, it may be more efficient to model a large memory space that has only sparse entries using
an associative array rather than using a static array. However, if the associative array becomes large
SV/PerformanceGuidelines
because of the number of entries then it would become more efficient to implement to use a fixed array
to model the memory space.
Avoiding Work
The basic principle here is to avoid doing something unless you have to. This can manifest itself in
various ways:
26
SV/PerformanceGuidelines
27
SV/PerformanceGuidelines
foreach (data_array[i]) data_array[i] inside {[0:4095]};
}
// Constraints setting the size of the array
constraint YCbCr_size_c {
data_array.size == (2*length); // 6
}
constraint RGB_size_c {
data_array.size == (3*length); // 6
}
constraint MONO_size_c {
data_array.size == (length); // 6
}
// Frequency of live/freeze frames:
constraint live_freeze_dist_c {
live_freeze dist { freeze := 20, live := 80};
}
// Set the frame size in pixels
constraint calc_length_c {
length == x_pixels * y_pixels; // 6
}
// UVM Factory Registration
`uvm_object_utils(video_frame_item)
//
//
//
//
28
SV/PerformanceGuidelines
this.RGB_inside_c.constraint_mode(1);
this.RGB_size_c.constraint_mode(1);
end
default : uvm_report_error(get_full_name(),
"!!!!No valid video format selected!!!\n\n", UVM_LOW);
endcase
function new(string
super.new(name);
endfunction
name = "video_frame_item");
endclass: video_frame_item
29
30
SV/PerformanceGuidelines
function void set_frame_vars(int pix_x_dim = 16,
int pix_y_dim = 16,
video_mode_e vid_type = MONO);
length = (pix_x_dim * pix_y_dim); // 1, 6
mode = vid_type;
endfunction: set_frame_vars
function new(string
super.new(name);
endfunction
name = "video_frame_item");
endclass: video_frame_item
The two code fragments are equivalent in functionality, but have a dramatic difference in execution
time. The re-factored code makes a number of changes which speed up the generation process
dramatically:
In the original code, the size of the array is calculated by randomizing two variables - length and
array size. This is not necessary since the video frame is a fixed size that can be calculated from other
properties in the class.
The length of the array is calculated using a multiplication operator inside a constraint
In the first example, the content of the data array is calculated by the constraint solver inside a
foreach() loop. This is unnecessary and is expensive for larger arrays. Since these values are within a
predictable range they can be generated in the post_randomize() method.
The enum types live_freeze_t and video_mode_e will have an underlying integer type by default, the
refactored version uses the minimal bit types possible.
The original version uses a set of constraint_mode() and rand_mode() calls to control how the
randomization works, this is generally less effective than coding the constraints to take state
conditions into account.
In effect, the only randomized variable in the final example is the live_freeze bit.
The first version of the constraint uses a modulus operator to set the lowest two bits to zero, the second
version does this directly avoiding an expensive arithmetic operation
Lower Performance Version
enum bit[3:0] {ADD, SUB, DIV, OR, AND, XOR, NAND, MULT} opcode_e;
opcode_e ins;
constraint select_opcodes_c {
SV/PerformanceGuidelines
ins dist {ADD:=7, SUB:=7, DIV:=7, MULT:=7};
}
The two versions of the constraint are equivalent in the result they produce, but the first one forces a
distribution to be solved which is much more expensive than limiting the ins value to be inside a set.
Bin Control
Each coverpoint automatically translates to a set of bins or counters for each of the possible values of the
variable sampled in the coverpoint. This would equate to 2**n bins where n is the number of bits in the
variable, but this is typically limited by the SystemVerilog auto_bins_max variable to a maximum of 64
bins to avoid problems with naive coding (think about how many bins a coverpoint on a 32 bit int would
produce otherwise). It pays to invest in covergroup design, creating bins that yield useful information
will usually reduce the number of bins in use and this will help with performance. Covergroup cross
product terms also have the potential to explode, but there is syntax that can be used to eliminate terms.
31
32
SV/PerformanceGuidelines
bit[7:0] a;
bit[7:0] b;
covergroup data_cg;
A: coverpoint a {
bins zero = {0}; // 1 bin
bins min_zone[] = {[8'h01:8'h0F]}; // 15 bins
bins max_zone[] = {[8'hF0:8'hFE]}; // 15 bins
bins max = {8'hFF}; // 1 bin
bins medium_zone[16] = {[8'h10:8'hEF]}; // 16 bins
}
B: coverpoint b{
bins zero = {0};
bins min_zone[] = {[8'h01:8'h0F]};
bins max_zone[] = {[8'hF0:8'hFE]};
bins max = {8'hFF};
bins medium_zone[16] = {[8'h10:8'hEF]};
}
A_X_B: cross A, B; // 2304 bins
endgroup: data_cg
covergroup data_cg;
A: coverpoint a; // 256 bins
B: coverpoint b; // 256 bins
A_X_B: cross A, B; // 65536 bins
endgroup: data_cg
In the first covergroup example, the defaults are used. Without the max_auto_bins variables in place,
there would be 256 bins for both A and B and 256*256 bins for the cross and the results are difficult to
interpret. With max_auto_bins set to 64 this reduces to 64 bins for A, B and the cross product, this saves
on performance but makes the results even harder to understand. The right hand covergroup example
creates some user bins, which reduces the number of theoretical bins down to 48 bins for A and B and
2304 for the cross. This improves performance and makes the results easier to interpret.
Sample Control
A common error with covergroup sampling is to write a covergroup that is sampled on a fixed event
such as a clock edge, rather than at a time when the values sampled in the covergroup are valid.
Covergroup sampling should only occur if the desired testbench behavior has occurred and at a time
when the covergroup variables are a stable value. Careful attention to covergroup sampling improves the
validity of the results obtained as well as improving the performance of the testbench.
33
SV/PerformanceGuidelines
int data;
bit active;
int data;
bit active;
covergroup data_cg;
coverpoint data {
bins a = {[0:4000]};
bins b = {[10000:100000]};
bins c = {[4001:4040]};
}
endgroup: data_cg
task update_coverage;
forever begin
@(posedge clk);
if(valid) begin
data_cg.sample();
end
end
endtask: update_coverage
In the first example, the covergroup is sampled on the rising edge of the clock and the iff(valid) guard
determines whether the bins in the covergroup are incremented or not, this means that the covergroup is
sampled regardless of the state of the valid line. In the second example, the built-in sample() method is
used to sample the covergroup ONLY when the valid flag is set. This will yield a performance
improvement, especially if valid is infrequently true.
Unique Triggering
The condition that starts the evaluation of a property is checked every time it is sampled. If this
condition is ambiguous, then an assertion could have multiple evaluations in progress, which will
potentially lead to erroneous results and will definitely place a greater load on the simulator.
Lower Performance Version
property req_rsp;
@(posedge clk);
req |=>
(req & ~rsp)[*2]
##1 (req && rsp)
##1 (~req && ~rsp);
endproperty: req_rsp
property req_rsp;
@(posedge clk);
$rose(req) |=>
(req & ~rsp)[*2]
##1 (req && rsp)
##1 (~req && ~rsp);
endproperty: req_rsp
SV/PerformanceGuidelines
In the first example, the property will be triggered every time the req signal is sampled at a logic 1, this
will lead to multiple triggers of the assertion. In the second example, the property is triggered on the
rising edge of req which is a discrete event. Other strategies for ensuring that the triggering is unique is
to pick unique events, such as states that are known to be only valid for a clock cycle.
Safety vs Liveness
A safety property is one that has a bound in time - e.g. 2 clocks after req goes high, rsp shall go high. A
liveness property is not bound in time - e.g. rsp shall go high following req going high. When writing
assertions it is important to consider the life-time of the check that is in progress, performance will be
affected by assertions being kept in flight because there is no bound on when they complete. Most
specifications should define some kind of time limit for something to happen, or there will be some kind
of practical limit that can be applied to the property.
Lower Performance Version
property req_rsp;
@(posedge clk);
$(posedge req) |=>
(req & ~rsp)[*1:2]
##1 (req && rsp)[->1] // Unbound condition - within any number of clocks
##1 (~req && ~rsp);
endproperty: req_rsp
Assertion Guards
Assertions can be disabled using the iff(condition) guard construct. This makes sure that the property is
only sampled if the condition is true, which means that it can be disabled using a state variable. This is
particularly useful for filtering assertion evaluation during reset or a time when an error is deliberately
injected. Assertions can also be disabled using the system tasks $assertoff() and $asserton(), these can be
called procedurally from within SystemVerilog testbench code. These features can be used to manage
overall performance by de-activating assertions when they are not valid or not required.
34
35
SV/PerformanceGuidelines
property req_rsp;
@(posedge clk);
$(posedge req) |=>
(req & ~rsp)[*1:2]
##1 (req && rsp)[->1]
##1 (~req && ~rsp);
endproperty: req_rsp
property req_rsp;
// Disable if reset is active:
@(posedge clk) iff(!reset);
$rose(req) |=>
(req & ~rsp)[*1:4]
##1 (req && rsp)
##1 (~req && ~rsp);
endproperty: req_rsp
Note that even leaving a blank begin ... end for the pass clause causes a performance hit.
36
UVM/Guidelines
UVM/Guidelines
Mentor Graphics UVM Guidelines
UVM Do's
Use sequence.start(sequencer)
UVM Don'ts
Avoid set/get_config_string/_int/_object()
Avoid callbacks
The UVM library is both a collection of classes and a methodology for how to use those base classes.
UVM brings clarity to the SystemVerilog language by providing a structure for how to use the features
in SystemVerilog. However, in many cases UVM provides multiple mechanisms to accomplish the same
work. This guideline document is here to provide some structure to UVM in the same way that UVM
provides structure to the SystemVerilog language.
Mentor Graphics has also documented pure SystemVerilog Guidelines as well. Please visit the
SV/Guidelines article for more information.
UVM/Guidelines
Class Definitions
1.1Rule:Define all classes within a package with the one exception of Abstract/Concrete
classes.
Define all classes within a package. Don't `include class definitions haphazardly through out the
testbench. The one exception to defining all classes within a package isAbstract/Concrete classes.
Having all classes defined in a package makes it easy to share class definitions when required. The other
way to bring in class definitions into an UVM testbench is to try to import the class wherever it is
needed. This has potential to define your class multiple times if the class is imported into two different
packages, modules, etc. If a class is `included into two different scopes, then SystemVerilog states that
these two classes are different types.
Abstract/Concrete classes are the exception because they must be defined within a module to allow them
to have access to the scope of the module. The Abstract/Concrete class is primarily used when
integrating verification IP written in Verilog or VHDL.
1.2Rule:Define one class per file and name the file <CLASSNAME>.svh.
Every class should be defined in its own file. The file should be named <CLASSNAME>.svh. The file
should then be included in another file which defines a package. All files included into a single package
should be in the same directory. The package name should end in _pkg to make it clear that the design
object is a package. The file that contains the class definition should not contain any import or include
statements. This results in a file structure that looks like this:
example_agent/ <-- Directory containing Agent code
example_agent_pkg.sv
example_item.svh
example_config.svh
example_driver.svh
example_monitor.svh
example_agent.svh
example_api_seq1.svh
reg2example_adapter.svh
With that list of files, the example_pkg.sv file would look like this:
// Begin example_pkg.sv file
`include "uvm_macros.svh"
package example_pkg;
import uvm_pkg::*;
import another_pkg::*;
//Include any transactions/sequence_items
`include "example_item.svh"
37
UVM/Guidelines
If one of the files that defines a class (example_driver.svh) contains a package import statement in it, it
would be just like the import statement was part of the example package. This could result in trying to
import a package multiple times which is inefficient and wastes simulation time.
1.4Rule:Don't add extra constructor arguments other than name and parent.
Extra arguments to the constructor will at worst case result in the UVM Factory being unable to create
the object that is requested. Even if the extra arguments have default values which will allow the factory
to function, the extra arguments will always be the default values as the factory only passes along the
name and parent arguments.
38
39
UVM/Guidelines
Factory
The UVM Factory provides an easy, effective way to customize an environment without having to
extend or modify the environment directly. To make effective use of the UVM Factory and to promote
as much flexibility for reuse of code as possible, Mentor Graphics recommends following guidelines.
For more information, refer to theFactoryarticle.
2.2Rule:Import packages that define classes registered with the UVM Factory.
This guideline may seem obvious, but be sure to import all packages that have classes defined in them. If
the package is not imported, then the class will not be registered with the UVM Factory. The most
common place that this mistake is made is not importing the package that contains all the tests into the
top level testbench module. If that test package is not imported, then UVM will not understand the
definition of the test the call to run_test() attempts to create the test object.
2.3Guideline: When creating an object with the UVM Factory, match the object's handle
name with the string name passed into the create() call.
UVM builds an object hierarchy which is used for many different functions including UVM Factory
overrides and configuration. This hierarchy is based on the string name that is passed into the first
argument of every constructor. Keeping the handle name and this string hierarchy name the same will
greatly aid in debug when the time comes. For more info, visit the Testbench/Build article.
UVM/Guidelines
Macros
The UVM library contains many different types of macros. Some of them do a very small, well defined
job and are very useful. However, there are other macros which may save a small bit of time initially,
but will ultimately cost more time down the road. This extra time is consumed in both debug and run
time. For more information on this topic, please see theMacroCostBenefitarticle.
Message Macros
3.2Guideline:Use the UVM message macros which are `uvm_info(), `uvm_warning(),
`uvm_error() and `uvm_fatal().
The UVM message macros provide a performance savings when used. The message macros are
`uvm_info(), `uvm_warning(), `uvm_error() and `uvm_fatal(). These macros ultimately call
uvm_report_info, uvm_report_warning, etc. What these macros bring to the table are a check to see if a
message would be filtered before expensive string processing is performed. They also add the file and
line number to the message when it is output.
40
UVM/Guidelines
Sequences
UVM contains a very powerful mechanism for creating stimulus and controlling what will happen in a
testbench. This mechanism, called sequences, has two major use models. Mentor Graphics recommends
starting sequences in your test and using the given simple API for creating sequence items. Mentor
Graphics does not recommend using the sequence list or a default sequence and does not recommend
using the sequence macros. For more information, please refer to the Sequences article.
41
42
UVM/Guidelines
Starting Sequences
4.1Rule:Do start your sequences using sequence.start(sequencer).
To start a sequence running, Mentor Graphics recommends creating the sequence in a test and then
calling sequence.start(sequencer) in one of the run phase tasks of the test. Since start() is a task that will
block until the sequence finishes execution, you can control the order of what will happen in your
testbench by stringing together sequence.start(sequencer) commands. If two or more sequences need to
run in parallel, then the standard SystemVerilog fork/join(_any, _none) pair can be used. Using
sequence.start(sequencer) also applies when starting a child sequence in a parent sequence. You can
also start sequences in the reset_phase(), configure_phase(), main_phase() and/or the shutdown_phase().
See Phasing below
43
UVM/Guidelines
Time Consumption
4.5Rule:Sequences should not explicitly consume time.
Sequences should not have explicit delay statements (#10ns) in them. Having explicit delays reduces
reuse and is illegal for doing testbench accelleration in an emulator. For more information on
considerations needed when creating an emulation friendly UVM testbench, please visit the Emulation
article.
Virtual Sequences
4.6Guideline:When a virtual sequencer is used, virtual sequences should check for null
sequencer handles before executing.
When a virtual sequencer is used, a simple check to ensure a sequencer handle is not null can save a lot
of debug time later.
Phasing
UVM introduces a graph based phasing mechanism to control the flow in a testbench. There are several
"build phases" where the testbench is configured and constructed. These are followed by "run-time
phases" which consume time running sequences to cause the testbench to produce stimulus. "Clean-up
phases" provide a place to collect and report on the results of running the test. See Phasing for more
information.
44
UVM/Guidelines
on the bus regardless of the when the transactions occur. See Phasing/Transactors for more information.
Configuration
6.1Rule:Use the uvm_config_db API to pass configuration information. Do not use
set/get_config_object(), set/get_config_string() or set/get_config_int() as these are
deprecated. Also do not use the uvm_resource_db API.
UVM provides a mechanism for higher level components to configure lower level components. This
allows a test to configure what a testbench will look like and how it will operate for a specific test. This
mechanism is very powerful, but also can be very inefficient if used in an incorrect way. Mentor
Graphics recommends using only the uvm_config_db API as it allows for any type and uses the
component hierarchy to ensure correct scoping. The uvm_config_db API should be used to pass
configuration objects to locations where they are needed. It should not be used to pass integers, strings
or other basic types as it much easier for a name space collision to happen when using low level types.
The set/get_config_*() API should not be used as it is deprecated. The uvm_resource_db API should
not be used due to quirks in its behavior.
UVM/Guidelines
To provide configuration information to agents or other parts of the testbench, a configuration class
should be created which contains the bits, strings, integers, enums, virtual interface handles, etc. that are
needed. Each agent should have its own configuration class that contains every piece of configuration
information used by any part of the agent. Using the configuration class makes it convenient and
efficient to use a single uvm_config_db #(config_class)::set() call and is type-safe. It also allows for
easy extension and modification if required.
6.4Rule:Pass virtual interface handles from the top level testbench module into the
testbench by using the uvm_config_db API.
Since the uvm_config_db API allows for any type to be stored in the UVM resource database, this
should be used for passing the virtual interface handle from the top level testbench module into the
UVM test. This call should be of the form uvm_config_db #(virtual bus_interface)::set(null,
"uvm_test_top", "bus_interface", bus_interface); Notice the first argument is null which means the
scope is uvm_top. The second argument now lets us limit the scope of who under uvm_top can access
this interface. We are limiting the scope to be the top level uvm_test as it should pull the virtual
interface handles out of the resource database and then add them to the individual agent configuration
objects as needed.
Coverage
7.1Guideline:Place covergroups within wrapper classes extended from uvm_object.
Covergroups are not objects or classes. They can not be extended from and also can't be programatically
created and destroyed on their own. However, if a covergroup is wrapped within a class, then the
testbench can decide at run time whether to construct the coverage wrapper class.
Please refer to the SystemVerilog Guidelines for more on general covergroup rules/guidelines.
45
46
UVM/Guidelines
End of Test
8.1Rule:Do use the phase objection mechanism to end tests. Do use the
phase_ready_to_end() function to extend time when needed.
To control when the test finishes use the objection mechanism in UVM. Each UVM phase has an
argument passed into it (phase) of type uvm_phase. Objections should be raised and dropped on this
phase argument. For more information, visit the End of Test article.
8.2Rule:Only raise and lower objections in a test. Do not raise and lower objections on a
transaction by transaction basis.
In most cases, objections should only be raised and lowered in one of the time consuming phases in a
test. This is because the test is the main controller of what is going to happen in the testbench and it
therefore knows when all of the stimulus has been created and processed. If more time is needed in a
component, then use the phase_ready_to_end() function to allow for more time. See
Phasing/Transactors for more information.
Because there is overhead involved with raising and dropping an objection, Mentor Graphics
recommends against raising and lowering objections in a driver or monitor as this will cause simulation
slowdown due to the overhead involved.
Callbacks
9.1Guideline:Do not use callbacks.
UVM provides a mechanism to register callbacks for specific objects. This mechanism should not be
used as there are many convoluted steps that are required to register and enable the callbacks.
Additionally callbacks have a non-negligible memory and performance footprint in addition to potential
ordering issues. Instead use standard object oriented programming (OOP) practices where callback
functionality is needed. One OOP option would be to extend the class that you want to change and then
use the UVM factory to override which object is created. Another OOP option would be create a child
object within the parent object which gets some functionality delegated to it. To control which
functionality is used, a configuration setting could be used or a factory override could be used.
UVM/Guidelines
47
UVM/Performance Guidelines
UVM/Performance Guidelines
Although the UVMimproves verification productivity, there are certain aspects of the methodology that
should be used with caution, or perhaps not at all, when it comes to performance and scalability
considerations.
During the simulation run time of a UVM testbench there are two distinct periods of activity. The first is
the set of UVM phases that have to do with configuring, building and connecting up the testbench
component hierarchy, the second is the run-time activity where all the stimulus and analysis activity
takes place. The performance considerations for both periods of activity are separate.
These performance guidelines should be read in conjunction with the other methodology cookbook
guidelines, there are cases where judgement is required to trade-off performance, re-use and scalability
concerns.
Avoid auto-configuration
Auto-configuration is a methodology inherited from the OVMwhere a component's configuration
variables are automatically set to their correct values from variables that have been set up using
set_config_int(), set_config_string(), uvm_config_db #(..)::set() etc at a higher level of the component
hierarchy. In order to use auto-configuration, field macros are used within a component and the
super.build_phase() method needs to be called during the build_phase(), the auto-configuration process
then attempts to match the fields in the component with entries in the configuration database via a
method in uvm_component called apply_config_settings(). From the performance point of view, this is
VERYexpensive and does not scale.
Lower Performance Version
class my_env extends uvm_component;
48
UVM/Performance Guidelines
bit has_axi_agent;
bit has_ahb_agent;
string system_name;
axi_agent m_axi_agent;
ahb_agent m_ahb_agent;
// Required for auto-configuration
`uvm_component_utils_begin(my_env)
`uvm_field_int(has_axi_agent, UVM_DEFAULT)
`uvm_field_int(has_ahb_agent, UVM_DEFAULT)
`uvm_field_string(system_name, UVM_DEFAULT)
`uvm_component_utils_end
function new(string name = "my_env", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase); // Auto-configuration called here
if(has_axi_agent == 1) begin
m_axi_agent = axi_agent::type_id::create("m_axi_agent", this);
end
if(has_ahb_agent == 1) begin
m_ahb_agent = ahb_agent::type_id::create("m_ahb_agent", this);
end
`uvm_info("build_phase", $sformatf("%s built", system_name))
endfunction: build_phase
endclass: my_env
49
UVM/Performance Guidelines
The recommended practice is not to use field macros in a component, and to not call
super.build_phase()if the class you are extending is from a UVMcomponent base class such as
uvm_component. Even then, when a component does not have a build_phase() method implementation,
the default build_phase() from the uvm_component base class will be called which will attempt to do
auto-configuration. In UVM1.1b, a fix was added that stops the apply_config_settings() method from
continuing if there are no field macros in the component, this speeds up component build, but it is more
efficient to avoid this method being called altogether.
50
UVM/Performance Guidelines
class axi_agent extends uvm_component;
// Configuration parameters:
virtual axi_if AXI;
uvm_active_passive_enum is_active;
int max_burst_size;
axi_driver driver;
axi_sequencer sequencer;
axi_monitor monitor;
function void build_phase(uvm_phase phase);
if(!uvm_config_db #(virtual axi_if)::get(this, "", "AXI", AXI)) begin
`uvm_error("build_phase", "AXI vif not found in uvm_config_db")
end
if(!uvm_config_db #(uvm_active_passive_enum)::get(this,
"", "is_active", is_active)) begin
`uvm_error("build_phase", "is_active not found in uvm_config_db")
end
if(!uvm_config_db #(int)::get(
this, "", "max_burst_size", max_burst_size)) begin
`uvm_error("build_phase", "max_burst_size not found in uvm_config_db")
end
monitor = axi_monitor::type_id::create("monitor", this);
if(is_active == UVM_ACTIVE) begin
driver = axi_driver::type_id::create("driver", this);
sequencer = axi_sequencer::type_id::create("sequencer", this);
end
endfunction: build_phase
function void connect_phase(uvm_phase phase);
monitor.AXI = AXI;
if(is_active == UVM_ACTIVE) begin
driver.AXI = AXI;
driver.max_burst_size = max_burst_size;
end
endfunction: connect_phase
endclass: axi_agent
51
UVM/Performance Guidelines
axi_agent_config axi_cfg; // Used by the AXI agent
// Only consider the build method:
function void build_phase(uvm_phase phase);
// Configuration code for the AXI agent
axi_cfg = axi_agent_config::type_id::create("axi_cfg");
if(!uvm_config_db #(virtual axi_if)::get(this, "", "AXI",
axi_cfg.AXI)) begin
`uvm_error("build_phase", "AXI vif not found in uvm_config_db")
end
axi_cfg.is_active = UVM_ACTIVE;
axi_cfg.max_burst_size = 16;
uvm_config_db #(axi_agent_config)::set(this,
"env.axi_agent*", "axi_agent_config", axi_cfg);
// Other code
endfunction: build_phase
endclass: static_test
// The AXI agent:
class axi_agent extends uvm_component;
// Configuration object:
axi_agent_config cfg;
axi_driver driver;
axi_sequencer sequencer;
axi_monitor monitor;
function void build_phase(uvm_phase phase);
if(!uvm_config_db #(axi_agent_config)::get(this,
"", "axi_agent_config", cfg)) begin
`uvm_error("build_phase", "AXI agent config object not
found in uvm_config_db")
end
monitor = axi_monitor::type_id::create("monitor", this);
if(cfg.is_active == UVM_ACTIVE) begin
driver = axi_driver::type_id::create("driver", this);
sequencer = axi_sequencer::type_id::create("sequencer", this);
end
endfunction: build_phase
function void connect_phase(uvm_phase phase);
monitor.AXI = cfg.AXI;
if(cfg.is_active == UVM_ACTIVE) begin
driver.AXI = cfg.AXI;
driver.max_burst_size = cfg.max_burst_size;
end
endfunction: connect_phase
endclass: axi_agent
The higher performance version of the example uses one uvm_config_db #(...)::set() call and two get()
calls, compared with three set() and four get() calls in the lower performance version. There are also just
two uvm_config_db entries compared to four. With a large number of components, this form of
optimization can lead to a considerable performance boost.
52
UVM/Performance Guidelines
53
UVM/Performance Guidelines
AXI = cfg.AXI;
endfunction: build_phase
endclass: axi_monitor
// The axi driver:
class axi_monitor extends uvm_component;
axi_if AXI;
int max_burst_size;
axi_agent_config cfg;
function void build_phase(uvm_phase phase);
if(!uvm_config_db #(axi_agent_config)::get(this,
"", "axi_agent_config", cfg)) begin
`uvm_error("build_phase", "AXI agent config object
not found in uvm_config_db")
end
AXI = cfg.AXI;
max_burst_size = cfg.max_burst_size;
endfunction: build_phase
endclass: axi_driver
54
UVM/Performance Guidelines
// The axi_monitor and axi_driver are implemented without
// a uvm_config_db #()::get()
The higher performance version has two fewer calls to the uvm_config_db #()::get() method, which
when multiplied by a large number of components can lead to a performance improvement.
In the higher performance version of this code, the scope is very specific and will only match on the
single component for a single key, this cuts downs the search time in the uvm_config_db
55
UVM/Performance Guidelines
56
UVM/Performance Guidelines
The second, higher performance, example shows how to use a shared package to pass the virtual
interface handles from the top level testbench module to the UVM test class. In this case this approach
saves a pair of uvm_config_db set() and get() calls, and also eliminates an entry from the
uvm_config_db for each virtual interface handle. When dealing with large numbers of virtual interfaces,
this can result in a substantial performance improvement. If there is an existing package that is used to
define other testbench configuration parameters such as bus field widths then it can be extended to
include the virtual interface handles.
57
UVM/Performance Guidelines
58
UVM/Performance Guidelines
59
UVM/Performance Guidelines
// Also for the DDR2 agent:
ddr2 = ddr2_agent::type_id::create("ddr2", this);
ddr2.cfg = cfg.ddr2_cfg;
// etc
endfunction: build_phase
The higher performance example avoids the use of the uvm_config_db altogether, providing the ultimate
configuration and build performance enhancement. The impact of using this approach is that it requires
the assignments to be chained together; that it requires the agent code to test for a null config object
handle before attempting to get the configuration object handle; and that any stimulus hierarchy needs to
take care of getting handles to testbench resources such as register models.
Another major consideration with this direct approach to the assignment of configuration object handles
is that if VIP is being re-used, it may well be implemented with the expectation that its configuration
object will be set in the uvm_config_db. This means that there may have to be some use of the
uvm_config_db to support the reuse of existing VIP.
60
UVM/Performance Guidelines
// Communicate the current id:
uvm_config_db #(int)::set(null, "*", "current_id", current_id);
current_id++;
end
// In a consumer component looking out for the current_id value
int current_id;
forever begin
uvm_config_db #(int)::wait_modified(this, "*", "current_id");
if(!uvm_config_db #(int)::get(this,
"", "current_id", current_id)) begin
`uvm_error( ....)
end
// Lots of work to track down a transaction with the current_id
end
The principle at work in the higher performance version is that the current_id information is inside an
object. Both the consumer and the producer components share the handle to the same object, therefore
when the producer object makes a change to the current_id field, it is visible to the consumer component
via the handle. This avoids the use of repeated set() and get() calls in the uvm_config_db and also the
use of the expensive wait_modified() method.
61
UVM/Performance Guidelines
Although the lower performance code example looks more compact, compiling with an -epretty flag will
reveal that they expand out into many lines of code. The higher performance example shows the
templates for the various uvm_object convenience methods which should be implemented manually, this
62
UVM/Performance Guidelines
will always improve performance and enhance debug should you need it.
The definitive guide on the trade-offs involved in using or not using these and the various other UVM
macros can be found here.
The higher performance example only makes one factory create call, and uses clone() to create further
copies of it, so saving the extended factory look-up each time that is expended each time round the
generation loop in the lower performance example.
63
UVM/Performance Guidelines
64
UVM/Performance Guidelines
ADDR_RANGE: coverpoint addr;
OPCODE: coverpoint we {
bins rd = {APB_READ};
bins_wr = {APB_WRITE};
}
ACCESS: cross ADDR_RANGE, OPCODE;
endgroup: register_space_access_cg;
task run_phase(uvm_phase phase);
apb_seq_item apb_item;
forever begin
seq_item_port.get(apb_item);
register_space_access_cg.sample(apb_item.addr[7:0], apb_item.we);
// Do the signal level APB cycle
seq_item_port.item_done();
end
endtask: run_phase
// ....
endclass: apb_coverage_driver
The lower performance example shows the use of a covergroup within a transaction to collect input
stimulus functional coverage information. This adds a memory overhead to the transaction that is
avoided by the higher performance example which collects coverage in a static component based on the
content of the transaction.
65
UVM/Performance Guidelines
function void report_phase(uvm_phase phase);
if(errors != 0) begin
`uvm_error("report_phase", $sformatf(
"%0d errors found in %0d transfers", errors, n_tfrs))
end
else if(warnings != 0) begin
`uvm_warning("report_phase", $sformatf(
"%0d warnings issued for %0d transfers", warnings, n_tfrs))
end
else begin
`uvm_info("report_phase", $sformatf(
"%0d transfers with no errors", n_tfrs), UVM_MEDIUM)
end
endfunction: report_phase
In the example shown, the same reports would be generated in each case, but if the verbosity settings are
set to suppress the message, the higher performance version would check the verbosity before generating
the strings. In a testbench where there are many potential messages and the reporting verbosity has been
set to low, this can have a big impact on performance, especially if the reporting occurs frequently.
Note also that the print() method calls $display() without checking verbosity settings.
66
UVM/Performance Guidelines
The higher performance version of the set_txen_field avoids the expensive regular expression lookup of
the field's name string.
67
UVM/Performance Guidelines
The higher performance version of the code only does one get_registers() call and avoids the overhead
associated with the repeated call in the lower performance version.
68
UVM/Performance Guidelines
In the higher performance version of the code, the objection is raised at the start of the sequence and
dropped at the end, bracketing in time all the sequence_items sent to the driver, this is far more efficient
than raising an objection per sequence_item.
69
UVM/Performance Guidelines
//
// Call-Back class for recording register transactions:
//
class record_reg_cb extends uvm_reg_cbs;
virtual
endtask
virtual task post_write(uvm_reg_item rw);
endtask
virtual task pre_read(uvm_reg_item rw);
endtask
function void do_record(uvm_recorder recorder);
endfunction
endclass : record_reg_cb
//
// Package function for enabling recording:
//
function void enable_reg_recording(uvm_reg_block reg_model,
reg_recording_mode_t record_mode = BY_FIELD);
uvm_reg regs[$];
record_reg_cb reg_cb;
//Set the recording mode
uvm_config_db #(reg_recording_mode_t)::set(
null,"*","reg_recording_mode", record_mode);
//Get the queue of registers
reg_model.get_registers(regs);
//Assign a callback object to each one
foreach (regs[ii]) begin
reg_cb = new({regs[ii].get_name(), "_cb"});
uvm_reg_cb::add(regs[ii], reg_cb);
end
reg_cb = null;
uvm_reg_cb::display();
endfunction : enable_reg_recording
endtask
70
UVM/Performance Guidelines
virtual task post_write(uvm_reg_item rw);
endtask
virtual task pre_read(uvm_reg_item rw);
endtask
function void do_record(uvm_recorder recorder);
endfunction
endclass : record_reg
The main argument for using call-backs in this case is that it does not require that the register model be
used with the extended class, which means that it can be 'retro-fitted' to a register model that uses the
standard UVM uvm_reg class. However, this comes at the cost of a significant overhead - there is an
additional call-back object for each register in the register model and the calling of the transaction
recording methods involves indirection through the UVM infrastructure to call the methods within the
call-back object. Given that a register model is likely to be generated, and that there could be thousands
of registers in larger designs then using the extended record_reg class will deliver higher performance
with minimum inconvenience to the user.
71
Cookbook
Verification Academy
www.mentor.com
2015 Mentor Graphics Corporation, all rights reserved. This document contains information that is proprietary to Mentor Graphics Corporation and
may be duplicated in whole or in part by the original recipient for internal business purposes only, provided that this entire notice appears in all copies.
In accepting this document, the recipient agrees to make every reasonable effort to prevent unauthorized use of this information. All trademarks
mentioned are this document are trademarks of their respective owners.
Corporate Headquarters
Mentor Graphics Corporation
8005 SW Boeckman Road
Wilsonville, OR 97070-7777
Phone: 503.685.7000
Fax: 503.685.1204
Sales and Product Information
Phone: 800.547.3000
Silicon Valley
Mentor Graphics Corporation
46871 Bayside Parkway
Fremont, California 94538 USA
Phone: 510.354.7400
Fax: 510.354.1501
North American Support Center
Phone: 800.547.4303
Europe
Mentor Graphics
Deutschland GmbH
Arnulfstrasse 201
80634 Munich
Germany
Phone: +49.89.57096.0
Fax: +49.89.57096.400
Pacific Rim
Mentor Graphics (Taiwan)
Room 1001, 10F
International Trade Building
No. 333, Section 1, Keelung Road
Taipei, Taiwan, ROC
Phone: 886.2.87252000
Fax: 886.2.27576027
Japan
Mentor Graphics Japan Co., Ltd.
Gotenyama Garden
7-35, Kita-Shinagawa 4-chome
Shinagawa-Ku, Tokyo 140-0001
Japan
Phone: +81.3.5488.3030
Fax: +81.3.5488.3021