Sei sulla pagina 1di 16

Whitepaper

Overview of Sequence Based Stimulus Generation in OVM 2.0

Andy Meyer Mentor Graphics

www.mentor.com

Introduction
Stimulus generation requirements are expanding. A variety of factors are driving the complexity of verification stimulus, including higher levels of intellectual property integration in designs, increased levels of software interaction in hardware functionality, more complex bus protocols, and the need to reuse significant portions of verification environments. This imposes new requirements in stimulus generation, including the ability to: Create stimulus modules that run in a controlled fashion, in serial or parallel. Separate the stimulus generation from the actual testbench. Layer stimulus in the generation of a single protocol and encapsulate layered stimulus into a single protocol. One example of this is Ethernet over SONET. Package stimulus generation with a verification IP component. These requirements are met in OVM with the sequences facility. Sequences allow a verification engineer to construct complex stimulus operations using a building-block approach. Sequences may be written, shared, and reused to generate the desired stimulus with less effort and greater modularity than more traditional testwriting approaches. This document provides an overview of sequences and then shows all the code necessary to use sequences within the OVM.

Basics of Sequences
A sequence is used to generate stimulus of a particular type or protocol. Figure 1 illustrates the basic components of sequences. The left-most block is a sequence. It contains a body that is responsible for creating a sequence item. The driver is responsible for requesting a sequence item, then sending that item to the DUT, and, optionally, sending a response back to the sequence. A typical response is the return data on a processor bus read. In between the sequence and the driver is the sequencer. It is responsible for coordinating between the sequence and the driver.
Sequencer Sequence Body() loop apply_operation(); end end Driver loop get(operation); send_to_dut; put(response); end

Arbitrate

DUT

Figure 1: Basic Sequence Connection

Multiple sequences may run in parallel. The sequencer is responsible for arbitrating between all the sequences connected to it. Figure 2 shows multiple sequenceseach of which have been started in parallel communicating with the sequencer. In this case, each sequence will request to send an operation, and the sequencer will arbitrate between the sequences, as well as providing various services, such as locking and randomization of items.

Overview of Sequence Based Stimulus Generation in OVM 2.0

Figure 2: Multiple Sequences

Modularity of Sequences
Sequences may be used to build a test from smaller, often reusable, stimulus objects into a full test suite. The building blocks are individual sequences that encapsulate specific functions, such as protocol-specific operations or module specific configurations. These can be called from higher-level sequences to create a complete, complex stimulus set.

Sequential Child Sequences Sequential sequence execution is most often used to configure a DUT before test stimulus is applied and collect DUT status information at the end of a test. By having a sequence call multiple child sequences in parallel, a full test can be built out of multiple sub-objects.

Figure 3: Basic Child Sequences Run Sequentially

Note that configuration and data collection operations are often shared across many tests and projects. Sequences that provide common functionality provide a simple mechanism for test reuse.

Overview of Sequence Based Stimulus Generation in OVM 2.0

Parallel Child Sequences After configuration is complete, a test may run a variety of sequences in parallel on a particular port in order to provide realistic, complex data patterns that can be easily created and controlled. Each individual sequence provides a serial stream of operations, such as a read-modify-write, while parallel sequences mix in different streams. One example of this is when separate virtual circuits in a network, generated in individual sequences, are combined onto a single driver.

Figure 4: Parallel Sequences

A complete test generally consists of both serial and parallel sequences, running in different combinations. A simple example of this is when a serial sequence is used for configuration before parallel sequences generate the actual test stimulus. Once the test stimulus is complete, another serial sequence can be used to check the DUT error status or report the DUT results. In the example shown in figure 5, the configuration is done serially, and then the three test1 sequences are executed in parallel, followed by test2. When all tests are done, the report sequence is called.

Figure 5: Sequential and Parallel Sequences

Overview of Sequence Based Stimulus Generation in OVM 2.0

Breaking the test into a collection of individual sequences has several benefits. Each sequence is easier to write, since it is concerned only with a specific function. If a specific function is contained within a sequence, it can be easily reused any time that function is required. Sequences can be generalized, which makes them highly reusable. When many smaller objects run in parallel, it may be easier to feed a more random mix of stimulus into the DUT by changing the frequencies or number of the sequences being run. This can be controlled through configuration or the factory without changing the underlying testbench or sequences.
Distribution of Parallel Sequences

