Building Scalable, Unified, Reusable Verification Platformfor Complex SoCs Using Synopsys’ Vera and RVM


Zhang Tao, Huang Kan, Zhou Zheng, Feng Yi
Microprocessor Research and Development Center Peking University

ABSTRACT

Simulation is still the primary workhorse for verifying the functional correctness of hardware designs. We propose a scalable, unified and reusable platform for complex System-on-Chip functional verification using Synopsys’ Vera and RVM. This platform has unified interfaces for various IPs and VIPs. We are able to integrate various IPs, such as PCI Bridge, Ethernet MAC, USB OTG controller and IDE ATA-7 controller, together with their VIPs into our platform conveniently. Our experiment result shows that it greatly reduces the time to build the verification environment and increases the functional coverage using constraint random test generation.

1 Introduction

As the size and the complexity of designs continue to grow up, we are facing the serious challenge of hardware verification. We integrate a number of IPs to implement complex I/O transactions while we can not train more verification engineers in the short time to market.

According to the traditional methodology, we have to write bundles of complex directed tests in HDL language, which are difficult to debug. Even though, we still can neither cover all the corner cases nor simulate the extreme traffic. Furthermore, it is completely unknown about the percentage of functional coverage we reached. Parts of bugs have to be found via FPGA prototyping, which is even more time-consuming.

Consequently, we urgently demand new tools and methodology to support up-to-date verification technology, such as constraint random verification and functional coverage which can raise the efficiency of hardware verification.

In our latest project, we have built a complex System-on-Chip (SoC) including numbers of IPs (shown in Figure 1). Because most of them are integrated for the first time, all-sided system-level functional verification work is unavoidable. We use Synopsys’ Vera and RVM (Reference Verification Methodology) to build a brand-new verification platform. We denominate it “SURP” standing for a Scalable, Unified and Reusable Platform.

2 Design Goal of SURP

Our SURP fulfills the increasing requirements of SoC verification, because of the following features:
● Scalable
Any VIP can be easily integrated into the platform. The VIP may be a Verilog behavioral model, a FlexModel or a VMT (Vera Modeling Technology) VIP. In addition, any IP with special features and functions can also be verified in the platform. So the platform should provide flexible interfaces and services for scalability.

● Unified
As a platform, SURP should have unified interfaces for various IPs. So it needs to be a layered platform. The higher the layer is, the less IP-related details are involved. All the IPs should use consistent interfaces and services in the high-level layers. In the low-level layers, the detailed design for each IP can be different from others.

● Reusable
If the verification platform can be reused smoothly in different projects, the verification cycle will be sharply decreased and the correctness of the results can be guaranteed. Through several projects’ accumulation, there will be a lot test suites for a specific IP. When a new IP comes up, we can reuse these test suites to verify this IP immediately. Besides, since we use the same verification flow for various IPs, we speed up our verification.

● Functional Coverage & Constraint Random Test
Nowadays the verification methodology based on functional coverage and Constraint Random Test (CRT) become more and more popular in complex SoC’s verification. Functional coverage can give us information of the efficiency and effectiveness of verification. CRT is a powerful method to achieve the functional coverage goal and almost cover the corner cases. Thus we can sign off the design with enough confidence.

● Multi-Port Processor & Multi-layered Bus
In our project, there is a two-port processor, one is connected to the 64-bit width AHB bus and the other one is connected to the 32-bit width AHB bus. The former’s clock frequency is doubled than the latter’s .

To model the processor, SURP has to provide a multi-port processor model. This model should have two suites of AHB-compliant bus interfaces and an arbiter to schedule these interfaces’ behavior.

Due to those features, we can surpass our previous verification platform and we can be released from the trivial works in verification.

3 Features of Synopsys’ Vera and RVM

Making the decision to build our platform using Synopsys’ Vera and RVM is based on these features below:

● Object-Oriented Program Supported
OpenVera supports object-oriented programming. So we can employ all the advantages of the object-oriented methodology such as encapsulation, inheritance and polymorphism.

Figure 1:Our complex Soc including numbers of IPs.

We can model our designs in Unified Modeling Language (UML). So it is much easier to build a hierarchical platform and all the modules can be reused without extra efforts.

● Randomize Easily
It is difficult to generate random signals in traditional HDL testbenches. As a hardware verification language, OpenVera provides powerful support to randomization. Everything including the size of arrays and the sequences of commands can be random. To support advanced application, OpenVera provides several built-in methods to realize different kinds of statistical distribution and randomization algorithms. The more important thing is that signals can be easily constrained as we wish.

● Functional Coverage Supported
Functional coverage is the key metric of hardware verification. It can give us enough confidence to sign off. OpenVera provides a simple way to define and calculate the functional coverage. Vera can automatically generate a detailed report which tells us exactly where we have covered.

