Writing a RVM-Compliant AHB Master Transactor


Prof. Yang Zhi Jia
Zhang Yu Feng
Li Su Gang
Shenyang Institute of Automation
Chinese Academy of Sciences

Jason C. Chen
Synopsys Professional Services
Synopsys Inc.

yang@sia.ac.cn  zhangyufeng@sia.ac.cn  lisugang@sia.ac.cn
jasonc@synopsys.com

Abstract

The use of AMBA-based buses is ubiquitous in today’s System On Chips (SoC). Verification IPs (VIP) are effective in generating stimuli for block-level, subsystem-level, and top-level testbenches. The reference verification methodology (RVM) and its base class library for Vera help verification engineers to build a testbench that enables constrained random verification and promotes re-use. Keeping the transactors in an RVM style makes them have the same look and feel and consequently make them easier to use (e.g. when writing tests). This paper discusses how an RVM compliant AHB master transactor is designed using the DesignWare AHB Master VIP and demonstrates how to configure it for the testbench and how to create test stimuli using the transactor.

Keywords: AHB Master, DesignWare VIP, RVM base class, transactor, rvm_xactor

1. Introduction

Many of today’s System-on-chips (SoC) have one or more ARM-based processors and use AHB- and APB-based on-chip buses and peripherals. While using the design sign-off models (DSM) for the ARM processors is an important phase of the SoC verification to run native C or Assembly code on the processors, using the bus functional models (BFM) such as Synopsys’s DesignWare Verification IP (VIP) improves the simulation performance and enables the observability and the controllability for the verification platform to find design bugs faster. Examples of the DesignWare VIP components for an AMBA-based SoC testbench are the AHB Master, AHB Slave, and AHB Monitor (Figure 1).

With the introduction of the reference verification methodology (RVM) for Vera, the testbench development becomes easier. The RVM base class library has a rich set of base classes such as the rvm_data base class for modeling data transactions that flow through the design under test (DUT) and the testbench and the rvm_xactor base class for modeling transactors that process the transactions. The use of the base classes not only simplifies the creation of a complex verification environment but also makes the environment easier to understand, debug, and reuse.

Using VIPs can further shorten the time it takes to develop a testbench. Sometimes, however, a significant amount of ramp-up time is needed for the users of these VIPs to learn how to use the models, especially when a testbench may include VIPs from multiple vendors. If all these models had the same look and feel, one could argue that it would be easier and less error prone to integrate and use these VIPs in a testbench. In addition, some of these VIPs may have complex functionalities. If everyone needed to learn the details of these functionalities and how to effectively use the VIPs to write tests, one may begin to question if some leverage in both time and knowledge aspects is possible.

Consider the following scenario for a team of verification engineers. The team is functionally split into two groups:

● a “developer” group that provides a library of reusable verification components to be used by multiple projects, and

● a “user” group that uses components from this library and implements other project-specific components to meet their individual testbench and test requirements.

When no commercial VIPs are available, the “developer” group implements these proprietary verification components using the RVM methodologies and base classes. For verification components that are based on commercial VIPs, the “developer” group becomes the super users for these VIPs and creates RVM wrappers for them, if they do not exist already. The “user” group can now leverage the knowledge of the “developer” group and the same look and feel user interface of these testbench components to focus on what they need the components to do as oppose to the lower-level details of how to use the testbench components.

Advanced verification methodologies like RVM emphasize a good transaction data model with a lot of built-in smarts to enable constrained random verification and to make writing tests easier. For example, transaction attributes of an AHB transaction object describe what the transaction is and a transactor like the AHB Master Transactor understands how to execute the AHB transaction. To take this idea further, the “user” specifies the AHB transactions at the test level and lets the RVM-compliant transactor, implemented by the “developer”, take care of all the details of using the DesignWare AHB Master VIP to execute the described transactions. The test writers (the “user”), are now leveraging the expertise of the “developer” about the AHB bus protocols and the commercial VIPs and allows them to better focus their valuable engineering time on other testbench and test development responsibilities.

As a matter of fact, the “user” may not even need to know anything more than the intents of his test: “I want to do a 16-bit INCR4 write with a start address of 0xABCD with 0-2 busy cycles inserted between data transfers”. The bus master transactor, done by the “developer”, will take care of all the details under the hood.

The content of the paper is organized as follows:

1. Sections 2 and 3 will highlight the features of the AHB protocol, the DesignWare AHB Master VIP and the RVM base classes. It is noted that specific sections may be skipped by readers who are familiar with these topics.

2. Section 4 will serves a case study of developing an RVM-compliant AHB data model and AHB Master Transactor. This is the “developer”.

3. Section 5 will demonstrate how easy it is to use these RVM-compliant testbench components from the test stimulus perspective. This is the “user”.

2. AHB Protocol and DesignWare AHB Master VIP

The AHB specification is available in [1] and details of using the DesignWare AHB Master VIP, or AhbMaster in short, can be found in [3]. However, some key features for the protocol and the VIP are summarized here so that it helps provide sufficient context and background information to better understand the code snippets in the later sections of the paper.

2.1 AMBA 2.0 AHB

A granted bus master starts an AHB transfer by driving the address, transfer direction (write or read), size of the transfer, as well as an indication if the transfer forms part of a burst and the burst type. The AHB protocol also allows the bus masters to insert idle cycles in between transfers.