When multiple parallel sequences are all requesting access to the driver simultaneously, the sequencer arbitrates between them. By default, sequence requests are arbitrated on a first-in-first-out (FIFO) basis, but the user may specify one of three arbitration methods for a sequencer: 1. First In First Out (SEQ_ARB_FIFO) FIFO distribution means that each sequence request is granted in the order it was received. This is the default distribution. 2. Random (SEQ_ARB_RANDOM) The random distribution chooses an available sequence randomly. There is no ordering or priority. If a sequence has a request pending, and is not locked out, it has an equal chance of being selected. 3. Weighted Random (SEQ_ARB_WEIGHTED) Weighted random allows sequences to be selected randomly, but with different probabilities of being selected. Each sequence has a default weighted priority of 100. The user can change the weighted priority of a sequence at any time. A sequence with a weight of 200 is twice as likely to be selected as a sequence with a weight of 100. Weighted priorities are positive integer values. Only the ratio between weight values matter. 4. Strict FIFO (SEQ_ARB_STRICT_FIFO) Strict FIFO distribution means that only sequences of the highest priority will be granted. Those sequences that are of the highest priority will be granted in the order that the requests were received. 5. Strict Random (SEQ_ARB_STRICT_RANDOM) Strict random distribution means that only sequences of the highest priority will be granted. Those sequences that are of the highest priority will be granted in random order. 6. User (SEQ_ARB_USER) User distribution means that each time the sequencer must choose a requesting, non-blocked, relevant sequence, a virtual method (user_priority_arbitration) is called to determine which sequence to choose. The default implementation of this method is FIFO. Users may extend the sequencer and define their own arbitration method.

Separation of Testbench and Stimulus


A verification testbench is generally a stable environment. The agents and RTL-level connections are defined at the architectural level and, once implemented, tend to change infrequently. Once functional, many verification engineers are dependent on it, so minimizing modifications to the testbench is important. Sequences allow a verification engineer to develop stimulus and include it in a test without needing to alter the testbench in any way. This separation of structure from behavior simplifies the test writing task and helps ensure that the activities of one engineer are not likely to affect anyone else using the testbench.

Overview of Sequence Based Stimulus Generation in OVM 2.0

Figure 6: Separation of Stimulus from Testbench

In figure 6, several kinds of OVM objects permit sequences to be controlled for each particular test. (These concepts are only mentioned here. They are discussed in more detail in the OVM reference manual.) Configuration is used to control the behavior of a sequence or any other OVM component from outside the testbench. This permits easy customization and control of the test flow. The OVM factory provides control of which individual objects are instantiated throughout the design. Again, that control can be from outside the testbench so that test behavior can be modified as needed without editing the testbench itself. These qualities allow the test environment to be modular and reusable, while providing a great deal of control to the verification engineer. Sequence sharing allows engineers to benefit from each others work only when intended.

Stimulus for Layered Protocols


Complex protocols are often made up of several sub-protocols. This is particularly frequent in networking applications, where packets may be broken into cells or where one type of protocol is encapsulated in another, such as Ethernet over SONET and Multi-Protocol Layer Switching. Often it is important that different layers of a protocol are created and checked independently of each other. For example, when the DUT is able to manage or check traffic at different layers, then the stimulus will generally need to be created and controlled in a layered fashion as well. Layering of stimulus allows each protocol level to be generated separately, modified separately, and sent to the scoreboard independently of activity in other layers. This allows modifications to be done at each level individually. One common use of this is to allow error packets to be injected at any particular layer without changing the original stimulus generation or affecting any other layer.

Overview of Sequence Based Stimulus Generation in OVM 2.0

Figure 7: Multiple Layers with Error Injection Sequence

Packaging and Reuse of Stimulus


A verification IP (VIP) provider often wants to provide stimulus as part of their IP in order to: Demonstrate the functionality of the IP Provide a series of protocol specific tests Allow the IP to start operating out-of-the-box Stimulus generation allows protocol-specific tests to be packaged as part of the VIP. When stimulus generation is a part of the VIP package, users can quickly use the IP with just configuration knobs or perform complex protocol operations using the existing stimulus sequences.

Implementation
The OVM sequence package is implemented with minimal user effort.