● Build Fast
RVM not only provides a powerful OpenVera class library, but also a complete methodology. The class rvm_channel and rvm_xactor help us building the transaction-level testbench. The class rvm_env defines a regular workflow and the class rvm_watchdog makes our platform safe and robust. The macro rvm_atomic_gen and rvm_scenario_gen are helpful when building a random transaction generator. The Common Message Service is convenient to generate a log file for our testbench.

● Debug Visually
Programmers will encounter many difficulties on debugging testbenches because most modules run in parallel. Since there is a built-in GUI debugger in Vera, we can use advanced techniques such as breakpoints, stepping and watches to debug our testbenches visually.

4 The Hierarchy of SURP

SURP follows the methodology advocated by RVM. From bottom to top, the whole platform has four layers: signal layer, bus layer, transaction layer and system layer. Figure 2 shows the hierarchy of SURP. Besides, there are some other function modules, such as interrupt routines, functional coverage and so on.

Figure 2:The hierarchy of SURP
Figure 3: Class Diagram of SURP

Signal Layer
It is the lowest layer of the platform. In this layer, the signals of the platform and the DUT (Design under Test) are connected together.

Bus Layer
It receives the transactions generated from transaction layer and translates them to bus signals.

Transaction Layer
It is the middle layer in SURP. This layer supplies the basic service to the upper layer and sends transactions to the lower layer. We can regard those services as basic commands to control the bus transfer.

Scenario Layer
This layer defines significative scenarios for each IP. This layer has two basic generators, atomic generator and scenario generator. The former generates individually constrained transactions and the latter generates sequences of transactions with certain random parameters.

System Layer
It is the highest layer of the platform. By calling the scenario defined in the scenario layer, it generates and schedules various scenarios for complex system-level tests. It can adjust the stimulus generation automatically according to the coverage report.

Functional Coverage
The module functional coverage collects all kinds of the coverage information and creates a coverage report. According to the feedback statistics, it guides the stimulus generator and further improves the coverage percentage.

The kernel of SURP consists of many classes. Figure 3 illustrates the relationship between those classes. The following passage will introduce some important classes.

4.1 Signal Layer

In this layer, there are two important members. One is interface and the other one is bind. The former is used to declare the interfaces connected to the DUT. The latter binds the interfaces with variables which will be used in SURP. Furthermore, some VIPs’ constructor, the task new(), have a parameter whose type is bind. It is used to declare which interface will be imported into the VIPs.

4.2 Bus Layer

This layer has only one class – bus_driver. This class instantiates the AHB MASTER, SLAVE and MONITOR VIPs. These VIPs have RVM-compliant interfaces, so we just need to connect the input and output channels to the transaction layer. The rest will be done automatically.

4.3 Transaction Layer

It is the most important layer in SURP. It can behave as real CPU executing instructions: scheduling the transactions, processing data and dealing with interrupts. It consists of several classes:

Figure 4:Detail Architecture of SURP

● xaction_driver:
The class has two channels, input and output channel. As one end of the output channel, been as the producer, this class will send the transaction to the other end of the channel which is the consumer. It implements four basic tasks: write_a(), read_a(), write_burst() and read_burst(). write_a() is to start a single write operation on the AHB bus. As the same, read_a() is to start a single read operation. write_burst() and read_burst() are responsible to initiate write and read burst transfer. These tasks provide service for the upper layer to build up the scenarios.

● xaction_dist:
For the requirement of the multi-port processor, we create this class to distribute the transactions, which is generated from the upper layer, to different AHB MASTER VIPs on the bus. All IPs’ stimulus will be registered to this class with its own priority. When the simulation starts, the class arbitrates which transaction will be granted to be executed by the priorities. More detailed content will be discussed in section 5.

● interrupt_dist:
This class is to detect the interrupt and inform the related ip_top immediately to call the interrupt routines when there is an interrupt in DUT. As soon as an interrupt is detected, interrupt_dist will record the interrupt address. Based on the interrupt address, the class will trigger on the corresponding ip_top誷 int_occur event until the interrupt is cleared. The error messages will be displayed if there is no interrupt routine to deal with the specific interrupt.

4.4 Scenario Layer

● ip_top:
This is a frame class which defines the basic framework and the common methods for each IP’s verification. Each IP’s top class will be derived from it. For example, if we want to verify the PCI bridge, a class named pci_top will be declared and it inherits and extends the class ip_top. Three important tasks are encapsulated in this class. init_t() defines how to initialize the VIP when SURP starts. stimulus_gen_t() is used to generate the stimulus. Both directed and random test stimulus in block level can be generated. Any scenarios can also be generated in this task. int_deal_t() is the interrupt routine for a specific IP. When an interrupt occurs, SURP will detect it and call the corresponding interrupt routine to deal with it. In order to implement those tasks, there are some local variables in this class. One of them is driver, an instance of the class xaction_driver. We can easily create some useful transactions and scenarios in the task stimulus_gen_t() with it. Another important variable is the event int_occur, which will be turned on as soon as the interrupt occurs.