Different kinds of transfer sizes are supported and are encoded in the hsize[2:0] bus from a byte (8 bits), halfword (16 bits), to a word (32 bits) transfer and beyond. The AHB specification further requires that all bursts must be address aligned. This implies the address requirements (implemented as Vera constraints) as shown in Table 1.

Table 2 shows the supported burst types and the corresponding hburst[2:0] encoding to indicate the single transfer, incrementing or wrapping transfer and the number of transfers, or beats, in a burst.

The total number of data transferred in a burst can be calculated by multiplying the number of beats, indicated by hburst[2:0], by the size of the transfer in each beat, indicated by hsize[2:0]. This is useful because the AHB specification requires that all masters must not perform incrementing transfers over a 1k-byte boundary because the minimum address space that can be allocated to a single slave is 1 kByte.

While the full AHB system supports multiple AHB masters and slaves, a simpler AHB system, called AHB-Lite, is one that has just one AHB master. [2] provides an overview for the AHB-Lite specification.

For each transfer of an AHB burst, there exists a set of response attributes indicating the status of the transfer. Examples of these response attributes are: the number of wait cycles inserted by the slave, the transfer response (OK, error, etc.), and whether the transfer gets an early termination.

2.2 AHBMaster VIP

AhbMaster can be configured to specify the model settings in the specific testbench application. These configuration parameters, such as the data bus width, the endianness, as well as some response behaviors. Table 3 lists some of the configuration parameters for the AhbMaster. These configuration parameters are specified using the model’s set_config_param() command. Please refer to [3] for a complete list and more details of the configuration parameters.

A set of commands are available in AhbMaster to create a burst buffer, set transfer attributes of the burst buffer, initiate a burst cycle, and get the results and slave responses. Other commands can be used to change the model configuration parameters, handle messages, and to set up event notifications. Once again, please refer to [3] for a complete list of AhbMaster commands and how to use them. Table 4 lists some of the commands for the AhbMaster to create, set up and initiate the burst data transfer as well as to capture the response attributes of the transfer.

The programmability of AhbMaster with the configuration parameters makes it easy to instantiate in different testbenches that have different requirements.

AhbMaster implements the signal activities adhering to the AHB protocol. The command-based interface provides the users the abstraction layer to initiate bus transactions such as a bus read or a bus write.

3. Using RVM Base Classes for Vera

The RVM User’s Guide [4] presents the verification methodology as well as the class references for each RVM base class. Some of the base classes and their basic features are highlighted in this section.

3.1 Message Reporting Class – rvm_log

The message reporting class, rvm_log, provides a consistent display format for all messages issued by components of the testbench. Each message has an associated type and severity, amongst other things, so that the messages can be easily controlled by the verification environment as well as the tests. Some macros like rvm_debug(), rvm_warning(),rvm_error(), and rvm_fatal() are pre-defined in the library and they are very convenient to create single-line messages of different severities.

The verification environment manager, to be discussed in Section 3.4, keeps a cumulative count of the warning, error, and fatal messages and determines the overall success or failure of a simulation run.

3.2 Transaction Class – rvm_data

The rvm_data base class needs to be extended to include additional data members, or properties, to model the attributes of the transaction data. Constraints are used to specify the relationship between these properties. These data members and constraints are completely transaction specific. While there are no fixed rules on how to design a transaction data class, the RVM User’s Guide [4] provides useful guidelines and methodologies. Section 0 will present the thought processes behind the design of the AHB transaction data model based on rvm_data.

This base class provides a set of virtual methods that will require user-defined implementations in the derived class to provide commonly needed features of a transaction data object by testbench components. For example, the rvm_data::display() method can be used to provide debug information about the transactions as they flow through the testbench. As another example, the rvm_data::compare() method can be used by the checker to determine whether the actual transaction matches with the expected transaction the testbench predicts.

3.3 Transactor Class – rvm_xactor

The rvm_xactor base class defines a main processing task, rvm_xactor::main_t(), where a specific behaviour to process a relevant transaction mus be implemented in the derived class. Along with the rvm_xactor::main_t(), several standard management methods are defined by the base class. They are:

● task rvm_xactor::start_xactor(): this forks off rvm_xactor::main_t()

● task rvm_xactor::stop_xactor(): this blocks the transactor thread

● task rvm_xactor::reset_xactor(): this aborts rvm_xactor::main_t()

Based on a few simple questions, as shown in a flow chart in Figure 2, the answers to these questions will help define what the transactor physically looks like, i.e. its external interfaces. This is the first step to design a transactor.

It should be noted that the flow chart in Figure 2 needs to be modified to support more complex interfaces to the transactor (e.g. separate request and response channels, e.g. non-blocking interfaces, etc.).

The AHB Master Transactor will be used as an example to illustrate the design of the transactor and the AHB transaction specific details will be used to demonstrate how the transaction data, as well as the AhbMaster VIP model, are used in concert.

3.4 Environment Manager Class – rvm_env

A verification environment manager comprises all the testbench components from the interface drivers and monitors, the traffic generators to the response checkers and the coverage model. The tests, running as Vera program blocks, provide guiding information to the verification environment to create the test scenarios. For directed tests, the tests provide the actual stimuli. For constrained random tests, they typically provide extra constraints for the testbench configuration and the randomized transaction objects inside the traffic generators.