Object Declaration For most drivers, a single ovm_sequence_item type is sufficient to provide the driver with all the information it needs to execute a transaction. The driver may optionally send a response ovm_sequence_item back to the sequence at the end of the operation.
Three components must be declared: the ovm_driver, the sequencer, and the sequences. They may be declared statically, as shown below, or the factory could be used to create sequences dynamically at run time. The components are parameterized with a request ovm_sequence_item type that is used to execute a transaction from the sequence to the driver and for a response ovm_sequence_item that may be used to send the result of an operation from the driver back to the sequence. Even if the response isnt used, all components will be parameterized with a response type that matches the request type by default. It is important that all components for a particular type be parameterized the same way.

Overview of Sequence Based Stimulus Generation in OVM 2.0

class my_driver #(type REQ = ovm_sequence_item, type RSP = ovm_sequence_item) extends ovm_driver #(REQ, RSP); endclass class sequenceA #(type REQ = ovm_sequence_item, type RSP = ovm_sequence_item) extends ovm_sequence #(REQ, RSP); endclass class bus_req extends ovm_sequence_item; class bus_rsp extends ovm_sequence_item; ovm_sequencer #(bus_req, bus_rsp) my_sequencer; sequenceA #(bus_req, bus_rsp) sequence_a; ovm_driver #(bus_req, bus_rsp) my_driver;

Note: the REQ and RSP types must inherit from ovm_sequence_item; and the parameterization of driver, sequencer, and sequence must all match.

In the new function, the components must be newed.


function new(string name, ovm_component parent); string str; super.new(name, parent); my_sequencer = new("sequencer", this); sequence_a = new("sequence"); my_driver = new("driver", this);

Although not shown in this example, sequences are often created from the factory to allow for overrides.

The driver is connected to the sequencer through the ovm_seq_item_pull_port. As with all TLM ports, this port must be connected for the driver to communicate with the sequencer. The connection call must be made after the sequencer and driver have been constructed. The connect() phase is the recommended phase to make connections. The following call will provide the connection:
my_driver.seq_item_port.connect(my_sequencer.seq_item_export);

After these calls are made, all the components are ready for sequences to start sending operations.

Sequence-Sequencer API
Starting Sequences

A sequence will only start operation when the start() function is called. The start function calls the body of the sequence, which is where the operation of the sequence should be implemented. The start function takes three parameters: a pointer to the sequencer, a pointer to the parent sequence, and an optional weighted priority. The parent_sequence and priority arguments are optional.
virtual task start(ovm_sequencer_base sequencer, ovm_sequence_base parent_sequence = null, integer this_priority = 100);

Overview of Sequence Based Stimulus Generation in OVM 2.0

Parent start

After the declarations above are complete, the following code can be used to start a parent sequence. This should be called from within the run task or some other task that is called when the simulation is in the run phase.
sequence_a.start(my_sequence_controller, null, weighted_priority = 100);

This task will return after the sequence is finished.


Child start

A sequence may wish to start child sequences or other parent sequences. The sequence can itself start child sequences.
sub_sequence.start(m_sequence_controller, this, weighted_priority = 100);

This call will start a child sequence. The sequencer pointer m_sequence_controller is set in the sequences base class. The second argument specifies the parent of the sequence, which is the current sequence in this example.
Serial start

Calling the start method of several sequences causes them to be executed in serial. If a configuration sequence is run before the test, and a cleanup sequence is run afterwards, calling the start() method causes the sequences to run sequentially.
config_sequence.start(m_sequencer, this); test_sequence.start(m_sequencer, this); cleanup_sequence.start(m_sequencer, this);

Parallel start

To run sequences in parallel, the SystemVerilog fork-join construct is used to run sequences concurrently.
config_sequence.start(m_sequencer, this); fork test1_sequence.start(m_sequencer, this); test2_sequence.start(m_sequencer, this); join cleanup_sequence.start(m_sequencer, this);

The test sequences will run concurrently, and, in this example, the cleanup sequence runs after both of the test sequences are complete.
Setting Priority

In addition to setting the weighted priority from the start call, it is also permitted to set the weight directly:
sequence.set_priority(int new_priority);