4.5 System Layer

● base_env:
This class inherits the RVM’s basic class rvm_env. In this class, we develop the register scheme. Every IP’s test suites should be registered to base_env. And the class has placeholders to reference to each IP’s test suites. If we want to start or stop testing, we just call the start_t() or stop_t() and the class will automatically start or stop all the registered test suites.

● surp_env:
This class inherits the class base_env. An instance of this class stands for the SURP. The instances of each IP’s scenario generator (ip_top) need to be registered in it. When we call the task run_t() in surp_env, the whole system will start. In addition, it can schedule all scenario generators to create a complete test.

Based on all of the classes introduced above, we can easily verify all IPs via SURP.

5 Detail Architecture of SURP

In SURP, we adopt many techniques to make the platform more similar to the real system. They are very important for the performance of the system (shown in Figure 4). Following, we will introduce them for detail.

5.1 Register Scheme

The register scheme is used to let the platform know what and how many units are in it. There is not only one class using this scheme. All of them use the placeholder to stand for one unit. We adopt the smart queue in OpenVera to implement the placeholder. The placeholder must be declared as the super class of the class which we will use.

Figure 5: Architecture of Transaction Distribution

In fact, this technique is commonly used in any object-oriented languages. It’s known as “Upcasting”. We know that classes will inherit all attributes and tasks (functions) from their super class. So we can pass the child class as a parameter to substitute its super class’. An example is showed as follow.

In this example, class B is derived from class A. So class A is the super class and class B is the subclass. Class B overloads the task write() which is declared in class A. Class C has a task write_article() whose parameter’s type is class A and uses class A’s write() to do something. In the function main(), we pass an instance of class B to the class C. While the program is executing at c.write_article(b), class B’s task write() will be executed instead of the one defined in class A.

In class base_env, there are placeholders whose type is rvm_xactor. And the function register_iptop() will push the instance of ip_top which is derived from rvm_xactor as the parameter into the smart queue and return an integer as the identifier. When the task start_t() is called, the platform will start and it will start all registered transactors using start_xactor(). The following is the source code.

When we instantiate the surp_env and call its task start_t(), the class xaction_dist’s own task start_xactor() will be executed.

Similarly, in class xaction_dist, there are placeholders whose type is rvm_channel. The function register_channel() will push each instance of rvm_channel as the parameter into the smart queue and return an integer as the identifier. At the same time, another smart queue is used to record the priority of each instance of rvm_channel. When the xaction_dist starts, it will get the transaction from the channel controlled by scenario layer and distribute them to the lower layer.

5.2 Transaction Distribution

As what we have mentioned before, SURP has two AHB master VIPs. So we need a unit to decide which VIP the transaction should be sent to. The class xaction_dist does this job. Figure 4 shows its internal architecture.

After all IP’s stimulus generators (ip_top) start, an arbiter in xaction_dist is used to arbitrate which transaction generated by scenario layer should be put into the bus layer. According to each IP’s priority, xaction_dist completes this action. Higher the priority is, earlier the transaction will be gotten from upper layer and transferred to the lower layer.

Though we have decided which transaction to be propagated, the next problem follows. Which AHB MASTER VIP should the transaction be transferred to? xaction_dist distributes the transaction basing on the access address of this transaction. When the address is among the lower 2G spaces (memory space), the transaction will be put to the first AHB MASTER VIP’s input channel. Otherwise, it will be put to the second AHB MASTER VIP’s channel (register space).

When there is a read operation on the bus, the data should be returned to the right stimulus generator. Because of usage of the two AHB MASTER VIPS, we use two smart queues as FIFO to record the identifier of each instance of ip_top who initiates the read transaction. When a returned transaction from AHB MASTER VIP’s output channel arrives at the distributor and it is a read returned transaction, it will get the corresponding identifiers from the FIFO that stores them based on the access address and put the transaction to the related ip_top’s input channel.

return_read_result() {
……
fork{
while(1) {
cast_assign(xact1, inchan_from_bus_driver_1.get_t());
if(xact1 != null && xact1.m_enXactType == READ)
ip_sq[unibus_driver_flag.pop_front()].inchan.put_t(xact1);
}
}
{
while(1) {
cast_assign(xact2, inchan_from_bus_driver_2.get_t());
if(xact2 != null && xact2.m_enXactType == READ)
ip_sq[membus_driver_flag.pop_front()].inchan.put_t(xact2);
}
}
}

5.3 Interrupt Distribution