A key benefit for using the environment management class is that it manages the sequencing of the simulation phases. The RVM environment manager base class, rvm_env, separates a simulation flow into several discrete phases. The environment manager will orchestrate the execution of the test automatically by moving the simulation flow through the following phases, defined by the rvm_env base class [4]:

1. Generation of the testcase configuration

● task rvm_env::gen_cfg()

2. Building the verification environment

● task rvm_env::build()

3. Configuration of the DUT according to the generated test configuration

● task rvm_env::cfg_dut_t()

4. Starting all transactor and generators

● task rvm_env::start_t()

5. Detection of the end-of-test conditions

● task rvm_env::wait_for_end_t()

6. Stopping all transactors in an orderly fashion

● task rvm_env::stop_t()

7. Performing any final cleanup / statistics, etc. before the simulation ends

● task rvm_env::cleanup_t()

8. Reporting on the success or failure of the simulation run

● task rvm_env::report()

These discrete steps are designed in a way that the test can intervene at any phases as the test requires. The method rvm_env::run_t() will run what is left to do in the 8-step sequence. For example, a test may ask the environment to run up to the ‘build’ phase by calling rvm_env::build(). At this time, the test can introduce some test-specific code to execute, and then it can call the rvm_env::run_t() method to let the verification environment manager resume the simulation from the next ‘cfg_dut_t’ phase all the way to the ‘report’ phase. It will become evident, as shown by test examples in Section 5, how the same verification environment is effectively reused by the tests to accomplish their individual requirements.

4. The “Developer”: Designing a RVM-compliant AHB Master Transactor

This section takes a bottom-up approach to first discuss the transaction data object, the transaction object interface, internal components of a transactor like a message service object and a configuration object, and finally the RVM compliant transactor.

4.1 Transaction Object

There are four kinds of transactions for AHB, namely read, write, idle and read expect. They are declared using an enumerated type “kinds_e”. This is one of the many randomizable parameters for an AHB transaction and the declaration of these data members shall have the rand modifiers.