When the set_priority() call is made, the current weight of the sequence changes and the sequencer uses the new weight from that point on. Note that the sequencer only considers sequence priority values when it is in one of the SEQ_ARB_WEIGHTED or SEQ_ARB_STRICT_xxx distributions. The sequence weight_priority value has no affect on SEQ_ARB_RANDOM or SEQ_ARB_FIFO distributions.
Overview of Sequence Based Stimulus Generation in OVM 2.0 9

Executing Operations

In the body of the sequence, operations contained within ovm_sequence_items are sent from the sequence to the driver. A response ovm_sequence_item may send the results of the transaction back to the sequence. Several tasks are available as a convenience to the sequence writer. These tasks do the following operations: 1) Send a request to the sequencer 2) Wait for a grant to be received from the sequencer 3) Randomize the item if desired. Because the driver is waiting for the sequence item at this point, this is late randomization 4) Send the ovm_sequence_item to the driver 5) If applicable, get the ovm_sequence_item response from the driver Note that the randomization step happens at the simulation time when the driver is ready to accept this particular ovm_sequence_item, which may be considerably later than when the apply operation was first called. This is referred to as late randomization.
wait_for_grant
virtual task wait_for_grant(integer item_priority = -1 bit lock_request = 0);

The wait_for_grant operation sends a request for arbitration to the sequencer. It will return when the sequencer has granted this sequence. Once the wait_for_grant call returns, the sequence must issue a single send_request. No time delays should be introduced between the wait_for_grant and send_request; however, randomization, or communication with other components, may occur between these calls. The item_priority argument is used when this specific item overrides the default priority of this sequence. If set to a value, this particular item will use the value as the priority. If no value is specified, or if the value is left as a -1, then the sequences priority is used. If set to another value, then this particular item will have the specified priority. Again, note that the sequencer only considers priority values when it is in certain arbitration modes. The lock_request argument can be used to request the sequencer to lock when this item is granted. By default the sequencer will not be locked. Locking is discussed below.
send_request
function void send_request(ovm_sequence_item request, bit rerandomize = 0);

The send_request function must be called exactly once after each wait_for_grant is called. This function sends the parameterized item to the driver. Note that this item must be parameterized the same way for the sequence, sequencer, and driver.

Overview of Sequence Based Stimulus Generation in OVM 2.0

10

Responses

There are two methods for a sequence to get responses from the driver. When a sequence calls get_response(), the sequence will wait until a response is available and then return with the response. This method is useful for an inline coding style, where a specific response is desired after a request. The most common example of this is a processor test that issues a read then waits for the read response. This is the default method. The second response method is to have all responses call a function in the sequence. This function is responsible for determining what to do with the response. This method is generally desirable where responses are not directly related to requests. Responses may be sent to a scoreboard or, in the case of layered protocols, may be sent to a higher-layer sequencer. This is implemented using the response_handler method. Each time a response is received for the sequence, the sequencer calls the response_handler, with the response as a parameter.
use_response_handler
function void use_response_hanlder (bit enable);

The use_response_handler function specifies how responses are to be handled by a sequence. When called with enable set to 0, the get_response method is used. When called with enable set to 1, the response_handler method is used. Note that each sequence controls its own response method independently.
get_use_response_handler
function bit get_use_response_handler ();

This method returns the current setting of use_response_handler. When 0, the get_response method is used. When 1, the response_handler function is used.
get_response
task get_response (output RSP response, input integer transaction_id = -1);

When get_response is called, it will wait until a response is received from the driver. This call should be made only when the driver is sending responses. Otherwise, the get_response call will hang, since no response is coming from the driver. The optional transaction_id field may be used to wait for a specific response. If this field is not -1, the get_response call will wait for a response whose transaction_id matches the parameter passed in the get_response call.
response_handler
virtual function void response_handler (ovm_sequence_item response);

When use_response_handler is called with enable = 1, all responses to this sequence will be returned by calling the response_handler function. The default implementation of this function is empty, so responses are discarded. The user may extend this method to handle responses as they are received by the sequencer.

Overview of Sequence Based Stimulus Generation in OVM 2.0

11

When enabled, the response_handler method is called by the sequencer each time it receives a response from the driver that is meant for this sequence. The extended response_handler function must determine what to do with each response.
pre_body and post_body

Two callback tasks are defined in ovm_sequence: pre_body() and post_body(). The default implementation of these tasks is empty.
virtual task pre_body(); virtual task post_body();