This module imitates serial actions of CPU when there is an interrupt. The class interrupt_dist imports two signals, nIRQ and nFIQ, and one signal vector, INT_addr, through hdl-vera interface from the DUT. All of interrupts are active low. Figure 6 illustrates the connection between interrupt_dist and DUT. Since there are two kinds of interrupt, IRQ and FIQ, the class interrupt_dist has two events which are named FIQ_INT and IRQ_INT. When the nIRQ is active, event IRQ_INT will be triggered. So does FIQ_INT.

Figure 6:Architecture of Interrupt Distribution

Two tasks need us to describe. detect_int() is used to detect the interrupt. It use a “while” iteration to monitor the nIRQ and nFIQ signals cycle by cycle. Once an interrupt occurs, it will trigger the corresponding event. Meanwhile, it records the INT_addr. The detail is shown as follow:

detect_int(){
while(1){
wait_if_stopped_t();
wait_var(intc_ports.$nFIQ, intc_ports.$nIRQ);
@(posedge CLOCK);

if(intc_ports.nFIQ == 1’b0 && intc_port.$int_vec_sel == 1’b1) {
addr_reg =intc_ports.$INT_addr;
trigger(ON, FIQ_INT)
// detect the FIQ and trigger the event
}
if(intc_ports.nIRQ==1’b0 && intc_port.$int_vec_sel == 1’b1){
addr_reg = intc_ports.$INT_addr;
trigger(ON, IRQ_INT)
// detect the IRQ and trigger the event
}
}
}

jump_to_entry() is a task for switching to the entry of the interrupt routine. Because there are so many interrupts belonging to different IPs, we should know which IP is the source. So we also use the Register Scheme to find the source. When each IP’s ip_top is registered to the xaction_dist, it is also registered to the interrupt_dist with the same IP identifier. Class ip_top has a property to store its own interrupt address. When task jump_to_entry() detects either the IRQ or FIQ event is on, it will compare the interrupt address recorded by the task detect_int() with each ip_top’s interrupt address. If they are equal, the jump_to_entry() will trigger the event int_occur in the related ip_top and this ip_top will deal with the interrupt spontaneously. Following codes reveal the implementation.

int_response() {
while(1) {
sync(ANY, FIQ_INT, IRQ_INT);
trigger(OFF, IRQ_INT);
trigger(OFF, FIQ_INT);
jump_to_entry(this.addr_reg);
}
}
jump_to_entry(reg[31:0] addr) {
foreach(this.ip_sq, i) {
if(ip_sq[i].int_addr == addr)
rigger(ON, ip_sq[i].int_occur);
return;
}
}
}

5.4 Atomic Generator and Scenario Generator

5.4.1 Basic Implementation
The stimulus generator is another key module in SURP. RVM provides two kinds of random stimulus generator, the atomic generator and the scenario generator. The atomic generator is designed for directed tests and simple random tests while the scenario one is for complex and extreme random tests. Since the scenario generator with the default scenario descriptor is just an atomic generator, we only implement the scenario generator.
First, we define the surp_xaction

class surp_xaction extends rvm_data {
integer IP_TYPE;
integer direction;
integer number;
….
virtual function rvm_data copy(rvm_data to);
}

Secondly, we use RVM macros to define the channel and the generator

rvm_channel(surp_xaction)
rvm_scenario_gen(surp_xaction, “The SURP scenario generator”)

Thirdly we define the scenario descriptors. A scenario descriptor defines the number of transactions in each scenario and the combinations of multiple transactions.

For example, we want to verify the arbitration of our bus module. We need to generate a number of transactions from multiple IP modules simultaneously. Consequently, we define the scenario descriptor as below:

class bus_traffic extends surp_xaction_scenario {
integer BUS_TRAFFIC;
constraint bus_traffic_scenario {
if (this.kind = = this.BUS_TRAFFIC) {
length = = 100;
foreach (this.items,i) {
case ( i % 5)
{
0: this.items[i].IP_TYPE == MAC;
1: this.items[i].IP_TYPE == USB;
2: this.items[i].IP_TYPE == IDE;
3: this.items[i].IP_TYPE == PCI;
4: this.items[i].IP_TYPE == AC97;
// Every 5 transactions are from 5 different modules
}
this.items[i].number == 1;
}
}
}
task new() {
super.new();
this.BUS_TRAFFIC = define_scenario(“Bus traffic”, 100);
}
}

Finally, we instantiate both the scenario generator and the scenario descriptor in SURP.

surp_xaction_scenario_scn_gen = new (“”, “”, *, out_chan);
bus_traffic bt_s = new();
/* Add user-defined scenario to the generator */
scn_gen.scenario_set.push_back(bt_s);
scn_gen.start_xactor();

Because of the great numbers of scenarios the system verification requires, we also adopt the hierarchical organization of the scenario descriptors to make them reusable and scalable.