class ahb_trans extends rvm_data {

enum kinds_e = READ, WRITE, IDLE, READ_EXPECT;

rand kinds_e kind;

While the digital circuits work with the binary 1s and 0s, humans do not generally want to. While some may be very familiar with the defined encoded values in the protocol specification for the transaction attributes, most find it simpler to work with their meanings. Examples of such AHB transaction attributes are the burst type (hburst) and the transfer size (hsize). For example, it is easier and more descriptive for a test writer to describe the intended transfer size of 32 bits or a burst type of INCR instead of using the respective encoded values 3’b010 for hsize or 3’b001 for hburst from Table 1 and Table 2. As the transfer size is limited to a set of discrete values, a Vera constraint shall be set for this random integer. In this case, the burst size is limited to the set 8, 16, and 32 indicating the 8-bit, 16-bit, and 32-bit transfer sizes respectively. Even though wider transfer sizes are allowed in the full AHB specification (see Table 1), it is decided that our projects using this AHB transaction class will be limited to byte, halfword, and word transfers.

class ahb_trans extends rvm_data {

enum kinds_e = READ, WRITE, IDLE, READ_EXPECT;

enum hburst_e = SINGLE, INCR, WRAP4, INCR4,

WRAP8, INCR8, WRAP16, INCR16;

rand hburst_e hburst;

rand kinds_e kind;

rand integer hsize;

constraint ahb_hsize_valid {

hsize in {8,16,32};

// AHB also supports sizes: 64,128,256,512,1024

}

}

Even though the burst length is not encoded anywhere in the actual AHB signal definitions, it is an essential attribute to characterize a burst transaction. This data member is declared as hburst_len in the code example below. For each beat of the burst transfer, there are transfer attributes such as the write or read data and the number of busy cycles inserted. There are also a set of response attributes like the number of wait states, ok/error responses, etc. Since the burst length is itself random, special random dynamic arrays with their dynamic size set to the burst length are used to describe the multi-beat nature of a burst transaction.

class ahb_trans extends rvm_data {

// command attributes

enum hburst_e = SINGLE, INCR, WRAP4, INCR4,

WRAP8, INCR8, WRAP16, INCR16;

rand hburst_e hburst;

rand integer hburst_len;

rand bit[31:0] hdata[*] dynamic_size hburst_len;

rand integer n_busy[*] dynamic_size hburst_len;

// response attributes

integer num_retries[*] dynamic_size hburst_len;

integer num_waits[*] dynamic_size hburst_len;

integer early_term[*] dynamic_size hburst_len;

integer hresp[*] dynamic_size hburst_len;

}

Relationships between the random variables are specified as Vera constraints. There are some constraints that always have to hold true; however, there are others that can be tinkered with to introduce intended error conditions. It is important to plan for error injection ahead of time when designing a transaction object.

class ahb_trans extends rvm_data {

enum hburst_e = SINGLE, INCR, WRAP4, INCR4,

WRAP8, INCR8, WRAP16, INCR16;

rand hburst_e hburst;

rand integer hburst_len;

rand integer hsize;

rand bit[31:0] haddr;

constraint ahb_burst_valid {

(hburst == SINGLE) => hburst_len == 1;

(hburst == WRAP4) => hburst_len == 4;

(hburst == INCR4) => hburst_len == 4;

(hburst == WRAP8) => hburst_len == 8;

(hburst == INCR8) => hburst_len == 8;

(hburst == WRAP16) => hburst_len == 16;

(hburst == INCR16) => hburst_len == 16;

solve hburst before hburst_len;

}

constraint ahb_hsize_valid {

hsize in {8,16,32};

}

constraint size16_addr_align {

(hsize == 16) => (haddr[0] == 0);

}

constraint size32_addr_align {

(hsize == 32) => (haddr[1:0] == 2′b0);

}

constraint solver_order_valid {

solve hsize before haddr;

}

}

Conditional constraints are used to specify the relationship between the random variable hburst (SINGLE, INCR4, etc.) and the random variable hburst_len. They are grouped into one single constraint and a constraint solving order is also used to ensure that the distribution for the hburst is not badly skewed by the much greater range of values the hburst_len can take on. This constraint block shall never be modified or turned off and is appropriately named “_valid” to indicate such a special requirement to the user of this data class.

A deliberate use of separate constraint blocks to describe the address alignment as shown in the constraint blocks named size16_addr_align and size32_addr_align. They are not grouped into one because it is intended, by design, that any or both of these constraints can be turned off or modified as the test writer wishes to introduce address alignment protocol errors.

When an instance of the ahb_trans object is randomized, an error free transaction is created as the constraint solver finds a solution from the set of constraints. It is recommended to design a transaction data class so that the result of the randomization is always an error-free transaction unless specific error constraints are introduced otherwise.

The following example shows how an extension of the ahb_trans class can be used to modify the constraints to inject some address alignment errors:

class my_ahb_trans extends ahb_trans {

constraint size32_addr_align {

(hsize == 32) =>

haddr[1:0] dist {0 := 7, 1:3 := 1} ;

}

task pre_randomize() {

super.pre_randomize();

void = constraint_mode(OFF, “size16_addr_align”);

}

}

Before an instance of the derived my_ahb_trans class is randomized, the constraint block “size16_addr_align” is turned OFF which means the haddr[0] bit will have a 50% probability to be 1’b1 if the hsize is 16. This will introduce the address misalignment condition for transactions with a 16-bit transfer size. The constraint block “size32_addr_align” is overridden with a distribution which will give a 30% chance that haddr[1:0] is non-zero if the hsize is 32. This will also introduce the address misalignment condition, but for transactions with a 32-bit transfer size.

More complex constraints may be required to express the relationships that involve multiple variables. Sometimes, these constraints are more than just simple discrete values, ranges, or distributions. Some, like the one below, may involve arithmetic computations. For example, the constraint below makes sure that incrementing bursts must not cross a 1kByte address boundary.

class ahb_trans extends rvm_data {

enum hburst_e = SINGLE, INCR, WRAP4, INCR4,

WRAP8, INCR8, WRAP16, INCR16;

rand hburst_e hburst;

rand integer hsize;

rand bit[31:0] haddr;

rand integer hburst_len;

constraint ahb_haddr_valid {

(hburst == INCR) =>

(10′h3FF – haddr[9:0]) >= hburst_len * hsize/8 – 1;

(hsize==8 && hburst==INCR4) => (haddr[9:0] <= 10′h3FC);

(hsize==16 && hburst==INCR4) ||

(hsize==8 && hburst==INCR8) => (haddr[9:0] <= 10′h3F8);

(hsize==32 && hburst==INCR4) ||

(hsize==16 && hburst==INCR8) ||

(hsize==8 && hburst==INCR16) => (haddr[9:0] <= 10′h3F0);

(hsize==32 && hburst==INCR8) ||

(hsize==16 && hburst==INCR16) => (haddr[9:0] <= 10′h3E0);

(hsize==32 && hburst==INCR16) => (haddr[9:0] <= 10′h3C0);

}

}

The rvm_data base class provides a framework on what features the data object should provide for the testbench by declaring a set of virtual methods that need custom definitions. These methods are called by a number of testbench components like transactors and checkers and through the use of object oriented programming concepts like inheritance and polymorphism, the code development and its styles for these testbench components become simpler and more consistent. All of that is to help developing a testbench faster and make it less error prone.

class ahb_trans extends rvm_data {

// random variables

// constraints

// class methods

virtual task display(…);

virtual function bit is_valid(…);

virtual function rvm_data allocate();

virtual function rvm_data copy(…);

virtual function bit compare(…);

virtual function integer byte_size(…);

virtual function integer byte_pack(…);

virtual function integer byte_unpack(…);

} // Class: ahb_trans

Some of these virtual methods may not be applicable to all transactions. For example, the rvm_data::byte_pack() and rvm_data::byte_unpack() methods are great facilities for packet-based transactions like ATM cells, USB packets, or PCI express packets. They are less relevant to an AHB transaction. Other more standard methods like rvm_data::display(), rvm_data::copy(), and rvm_data::compare() are more applicable to AHB transactions and shall be implemented.

The detailed implementation of these ahb_trans class methods and other advanced features of the rvm_data base class such as the event notification are beyond the scope of this paper. However, relevant methodology and implementation guidelines in [4] that will help the design and creation of a good transaction object.

4.2 Channel and Generators

Writing a data channel to carry the ahb_trans transaction object and the atomic and scenario generators to generate ahb_trans transactions is extremely simple. The RVM base class library provides some macro interfaces to create the channel and generator objects. They are: rvm_channel(), rvm_atomic_gen(), and rvm_scenario_gen().

class ahb_trans extends rvm_data {

// random variables

// constraints

// class methods

} // Class: ahb_trans

rvm_channel(ahb_trans)

rvm_atomic_gen(ahb_trans, “ahb_trans”)

rvm_scenario_gen(ahb_trans, “ahb_trans”)

The names of the resulting classes are <name>_channel, <name>_atomic_gen, and <name>_scenario_gen. For the ahb_trans transaction data, the names of the channel and the generator classes are ahb_trans_channel, ahb_trans_atomic_gen, and ahb_trans_scenario_gen respectively.

4.3 Transactor Configuration

The AhbMaster VIP model has a number of configuration parameters and so will the AHB Master Transactor. Each testbench using this transactor may configure the parameters with different values as it requires. These configuration parameters are encapsulated in a configuration object to enable constrained randomization using Vera.

Table 3 provides some of these AhbMaster configuration parameters and the complete list of the configuration parameters are documented in [3]. A configuration class, ahb_master_xactor_cfg, is created and these configuration parameters are the data members of the class.

class ahb_master_xactor_cfg {

rand integer hdata_width;

rand bit is_little_endian;

rand bit bswap;

rand integer max_num_retries;

rand bit drive_z_before_grant;

rand bit retry_after_error;

rand bit ahb_lite;

rand bit ignore_error_response;

constraint config_valid {

hdata_width in {8,16,32,64,128,256,512,1024};

max_num_retries >= 0;

}

constraint max_num_retries_reasonable {

max_num_retries <= 15;

}

}

The data members that are used to configure the transactor are set random so that they can be constrained by the testbench that uses the transactor. As shown in Table 3, these parameters have a set of legal values which are enumerated in a constraint block named config_valid.

4.4 Message Service

Messages of different verbosities and severities are issued by AhbMaster. The same message reporting concept exists for the rvm_log base class. The RVM environment manager class, rvm_env, makes a decision about a test passing or failing based on internal warning and error count variables. These variable are automatically incremented when a warning or an error message is issued, such as when the rvm_warning() and the rvm_error() macros are called respectively.

To correctly determine the pass or fail status of a test, it is important for the RVM testbench to be aware of any error and warning messages that are issued from all the testbench components, including the models that may not use the same rvm_log message service (e.g. AhbMaster VIP, vendor memory models, etc.). One implementation is to provide a one-time message about the presence or absence of any warnings and errors issued by the AhbMaster instead of accurate error and warning counts from the model. This implementation is shown below.

#include “AhbMasterPort.vri”

#include “AhbMaster.vri”

bind AhbMasterPort ahbmaster_bind {…}

AhbMaster ahb_master_vmt;

ahb_master_vmt = new (”AHB_MASTER_VMT”, ahbmaster_bind);

{

integer wp_handle_warn;

integer rt_handle_warn;

integer wp_handle_err;

integer rt_handle_err;

apb_master_vmt.create_watchpoint (VMT_MESSAGE_TYPE,

VMT_MSG_WARNING,

wp_handle_warn);

ahb_master_vmt.create_watchpoint (VMT_MESSAGE_TYPE,

VMT_MSG_ERROR,

wp_handle_err);

fork

{

ahb_master_vmt.watch_for (wp_handle_warn,

rt_handle_warn);

rvm_warning (log, “warnings from AhbMaster”);

}

{

ahb_master_vmt.watch_for (wp_handle_err,

rt_handle_err);

rvm_error (log, “errors from AhbMaster”);

}

join none

}

Some watchpoints are created and watched for right after the AhbMaster VIP instance is constructed. When the AhbMaster warning or error is issued, the AhbMaster model’s watch_for() method unblocks and the code above calls the corresponding rvm_warning() and rvm_error() macro. The RVM environment will then determine whether a test should be considered passing or failing reliably.

4.5 Transactor

The first step to design a transactor is to determine what its constructor should look like. A flow chart similar to Figure 2 is useful for this purpose. In the case of the AHB master transactor, because it is in the stimulus generation stack as a bus driver, an input ahb_trans data channel interface is used to connect to the higher layer of the stack and a virtual port is used to connect to the DUT.

As described in Section 4.3, the AHB master transactor will also have a configuration object, along with the built-in objects like a message logger from the rvm_xactor base class. Since it is a wrapper transactor built on top of AhbMaster, this transactor will also have an instance of AhbMaster.

In Vera, the AHB Master Transactor class code looks like this:

#include “AhbMasterPort.vri” // include the virtual port

#include “AhbMaster.vri” // include the AhbMaster VIP

class ahb_master_xactor extends rvm_xactor {

rvm_log log;

ahb_master_xactor_cfg cfg;

AhbMaster ahb_master_vmt;

ahb_trans_channel in_chan;

AhbMasterPort sigs;

task new(string instance,

integer stream_id,

AhbMasterPort sigs,

ahb_master_xactor_cfg cfg,

ahb_trans_channel in_chan = null);

virtual task start_xactor();

protected virtual task main_t();

}

There code listing above shows 3 important methods of a transactor class: the constructor, the start method, and the main processing routine of the transactor. They will be described next.

4.5.1 Task: ahb_master_xactor::new()

In the constructor, the transactor has some generic identifier arguments (instance and stream_id) and it has some transactor specific interfaces: a lower-level virtual port connecting to the DUT, it has a transactor configuration that will be testbench specific, and a higher-level transaction channel for the ahb_trans data class for the flexibility to interface with higher level transactors.

task ahb_master_xactor::new(

string instance,

integer stream_id,

AhbMasterPort sigs,

ahb_master_xactor_cfg cfg,

ahb_trans_channel in_chan = null) {

super.new(”ahb_master_xactor”, instance, stream_id);

// Note (1) below

this.sigs = sigs;

this.cfg = cfg;

if (in_chan == null) {

in_chan = new(”ahb_master input channel”, instance);

}

this.in_chan = in_chan;

// Note (2) below

ahb_master_vmt = new(”ahb_master_vmt”, sigs);

// Note (3) below

ahb_master_vmt.set_config_param(

VMT_DEFAULT_STREAM_ID,

DW_VIP_AMBA_HDATA_WIDTH_PARAM,

cfg.hdata_width);

ahb_master_vmt.set_config_param(

VMT_DEFAULT_STREAM_ID,

DW_VIP_AMBA_LITTLE_ENDIAN_PARAM,

cfg.is_little_endian);

}

Some information are worth mentioning about the body of the ahb_master_xactor::new() method.

1. The port binding and an instance of the transactor configuration object are passed in.

2. The port binding is also passed to AhbMaster at its construction.

3. The configuration object contains properties (such as the data width and the endianness setting). AhbMaster allows these settings to be specified using its ahb_master_vmt.set_config_param() command.

4.5.2 Task: ahb_master_xactor::main_t()

This ahb_master_xactor::main_t() method is where the main functionality of the transactor is implemented. For an AHB transaction, the processing generally will follow the series of actions below

1. Retrieve the next AHB transaction from the channel. If the channel is empty, block the thread.

2. Conditionally issue some debug messages indicating the AHB transaction that is about to be started.

3. Execute the transaction. Clearly, this step is transaction and driver specific. If a driver is available, a set of task calls are made to the driver. If the driver is not available, this may include sampling and driving HDL signal through the virtual port to implement the signal-level protocol.

4. Conditionally issue some debug messages indicating the AHB transaction is completed.

5. Ready to retrieve the next AHB transaction from the channel, return to the beginning of the while loop.

task ahb_master_xactor::main_t() {

ahb_trans tr;

fork

super.main_t();

join none

while (1) {

// Note (1) above

cast_assign(tr, this.next_transaction_t(this.in_chan));

// Note (2) above

if (this.log.start_msg(log.DEBUG_TYP, log.TRACE_SEV)){

if (this.log.text(”Starting transaction…”)) {

void = this.log.text();

tr.display(”ahb_master_xactor started: “);

}

this.log.end_msg();

}

// Note (3) above

case (tr.kind) {

ahb_trans::WRITE: …

ahb_trans::READ: …

ahb_trans::IDLE: …

ahb_trans::READ_EXPECT: …

}

// Note (4) above

if (this.log.start_msg(log.DEBUG_TYP, log.TRACE_SEV)){

if (this.log.text(”Completed transaction…”)) {

void = this.log.text();

tr.display(”ahb_master_xactor completed:”);

}

this.log.end_msg();

}

// Note (5) above, ready for the next AHB transaction

}

}

The Step 3 above is where the detail of how an AHB transaction will be executed using a set of AhbMaster VIP commands. More specifically, a number of sub steps are involved here.

1. Recall in Section 0, the transaction attributes such as hburst specify the meanings, not the encoded values. This step prepares the encoded attribute values that will later be used in issuing AhbMaster VIP commands to execute the AHB transaction.

2. Different kinds of AHB transaction may use different set of AhbMaster VIP commands. In this paper, only an AHB write transaction is shown.

3. The following procedure outlines the execution of an AHB write transaction. Applicable AhbMaster VIP commands are listed in the parentheses. More information about these commands can be found in Table 4 and [3].

● Create a burst buffer to hold data and expected data from a burst transfer (new_burst_buffer)

● Set the following transfer attribute for each of the beats of a burst (set_burst_buffer_xfer_attr, set_burst_buffer_busy)

i. Request control

ii. Transfer size

iii. Burst type

iv. Protection control

v. Lock control

vi. Busy

vii. Data, if it is a write transaction

● Initiate a burst write cycle (write_burst)

● Wait for transfer responses from the slave (get_burst_result, get_burst_buffer_num_words, get_burst_buffer_response)

● Clean up and delete the burst buffer (delete_buffer)

In essence, the main functionality for the AHB Master Transactor is to convert a transaction object to a series of AhbMaster VIP model commands to execute the intended transaction in the form of signal activities adhering to the AHB specification. The user of this transactor will only need to understand the AHB transaction data, the ahb_trans class, but not the details of the protocol nor how to use the VIP model at its command level. The user of this transactor can be the actual test program, or a higher level transactor, or both.

4.5.3 Task: ahb_master_xactor::start_xactor()

A separate method is used to start the transactor. It must first call the start_xactor() of the parent class, as documented [4] and this will fork off the ahb_master_xactor::main_t() in Section 4.5.2. Since an instance of the AhbMaster VIP model is used, this VIP model also needs to be started by calling its ahb_master_vmt.start() command (see Table 4 or [3]).

task ahb_master_xactor::start_xactor(){

super.start_xactor();

ahb_master_vmt.start();

5. The “User”: Using the RVM-compliant AHB Master Transactor

The entire Section 4 is dedicated to discussing the thought processes behind the use of the RVM base classes for a well defined framework, the design of the transaction for an easier user-interface, and finally the development of the RVM compliant AHB master transactor using the AhbMaster VIP. With the exception of using a commercial VIP, the ideas presented should be applicable to any transaction and transactor designs using the RVM base classes.

This section, Section 5, will focus on how all the investment that goes into the development pays off quickly as it will show, through many examples, how this RVM-compliant AHB Master Transactor can be used in a typical testbench environment and how it makes test writing easy

5.1 Putting It All Together – Verification Environment Manager

The environment manager class, derived from the rvm_env base class, instantiates the following components, in this example:

● An atomic AHB Transaction generator

● An AHB master transactor, or the driver

● A configuration object to configure the AHB master transactor

● A channel object between the generator and the driver

class top_env extends rvm_env {

ahb_master_xactor_cfg cfg;

ahb_master_channel ch;

ahb_master_xactor drv;

ahb_trans_atomic_gen gen;

task new() {

super.new(…); // required by RVM methodology

cfg = new;

}

virtual task gen_cfg() {

super.gen_cfg(); // required by RVM methodology

if (!cfg.randomize()) {

// use built-in rvm_log to issue messages

rvm_fatal (log, “randomize failure”);

}

}

virtual task build() {

super.build(); // required by RVM methodology

ch = new (…);

// channel is used to connect the gen and drv

gen = new (…, ch);

drv = new (…, cfg, ch);

}

virtual task cfg_dut_t() {

super.cfg_dut_t(); // required by RVM methodology

// AhbMaster must be started before reset

drv.start_xactor();

// reset sequence begins

// programming of DUT configuration registers

}

virtual task start_t() {

super.start_t(); // required by RVM methodology

gen.start_xactor();

}

virtual task stop_t() {

super.stop_t(); // required by RVM methodology

drv.stop_xactor();

gen.stop_xactor();

}

}

This user-defined verification manager class, top_env, will be reused by the tests, implemented as Vera programs. Section 5.2 to Section 5.4 provide some examples on how the same verification environment is used in the tests while each of the tests has different stimulus requirements.

5.2 Transactor Configuration

The AHB master transactor configuration object inside the AHB Master Transactor can be configured to meet different project-specific requirements. For example, to use it with a DUT whose AHB interface adheres to the AHB-Lite specification and uses a 32-bit little-endian data, the testbench will need to set a constraint for the configuration object accordingly.

#include “ahb_master_xactor_cfg.vrh”

// class ahb_master_xactor_cfg {…}

#include “top_env.vrh”

// class top_env extends rvm_env {…}

class my_ahb_master_cfg extends ahb_master_xactor_cfg {

constraint for_this_tb_valid {

ahb_lite == 1;

is_little_endian == 1;

data_width == 32;

}

}

program test {

top_env env = new;

{

my_ahb_master_cfg my_cfg = new;

env.cfg = my_cfg;

}

env.run_t();

}

In the above example, the characteristics of the AHB interface are specified in a constraint block inside the user-defined configuration class, extending the ahb_master_xactor_cfg class (see Section 4.3).

After the verification environment constructs the configuration object of the testbench, but just before the configuration object gets randomized in the rvm_env::gen_cfg() phase, the test decides to allocate a new instance of the derived class type with the additional constraints and assign the new instance of my_ahb_master_cfg back to the environment’s configuration object. When the rvm_env::run_t() is called, to start sequencing the test through simulation flow from the immediate rvm_env::gen_cfg() phase where the configuration object gets randomized.

The details of how this DUT-specific testbench configuration requirement, specified in the form of Vera constraints in the derived configuration class, gets used in configuring the underlying AhbMaster are hidden from the test writer. The test writer only needs to focus on what the test needs to do and leave the lower level details (or how) to the verification environment manager and the components inside it.

5.3 Constrained Random Transactions

One of the great strengths of Vera is the constraint solver that enables a constrained random verification approach. The example below demonstrates how to modify constraints in the AHB transaction generator.

#include “ahb_trans.vrh”

// class ahb_trans extends rvm_data {…}

#include “top_env.vrh”

// class top_env extends rvm_env {…}

class my_ahb_trans extends ahb_trans {

constraint ahb_const {

kind in {WRITE, READ, IDLE};

hburst in {SINGLE, INCR, INCR4, INCR8, INCR16};

addr >= ‘h1200;

addr <= ‘h3000;

}

}

program test {

top_env env = new;

env.build();

{

my_ahb_trans my_tr = new;

env.gen.randomized_obj = my_tr;

}

env.run_t();

}

The test first constructs the verification environment manager. It then calls the rvm_env::build() method to advance the simulation flow up until the ‘build’ phase. By this time, the testbench components like the driver and the generator will have also been constructed (see Section 5.1).

At this time, the test introduces some extra code to first allocate a new instance of my_ahb_trans, a derived class from the ahb_trans base class (see Section 0). It then assigns the new instance of my_ahb_trans with additional Vera constraints in the constraint block, my_ahb_trans::ahb_const to the generator’s randomized_obj object instance that is repeated randomized to create the random content of the output transaction object stream.

Once again, the test simply specifies what the stimulus requirements:

● Only AHB writes, reads transactions and some IDLE transactions

● No wrapping burst transactions (WRAP4, WRAP8, and WRAP16)

● Starting address within the range 32‘h1200 and 32‘h3000

and the verification environment (and its components within) will take care of the rest automatically, including but not limited to:

● Generation of the AHB transactions per user-defined constraints

● Passing the AHB transaction from the generator to the AHB Master

● Executing the AHB transaction in the AHB Master by driving and sampling HDL signals on the AHB bus interface.

This example demonstrates again how the user will specify what he or she wants to do, the environment manager encapsulating the testbench components like the AhbMaster VIP will take care of the rest. The user is now leveraging the knowledge of the AHB protocol and the use model of the AhbMaster VIP commands effectively.

5.4 Directed Random Transactions

While the RVM methodology encourages the use of a constrained verification methodology, it does not stop the user from writing directed tests. An example is given below.

#include “ahb_trans.vrh”

#include “top_env.vrh”

class my_ahb_trans extends ahb_trans {

// a 16-bit INCR4 write with a start address of

// 0xABCD with 0-2 busy cycles inserted

constraint directed {

kind == WRITE;

hburst == INCR4;

hsize == 16;

addr == 16’hABCD;

foreach (busy, i) {

n_busy[i] in {0, 1, 2};

}

n_busy[3] == 0;

}

}

program test {

top_env env = new;

env.gen_cfg();

{

env.cfg.ahb_lite = 0;

env.cfg.is_little_endian = 1;

env.cfg.data_width = 32;

env.cfg.retry_after_error = 0;

}

env.start_t();

{

my_ahb_trans my_tr = new;

void = my_tr.randomize();

env.ch.put_t(my_tr);

}

env.run_t();

}

In this example, the test adds two levels of directness to the stimulus – the testbench configuration and the AHB transaction.

The configuration is randomized in the environment’s ‘gen_cfg’ phase. This example then overwrites some properties of the configuration object (e.g. the ahb_lite, the data_width properties) to demonstrate one way that the configuration can be directly specified.

A user derived transaction class adds a set of Vera constraints to specify the kind of AHB burst transaction with the transfer size, address, as well as the number of busy cycles to insert between data beats. An instance of this derived AHB transaction object is allocated and inserted directly into the AHB transaction channel. The AHB Master Transactor in the verification environment manager will then automatically take care of retrieving the transaction from the channel, executing the transaction and driving this AHB transaction to the DUT under the hood.

6. Conclusions and Recommendations

The use of Verification IPs is ubiquitous in today’s testbenches for complex DUTs. With the adoption of advanced verification methodologies like RVM will further provide the productivity gain in testbench development. Using the RVM base classes provide a consistent look and feel for the modeling of the data transactions, the transactors, and also how the tests reuse the same verification environment manager while having the flexibility to introduce additional requirements to the stimulus generation.

This paper recommends the concept of developers and users. The developers focus on the domain-specific expertise such as the standard interfaces and the use of the commercial or custom VIPs. For the VIPs that do not currently have the RVM support available, they can create the RVM interfaces for these VIPs. The users of these RVM compliant VIPs will be able to quickly reuse these VIPs in any verification projects that adopt the RVM methodology. Furthermore, the users can focus on what they want to accomplish for their tests in their application specific testbench instead of the details of the specification and how to use these VIPs. With this collaboration, the users can effectively leverage the expertise and experience of these developers and focus on the high values they will provide in other areas of the testbench development, such as the response checker, the coverage model, and the tests.

At the time of writing, the RVM support for Synopsys’ DesignWare AHB Master VIP model is not yet generally available; therefore, one was developed for the projects here at Shenyang Institute of Automation. The paper walks through the thought processes during the design of the transaction and the transactor (“as a developer”) and then demonstrates how this RVM compliant transactor can be integrated easily in the creation of a verification environment and how the environment can be effectively leveraged in test development (“as a user”).

7. Biography

Prof. Zhi Jia Yang is a professor at the Shenyang Institute of Automation of the Chinese Academy of Sciences. He has nearly 10 years of experience as a designer and a project manager on embedded systems. He has worked in the areas of computer network systems and fieldbus control systems. He holds a Bachelor of Engineering degree from Zhejiang University, China.

Miss Yu Feng Zhang is a member of the verification team for an ARM-based SoC project for the industrial control applications at Shenyang Institute of Automation (SIA) of the Chinese Academy of Sciences (CAS). She is in the last year of her graduate studies at the institute and will earn a Master’s degree in Engineering from CAS in July 2005.

Mr. Su Gang Li is a member of the verification team for an ARM-based SoC project for the industrial control applications at Shenyang Institute of Automation (SIA) of the Chinese Academy of Sciences (CAS). He is in the last year of his graduate studies at the institute and will earn a Master’s degree in Engineering from CAS in July 2005.

Mr. Jason C. Chen is a Sr. Design Consultant for Synopsys Professional Services. He has 4 years of ASIC design experience using VHDL and Verilog and 4 years of functional verification experience using Vera, assertions, and advanced methodologies. He holds a Bachelor of Engineering degree from McGill University, Canada.

8. Acknowledgement

This paper would not have been possible without the support of Shenyang Institute of Automation, Chinese Academy of Sciences, and members of Synopsys who gave their approval and review of the paper. The authors also want to thank the assigned reviewer, Weihua Han, for his feedback on the organization and the content of the paper.

9. References

[1] AMBA™ Specification Rev 2.0, ARM IHI 0011A, ARM Limited.
[2] AHB-Lite Overview ARM DVI 0044A, ARM Limited.
[3] DesignWare AHB Verification IP Databook, Release 2.45b, Synopsys Inc.
[4] Reference Verification Methodology User Guide, Synopsys Inc.