When the start method is called, a sequence calls the pre_body task before the body is called. At the completion of body, the post_body task is called. The pre_body and post_body calls are useful to perform set up before a test is run or do a cleanup or check at the completion of a test. For example, a test that is going to drive a specific quantity of packets may increase a threshold value in the DUT so that the packets are less likely to be dropped if the test is running in parallel with other sequences. Once done, the threshold may be set back to its previous value in the post_body function.
Locking

A sequence may issue a lock in order to gain exclusive access to a driver.


task lock();

A lock request is granted by the sequencer after all earlier issued requests are completed. A sequence is informed that the lock is granted when the lock call returns. Once a lock has been granted, only requests from this sequence or children of this sequence are granted by the sequencer. Arbitration still continues normally between any sequences which are permitted by the lock. Locks are hierarchical. When a parent sequence obtains a lock, a child sequence may also request a lock. Once the lock is granted, the child and its children have exclusive access to the driver. The parent is locked out, even though it also has a lock.
task lock();

Unlocking may be done only by the sequence that requested the lock. In the case of hierarchical locks, the parent is allowed to continue its locked operations only after the child releases its lock. Once that happens, the parent may complete its locked operations and release the parent lock. The unlock call returns immediately. Unlock operations are not queued.
is_relevant

The time between a sequence issuing wait_for_grant and the sequencer granting the request is indeterminate. A sequence has the ability to delay an object after the request has been issued, if it is not ready. This is done using the is_relevant function. By default, this function returns a 1, indicating that the request is valid. Should the function return a 0, then the controller will ignore a pending request from the stimulus generator. There is no mechanism to remove a requested object, only to delay it. The only way for the stimulus generator to request another item be sent is to have is_relevant return 1 and wait until the sequencer grants the request.

Overview of Sequence Based Stimulus Generation in OVM 2.0

12

The only common use of is_relevant is in layered protocols. A lower-layer sequence may have is_relevant return 0 when it is unable to obtain an ovm_sequence_item object from the higher layer. The has_do_available call, described in the driver-sequencer API below, is generally used to determine if an ovm_sequence_item is available.
virtual function bit is_relevant();

wait_for_relevant

If a sequence implements is_relevant, then it must also implement wait_for_relevant. This task is needed by the sequencer to determine how long to wait before calling is_relevant again. When a sequencer arbitrates for a sequence, it will call is_relevant on all currently requesting, unblocked sequences. If all available sequences return is_relevant of 0, then the sequencer must wait before trying to arbitrate again. The sequencer will call the wait_for_relevant task and re-arbitrate when this task returns.
virtual task wait_for_relevant();

As with is_relevant, the most common use of wait_for_relevant is in a layered protocol. The lower-layer sequence may call the upper layer get, or it may have access to a protocol specific clock to implement a meaningful delay.
is_blocked

A sequence may determine if it is currently prevented from accessing a driver due to a lock by another sequence. The is_blocked() function will return 1 if another sequence currently has a lock and 0 if there are currently no locks that prevent this sequence from accessing the driver.
function bit is_blocked();

Note that the is_blocked function shows only the current status of the locks. If another lock is queued, or if another sequence calls lock immediately following the return of the is_blocked call, this sequence may not be able to call apply() before another sequence obtains a lock.
display_queues

The sequence_controller provides a debugging function:


function string display_queues();

The display_queues function returns a string that displays all of the queued requests for operations and locks and shows the i.d. of all sequences that currently own a lock. The format of the output of this function may change at any time and should be used only for debug purposes.

Driver-Sequencer API The ovm_driver uses methods in the seq_item_port to get operations from the sequencer and to optionally send responses back to the sequencer. The following methods in the seq_item_port are used for this communication. Note that this list provides the most common methods. For a complete list of methods, refer to the OVM 2.0 reference manual.

Overview of Sequence Based Stimulus Generation in OVM 2.0

13

get
task get(REQ request);

The get task will block until an item is available. It will return only when a valid item is received from a sequence. This call is equivalent to calling get_next_item, followed immediately by item_done. When this call is made, the sequencer will arbitrate to find the next requesting, non-blocked, relevant sequence. The wait_for_grant method of that sequence will return so that the sequence issues a send_request call. The parameter in the send_request will be returned by this get call.
try_next_item
task try_next_item(REQ request);