5.4.2 Use Callback Classes to Extend the Generators

After we complete defining the generators, we want to extend the generators for advanced usage such as displaying the generated transactions or injecting errors manually. So the callback classes are exerted.

For example, we want to display the details of transactions generated.

First, we define a function psdisplay() in the class surp_xaction:

class surp_xaction extends rvm_data{
….
virtual function string psdisplay();
….
}

Secondly, we call the function psdisplay() in the callbacks class.

class my_surp_xaction_scenario_gen_callbacks
extends surp_xaction_scenario_gen_callbacks {
virtual task post_scenario_gen_t( surp_xaction_scenario_gen gen,
surp_xaction_scenario scn) {
printf(“The scenario is generated!\n”);
scn.display();
}
}

Finally, we register the callbacks instance into the generator in SURP.

my_surp_xaction_scenario_gen_callbacks cb = new;
scn.register_callback(cb);

5.4.3 Hierarchy of Stimulus Generator

We define a hierarchical structure for stimulus generators to make it scalable, unified and reusable. The class surp_xaction only includes the system-level information. It defines the protocol type, the direction and the number of the transactions. There is also a block-level generator in each instance of class ip_top. Immediately when ip_top receives a surp_xaction from the system layer, the generator in ip_top will generate a scenario correspondingly according to the scenario descriptors defined in an ip_top. The actual data transferred are generated in the second lowest transaction-level generators. The transactions generated in the transaction-level generators are delivered by two directions. One is to the lowest-level bus-level and the other is to the checker according to the transaction type defined in the check_xaction. The class check_xaction will be introduced in the next section.

Figure 7: Architecture of Checker
5.5 Checker

The module checker is designed to check the data integrity. It is unified for all types of transactions. And it is scalable via the register scheme.

As it shows in Figure 7, each data interface is check_xaction_channel. The class check_xaction is designed to generalize the format of the transactions, so it can be processed conformably. It is defined as below:

class check_xaction extends rvm_data {
bit [7:0] data[$];
integer mem_addr;
integer length;
boolean need_check;
virtural function boolean compare(check_xaction cx);
}

There is only one fixed interface in checker, which is connected to the memory model. It is used for collecting the data from the memory model. The flexible parts of checker are checkwares. The class checkware is defined as below:

class checkware extends rvm_xactor {
check_xaction_channel ip_chan;
check_xaction ip_sq[$];
check_xaction mem_sq[$];
integer MEM_START_ADDR;
integer MEM_END_ADDR;
virtual function boolean check();
}

ip_chan is the channel connected to ip_top;

The two smart queues ip_sq and mem_sq are designed to store the transactions captured from both the memory side and IP side.

MEM_START_ADDR and MEM_END_ADDR must be specified for each instance of checkware to determine

which part of the data in the memory will be gathered.

Task check() is the method for general comparison. The class checker is defined as below:

class checker extends rvm_xactor{
check_xaction_channel mem_chan;
checkware cw[$];
virtual function integer register_checkware(checkware cw);
task mem_xaction_dist();
}

The method register_checkware() is the implementation of the register scheme which is very similar to the other register methods in SURP.

The method mem_data_dist() is designed to distribute the data to the checkwares according to the memory address range defined in each checkware.

Finally, we instantiate and register as many checkwares as the number of IPs to be tested dynamically in the testbench.

Additionally, considering that error transactions intentionally injected do not need to be checked, we add a Boolean property need_check to the class check_xaction. So the checkware can determine whether to check the transactions correspondingly by this Boolean property.

5.6 Constraint Random Test (CRT)

In system verification, random test helps verification engineers save big effort to write the test vectors and find the deep bugs not found by normal means. But the test space is so huge that the complete random test is not useful for the reason of tremendous time consuming. Constraint random test makes the meaningful test vectors targeting functional coverage without wasting time in those unrelated cases. Furthermore, we use constraint random test to produce the “real world” traffic on the multi-protocol system. This section we describe the criteria on which we demonstrate how to assert hierarchical constraints on our stimulus generator to produce real system traffic.

Our stimulus generator is divided into four layers, constraints are designed according to each different layer: system layer constraints, scenario layer constraints, transaction layer constraints, and bus layer constraints. The lower layer constraints stick to the higher layer constrains to make the hierarchical constraints integrative. Due to the usage of VIP, we could get rid of the consideration of the bus layer constraints. So we get three layer constraints in all.

The system layer constraints are responsible for the common system environment conditions, such as reset sequence and the frequency of system clock and the components available, and control scenario distribution. The following table describes the system budgets.

The scenario layer is responsible for the parameters of the fragment function. Fragment function means the main functions of the IP referring to the mode under which the IP is working. For example, the ATA5 has three major fragment functions: PIO DMA and UDMA with the additional definition of writing or reading. The scenario layer decides which fragment function dominates the test. The following table illustrates the scenario budgets.

The transaction layer controls the transactions sequence, data value and data size. Transaction layer obtains configuration information from the scenario layer as an input, and derives the VIP parameters as an output. Actually, only this layer deals with specific random variables like transfer length, register’s value, exceptions, memory settings and so on. For example

After those hierarchical constraints, the transactions are put into the VIP channels to drive the signals to complete the desired transfer.

There are several considerations in the favor of our hierarchical constraint structure.

● Our hierarchical constraint architecture consists with our SURP’s stimulus generator architecture, obeying the rules that the higher layer do not need to know the details of its lower layer. This helps the verification engineers conveniently create the desired scenario.

● It is much understandable and easy to maintain and add new IP.

● It is important for a system with concurrent processes with interposes communication mechanism.

5.7 Smart Definition of Functional Coverage

There are major difficulties in defining the functional coverage. One is the completeness. The other one is the rationality. In order to overcome those difficulties, we exert new definition flow different from previous definition flow which is based on some functional coverage types such as stimulus coverage, response coverage, state coverage, interface-base coverage, feature-base coverage and corner-base coverage. Following is the interpretation of the new flow of hierarchical definition of system functional coverage.

First, according to the communication load of the real chip, we establish the highest level of function coverage concerning only the interactions of different masters and slaves on the system bus.

Secondly, according to the fragment function (mentioned in 5.6), we establish the second highest level functional coverage relating mere to different IP’s transfer types.

Thirdly, the most important one, the definition of functional coverage in details, is the most complex one.

● Describe the process of the transfer in natural language(e. g. English) following the pattern “Under conditions or initializations,Do,What,How many,Encounter er rors or exception”

● Use the semantics and terminology knowledge to find the key word corresponding to some event happening in the transfer as a functional coverage point or find a sequential actions corresponding to the sequential event as a tempo ral coverage point.

● Group the coverage points to two sorts: the IP_state coverage points which change the IP register values and the data_pattern coverage points which have relationship with the data transfer pattern including length and the like.

● Cross all IP_state coverage points with single data_pattern coverage points

● Use OpenVera coverage_group to implement different levels of functional coverage to set the goal of verification.
There are several advantages of using OpenVera to imple ment functional coverage quickly and effectively.

● It has the ability to sample various events from the signal level to the system level.

● It supports the temporal coverage point as well as the sampling coverage point.

● It supports recording cross coverage and supplies users with abundant information of coverage statistics.

● It also supports synchronously getting functional cover age information during verification, which makes the functional feedback mechanism possible.

5.8 Functional Feedback to Stimulus Generator

Redundant tests consume valuable CPU cycles. Although the CRT generates reasonable test vectors, without the direction of the functional coverage information, many of those test vectors are wasteful because they do not target at the functional coverage. That increases the verification cycle and decreases the efficiency of verification.

class ide_stimulus_gen extends ip_top {
ide_PIO_write_cov pio_write_cov;
task ide_stimulus_gen::new() {
pio_write_cov = new ();
}
task ide_stimulus_gen::ide_stimulus() {
If(pio_write_cov.query(NUM_BIN,
STATE, “.*PIO_WR_8BIT*”,GE,1)<10) {
ide_xaction = new(this.log,1,0,0);
}
If(pio_write_cov.query(NUM_BIN,
STATE, “.*PIO_WR_16BIT*”,GE,1)<10){
ide_xaction = new(this.log,0,1,0);
}
If(pio_write_cov.query(NUM_BIN,
STATE, “.*PIO_WR_32BIT*”,GE,1)<10) {
ide_xaction = new(this.log,0,0,1);
}
}
}

This section we demonstrate a method in which we use functional coverage information to direct the stimulus generator.
First, according to the multiple layers of stimulus generator and the multiple layers of functional coverage, we establish different feedbacks on each layer.

Secondly, use the query() system task to get functional coverage information during run time to modify the weight of parameter in the stimulus generator.

To establish the feedback mechanism, something should be considered before the building of the platform.

● Stimulus parameters are designed to map directly to one or more functional coverage points.

● The adjusting arithmetic of the stimulus parameters in the feedback mechanism.

5.9 End Conditions

It is necessary to set multiple end conditions to make our platform robust. Once the watchdog times out, the functional coverage reaches the goal or the directed tests finish, SURP will capture those events and terminate the simulation. It is also designed to be scalable via the register scheme. Besides, the watchdog timeout is the only fixed and higher-priority ending condition.