The try_next_item task provides a non-blocking method to get a transaction from a sequence. If the sequencer determines that no sequence is available, try_next_item will return with a null request. If an item was successfully received, then the request parameter will have the request. After try_next_item successfully receives an item, the driver must call item_done to indicate to the sequencer that the item has been processed. Note that the get method automatically calls item_done.
item_done
Function void item_done (RSP response = null);

The item_done function must be called after try_next_item successfully receives a request. An optional response may be sent back as a parameter to this call. Alternatively, the response may be sent back in a put call. In any case, a single response may only be sent back once (although multiple responses could be sent for a single operation).
put
task put (RSP response);

The put task is called to send a response back to the sequence.


set_id_info

Set_id_info is actually a method of ovm_sequence_item. It is used to correlate a response to a request. When the driver creates a response to send back to the sequence, set_id_info provides the information needed to route a response back to a sequence. To use this method, use the following call:
rsp.set_id_info(req);

This call must be made after a valid request has been received and before a response is sent back to the sequence. This operation ensures that the response is sent back to the specific sequence that issued the request and that the transaction_id of the response matches that of the request. This is particularly important when the driver handles multiple simultaneous requests and provides out-of-order responses.

Overview of Sequence Based Stimulus Generation in OVM 2.0

14

has_do_available

The has_do_available method allows a driver to determine if there is at least one unblocked, relevant, requesting sequence available. If this method returns 1, then at least one sequence is currently requesting and available to send a transaction to the driver. To use this method, use the following call:
function bit has_do_available();

Example Drivers Below are two example drivers, first a blocking driver using get and put, then a non-blocking driver using try_next_item. They are similar except that the non-blocking driver has the opportunity to drive idle cycles on the bus when no operation is available.
class my_blocking driver extends ovm_driver #(REQ, RSP); function new (string name, ovm_component parent); super.new(name, parent); endfunction task run(); forever begin seq_item_port.get(req); < do req bus cycle > rsp = new(); rsp.set_id_info(req); seq_item_port.put(rsp); end end endtask endclass // Blocking call

Note: set_id_info must be called to allow the sequencer to route the response back to the requesting sequence.

class my_non_blocking driver extends ovm_driver #(REQ, RSP); function new (string name, ovm_component parent); super.new(name, parent); endfunction task run(); forever begin do begin seq_item_port.try_next_item(req); if (req == null) begin < do idle cycle > end end while (req == null); < do req bus cycle > seq_item_port.item_done(); rsp = new(); rsp.set_id_info(req); seq_item_port.put(rsp); end end endtask endclass

// Blocking call

Note: set_id_info must be called to allow the sequencer to route the response back to the requesting sequence.

Overview of Sequence Based Stimulus Generation in OVM 2.0

15

Sequencer Specific Methods The distribution method used by the sequencer defaults to SEQ_ARB_FIFO. The user may change the arbitration method at any time using the set_arbitration function.
set_arbitration

The set_arbitration call takes the arbitration method as an argument. This is an enum with six legal values: SEQ_ARB_FIFO, SEQ_ARB_RANDOM, SEQ_ARB_WEIGHTED, SEQ_ARB_STRICT_FIFO, SEQ_ARB_STRICT_RANDOM, SEQ_ARB_USER.
set_arbitration(ARBITRATION_TYPE value);

The arbitration type may be changed at any point in time during a simulation. When changed, the new arbitration method is used from that point forward.

Conclusion
The OVM sequence facility provides a tool for meeting the requirements of complex stimulus generation. In combination with other OVM capabilities, in particular configuration and the factory, sequences allow users to create, control, and reuse stimulus. for complex DUT verification requirements and layered protocols.

For more information, call us or visit: www.mentor.com


Copyright 2008 Mentor Graphics Corporation. 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 purposed only, provided that this entire notice appears in all copies. In accepting this document, the recipient agrees to make every reasonable effort to prevent the unauthorized use of this information. All trademarks are the property of their respective owners.

Corporate Headquarters Mentor Graphics Corporation 8005 S.W. Boeckman Road Wilsonville, Oregon 97070 USA Phone: 503-685-7000 Sales and Product Information Phone: 800-547-3000

Silicon Valley Mentor Graphics Corporation 1001 Ridder Park Drive San Jose, California 95131 USA Phone: 408-436-1500 Fax: 408-436-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-3033 Fax: 81-3-5488-3004

5781:080916