class surp_env extends base_env {
……
integer end_condition[$];
task register_end_condition(integer ec) {
this.end_condition.push_back(ec);
}
task wait_for_end_t() {
super.wait_for_end_t();
fork {
if(this.end_condition.size()){
oreach(this.end_condition,i){

//Wait for each end condition.
}
}
}
{
void=this.watchdog.notify.wait_for_t(this.watchdog.TIMEOUT);
rvm_error(log,”Watchdog timeout!”);
}
join any
}
}

6 Integration of Various VIPs

To speed up our verification, we integrate the Verification IPs into SURP (As shown in Figure 8). There is a big challenge that the VIPs we get are built in different kinds of technology and with different interfaces. Thanks to the reusability of SURP, it won’t take big effort to integrate all of them. Here is how we integrate four typical types of VIPs into SURP.

Figure 8 : Integration of Various VIPs
6.1 DW Ethernet VIP

Since SURP is built based on RVM, the DesignWare Ethernet VIP which supports RVM is integrated simply.

The VIP is defined as the subclass of class rvm_xactor and supports RVM input and output channel. The VIP has already defined the Ethernet transaction as the class dw_vip_ethernet_transaction. So we just need to write two parts to integrate the VIP. One is responsible to generate the instances of dw_vip_ethernet_transaction and send them to the VIP’s input channel to initiate the packet on the Ethernet bus for Ethernet IP, and the other is responsible to receive the transactions from the VIP’s output channel for checking the correctness of data.

6.2 DW USB OTG VIP

The DesignWare USB OTG VIP does not support RVM. So we have to design the RVM wrapper around the original VIP.

First, we define the wrapped VIP as usb_otg_wrapped_vip which is the subclass of rvm_xactor. So we can call the methods such as start_xactor(), stop_xactor() to control the VIP.

Next, we encapsulate all the properties related to USB OTG transaction to one class usb_otg_xaction, whose super class is rvm_data.

Finally, we implement all the corresponding transaction operation tasks in the wrapped VIP based on the methods provided by the original VIP.

Any features in RVM can be implemented within a single wrapper, but those mentioned above are necessary.

6.3 PCI FlexModel VIP

When we want to verify the PCI Bridge designed by ourselves, we choose the FlexModel provided by Synopsys. And fortunately, we find a OpenVera-based PCI VIP, pci_scnr. This VIP uses the FlexModel to build a layered verification environment. Our goal is to add this VIP to SURP. We have done this by following steps:
Create pci_top: pci_top inherits ip_top and adds the instance of pci_scnr into this class. So pci_top can use everything defined in pci_scnr.

Generate the test vector: we use different scenarios and commands which are all defined in pci_scnr to generate the test stimulus.

Instantiate pci_top: in surp_env, we instantiate the pci_top and register it into SURP. So pci_top can start and the stimulus can be generated.

Instantiate pcimaster and pcislave: because of using swift interface, the PCI FlexModel has a part of modules declared in Verilog HDL. It can be treated as the interface between OpenVera and Verilog. So we should instantiate the pcimaster, pcislave and pcimonitor modules in our DUT and connect the PCI hdl-vera interfaces to make the stimulus generated by OpenVera can be normally passed to DUT and the DUT’s response can also be returned to SURP.

After all the above steps, we complete the PCI FlexModel’s addition to SURP. And our PCI Bridge can be verified easily and smoothly.

6.4 IDE Verilog VIP

The industrial IP vendor provides us the IDE ATA5 IP with testbench of block level to test the IP in block level to verify the correct function and get the necessary code coverage. When we create the SURP, we face the problem how to reuse the testbench which is written by Verilog HDL in our SURP in system level.

In the favor of OpenVera’s vera-to-verilog hdl_task and verilog-to-vera hdl_task, we could integrate the Verilog testbench into SURP under control of the high level stimulus generator of SURP to make the complicated scenario for complex functional verification.

To use the vera-to-verilog hdl_task, firstly we create a hdl_task.vri file to declare the hdl_task you want to use in OpenVera and the hierarchical position of the hdl_task in Verilog.
hdl_task.vri

hdl_task DMAW(bit drive, bit[31:0] addr, bit[31:0] wdata) “U_top.U_bk3710.DMAW”;
hdl_task DMAR(bit drive, bit[31:0] addr, bit[31:0] rdata) “U_top.U_bk3710.DMAR”;
hdl_task PIOW(bit drive, bit[31:0] addr, bit[31:0] wdata) “U_top.U_bk3710.PIOW”;
hdl_task PIOR(bit drive, bit[31:0] addr, bit[31:0] rdata) “U_top.U_bk3710.PIOR” ;

When use those tasks in OpenVera files, we declare those as external tasks on the top of the files. Then we can use those tasks easily.

extern task DMAW(bit drive, bit[31:0] addr, bit[31:0] wdata) “U_top.U_bk3710.DMAW”;
extern task DMAR(bit drive, bit[31:0] addr, bit[31:0] rdata) “U_top.U_bk3710.DMAR”;
extern task PIOW(bit drive, bit[31:0] addr, bit[31:0] wdata) “U_top.U_bk3710.PIOW”;
extern task PIOR(bit drive, bit[31:0] addr, bit[31:0] rdata) “U_top.U_bk3710.PIOR” ;
#include <>
#include<>
ide_stimuluse_gen extends rvm_xactor
{rand bit [15:0] length;}
ide_gold_result()::ide_stimulus_gen
{
If(trans_dir == WRITE)
{
case(trans_type)
{
PIO:
{
for( i=1, i<=length, i++)
PIOW(drive,32’h13003f0, 32’FFFFEEEE);
}
DMA:
{}
}
else
{}
}

Thus we change the previous testbench which only suits for simple and directed test to the complex and random test to make sure the correctness of IP. We need not to reconstruct the components of testbench to verify the IDE IP in our SURP. All the favor supplied by RVM and Vera obviously

saves us a great deal of time and effort and makes us find bugs as quickly as possible.

7 Summary of SURP

SURP boosts our verification efficiency prominently. We used to spend six months to complete only directed test for one IP. In contrast, after the three months to develop SURP, we have spent only two months to finish the functional verification of all the IPs.

Due to the features of scalability, unification and reusability, we are able to upgrade the ATA5 IDE controller to a new ATA7 IDE controller in a short time, finishing the functional verification.

And the maintenance and organization of the test suites become easier than before. The code size of the test suites decrease by seventy percents.
In the future, we plan to take full advantage of Synopsys’ Vera and RVM to extend the functions of SURP, such as performance evaluation, software and hardware co-verification, and gate-level post-simulation with back-annotated timing information.

8 Acknowledge

We would like to give our sincere thanks to colleagues of Microprocessor Research and Development Center of Peking University. We appreciate for their consideration and suggestion to the verification platform. Thanks for the advices and inspiration from Prof. Cheng and Assoc Prof. Tong. And we would also give thanks to Synopsys’ technical support. Especially, it is grateful that Synopsys provides SNUG for users to share engineering experience.

9 References

[1] Vera User Guide, Version X-2005.06-SP1 September 2005, Synopsys.

[2] OpenVera Language Reference Manual: Testbench, Version 1.4.3 September 2005, Synopsys.

[3] Reference Verification Methodology: User Guide,
Version 8.6.4 April 2005, Synopsys.

[4] FlexModel User’s Manual, August 2004, Synopsys

[5] VMT User’s Manual, Release 2.78a October 24, 2005, Synopsys.

[6] Cristian Samoila, Jacon C, Chen, “Application of RVM for Vera On a Networking Design Case Study”, SNUG Boston 2004

[7] Kevin Thompson, Ladd Williamson, “Block-Based ASIC Verification Using Vera”, SNUG San Jose 2003

[8] Victor Besyakov, David Shleifman, “Constrained Ran dom Test Environment for SoC Verification using VERA”, SNUG Israel 2003

[9] Chris LeBlanc, Jeff Alderson, “Integration and Verifi cation of AMBA based IP”, SNUG Europe 2004

[10] Joachim Geishauser, Joachim Fader, Wolfgang Mair, “Eton - A Methodology based on Vera to Verify Complex SoC’s”, SNUG Europe 2005

[11] Michael Neudegger, Stefan Sojka, “Usage of an OpenVera Testbench for a Controller ASIC”, SNUG Europe 2005

[12] David Shleifman, Victor Besyakov, “Vera/Verilog Testbench Integration: Problems and Solutions”, SNUG Boston 2005

[13] Janick Bergeron, Dave Simmons, “Exploiting the Power of Vera: Creating Useful Class Libraries”, SNUG San Jose 2000

[14] Charles Li, Ashesh Doshi, “Five Vital Steps to a Robust Testbench with DesignWare Verification IP and Reference Verification Methodology (RVM)”, August 2005, Synopsys.Inc

[15] Neil Johnson, Hans van der Schoot, Ian Perryman, Branko Petrovic, Cristian Vasiliu, “Building Reusable Verification IP Using Synopsys’ Reference Verifica tion Methodology”, SNUG Boston 2004

[16] Fabian Delguste, Remi Francard, Shashank Raj, Harpreet Singh, “Verifying the ST BusSwitch using Vera and RVM”, SNUG Boston 2004

[17] Janick Bergeron, “Writing Testbenches: Functional Verification of HDL Models, Second Edition”, Springer 2003

10 Glossary of acronyms

AHB Advanced High-Performance Bus
CRT Constrained Random Test
DUT Design under Test
DW DesignWare
IP Intellectual Property
RVM Reference Verification Methodology
SoC System on Chip
VMT Vera Modeling Technology
VIP Verification Intellectual Property