Transaction Recording, Modeling and Extensions for SystemVerilog

Rich Edelman, Mark Glasser, Chris Cotterel, Bill Cox
Mentor Graphics
San Jose, CA, US

Abstract

Adoption of transaction level modeling and the necessary tools for debugging and analysis has been slower than would be expected from growing SOC design sizes and complexities. This paper discusses ways to improve the adoption rate by improving the usability and simplifying the modeling concepts. Using SystemVerilog we demonstrate a simplified PLI interface for recording transactions and we extend previous language standard changes to improve automation.

Introduction

Currently, SystemC [5] and the SCV are the only transaction level modeling standards for using and recording transactions. The widespread use of transactions has been hindered because SystemC is not as widely used as Verilog and VHDL, and it is hindered because it is complex to use.

A previous proposal [6] for a Verilog [3] based PLI standard for recording transactions has value, but it suffers from being based on Verilog (rather than the newer, more powerful SystemVerilog [4]) and it suffers from being an extension of the SCV API – thus being more complex than necessary.

We believe that a simpler SystemVerilog API, along with proposals to extend the automation described in [6] for use with SystemVerilog will speed the use and availability of transaction level designs and tools.

This paper describes such extensions for a SystemVerilog PLI based transaction recording API.

With SCV in SystemC, in order to use transactions, the transaction must be described using scv_extensions for all but the simplest transaction. Although available for years, this system is complex enough that users have either opted to write simple transaction descriptions, or to simply not record transactions. The SCV is quite powerful, and offers many features and functionalities that make verification and debug more productive, but the overhead to reach these productivity levels is great.

If the transaction use model remains complex, it will only be delivered by specialists, and will not be a common end-user design and verification activity.

Specialists such as experienced IP developers will deliver transaction functionality in the form of embedded IP. The IP will contain transaction functionality programmed by the specialist, and used by the end-user. A goal of this simplified SystemVerilog PLI transaction recording API is to empower both these specialists as well as end-users.

Modeling

Once the hurdle of describing the transaction has been addressed, there still exists a large impediment to adoption – how to model transactions? We must answer the question – “How will my design and verification behavior be represented by transactions”?

 

Figure 1 - Two alternative modeling levels

In a large SOC there are many wires, and many transactions. Picking the abstraction level correctly will help optimize database sizes and simulation speed. For example, modeling an IMAGE_READ() as a sequence of primitive READ() transactions will generate more data, and potentially limit simulation speed as opposed to modeling a single IMAGE_READ() as a transaction. It is important to model enough detail for the job, but not so much detail as to obscure the results.

Once the amount of transaction detail is selected, we must decide at which topographic points to model – bus, FSM, network connection, etc. Furthermore, we must map the SOC transactions into the transaction recording modeling concepts.

Each bus interface could be modeled as a stream – this allows a stream to match the mental model of a bus. Alternatively, a stream could be used to model all the traffic into or out of a memory device. In this way we can easily identify the memory transactions – they are all collected on the same stream.  With this new, proposed transaction recording API the elements on a stream don’t have to be “managed” by the user of the API. This approach allows reuse and flexibility.

The SCV specification and the proposed Verilog standard both use streams, generators, transactions, special attributes, begin and end attributes, relations and the concept of recording transactions at times in the past.

Our proposal simplifies the PLI-based API, while retaining the power – it contains streams, transactions, attributes, relations and the concept of recording in the past. (See API listed below).

SystemVerilog Transaction Recording API

The proposed API provides a way for transaction recording to be added to any SystemVerilog design with minimal effort. These manual additions to a design or verification environment can be automated by proposals discussed in the following sections.

The API is presented below.

stream = $create_transaction_stream(
string streamName,
string streamKind)


$create_transaction_stream takes two string arguments – a name for the stream and a kind.

transaction = $begin_transaction(
handle stream,
string transactionName
[,time beginTime,
  handle parentTransaction])


$begin_transaction has two required arguments, a stream handle and a transaction name. It also has two optional arguments 1) the time in the past that this transaction started and 2) the parent transaction of this phase of the parent. Specifying a parent means that this transaction is a phase of the parent.

$end_transaction(
handle transaction
[, time endTime])

$end_transaction has a single required argument – the handle of the transaction that is to be ended. It also has a single optional argument, the time in the past that this transaction ended. After a transaction has been ended, the transaction handle can still be used to add attributes and create relations.

$free_transaction(
handle transaction)

$free_transaction has a single argument – the handle of the transaction to be deleted. Once a transaction is deleted the handle becomes invalid. It cannot be used in any other recording interfaces.

$add_attribute(
handle transaction,
object attributeValue
[, string attributeName])


$add_attribute has two required arguments – a transaction handle on which the attribute is to be created and the attribute that is to be recorded. There is one optional argument of type string named attributeName. This attributeName specifies an alias name for the attribute. If not specified, the name used for the attribute is the actual name of the SystemVerilog object.

$add_relation(
handle sourceTransaction,
handle targetTransaction,
string relationshipName)

$add_relation has three arguments – the first two are the two transaction handles which are related. The third argument is the string name of the relation.

Streams

A stream is the “location” of the transactions. Transactions occur “on” a stream. Streams are containers or holders for transactions – place for the transactions to be found, displayed and processed. A stream can be thought of as a collection of non-overlapping transactions collected over time. The non-overlapping transaction requirement helps streams be “drawable” – since no transaction on a stream will overlap another.

There are two special cases of overlap; the first case is a phase overlap which is a fully contained child of the original transaction and the second case is a true overlapping transaction on the stream [8]. In the first case, this is a special case, and not really an overlap, but rather a sub-transaction.

 

Figure 2 - Three transactions – two as phases

The phase overlap can be drawn in a special way as a part of the parent transaction [Figure 2]. In the second case there was a real overlap of two transactions, and the second transaction must occupy (and be drawn) on a separate stream [Figure 3].

 

Figure 3 - Stream and sub-stream modeling

This separate stream is an artifact of having two overlapping transactions on the same stream. Our proposal allows these artifacts at any time – the user or designer cannot generally predict when these artifacts are going to happen and doesn’t need to. Our proposal will automatically manage overlapping transactions on the same stream by automatically creating a sub-stream. The sub-stream is considered part of the parent stream, but for drawing and management purposes it is a separate stream (a sub-stream).

Modeling overlap is quite difficult in general. For example, one protocol (APB) may have no overlapping transactions, but when the bus is switched for a new protocol (OCP) with overlapping transactions, suddenly the transaction modeling infrastructure may need to be changed to support this new protocol. A more reusable transaction modeling approach is to simply model transactions as communication, and allow overlapping or non-overlapping to naturally occur. As requirements on design and communication change – overlap characteristics will change.

How transactions interact with each other is hard to predict, and hard to model. Our proposed solution allows the designer and verification engineer to avoid modeling complex transaction interaction such as overlap. Overlap is naturally handled by the recording environment.

Transactions

Transaction modeling is simple – a transaction lives on a stream (or sub-stream) and has a name or type, a begin time, an end time, a collection of (name, value) attributes and a collection of relations. Attributes and relations are optional.

The lifetime of a transaction is from the time $begin_transaction() is called to create the transaction to the time $free_transaction() is called to destroy the transaction. Calling $end_transaction() does not end the lifetime of a transaction. Calling $end_transaction() does end the transaction, but does not make the transaction handle invalid. A transaction that has ended may still have relations annotated to it or attributes added to it. It is common practice to “relate” ended transactions to each other much later in the simulation, and similarly to annotate “calculated” data to an ended transaction. But once the ended transaction is no longer needed, it should be destroyed.

Attributes

Attributes can be recorded or “attached” to a transaction at any time. As a transaction progresses through a system attributes are naturally added as new annotations are discovered. Each attribute is a simple (name, value) pair, and is viewed as a “field” within the transaction.

No special treatment is given for begin or end attributes – except that they may be recorded by the design either earlier or later in the transaction.

Relations

Relations are simply tags between two transactions. An arc can be drawn between any two transactions and labeled. The label is the relation name, and the arc describes a “from/to” property that the relation may exhibit. For example a relation of PARENT points from the child transaction to the parent transaction. The PREDECESSOR relation points from the second transaction to the previous transaction. Relations can be tricky, since they may be created at any point after the transactions in question exist. Most relations are created at the time of creation of the latest transaction in the relationship, but this is not a requirement.

Transaction Lifetime

This brings us to managing transaction handles over the life of the simulation. As mentioned, most transaction operations occur at relatively the same time as the transaction begin and end times. There are situations where transaction handles are kept for longer times, and processed later. This can cause many transactions to be “alive” during simulation at once – which can impact performance.

In order to limit the lifetime of transaction handles it is good practice to manage transactions from one or two levels above and no further. For example, an IMAGE_READ() transaction may have child LINE_READ() transactions, which may have child PIXEL_READ() transactions. It is good practice to have all PIXEL_READ() and LINE_READ() transactions handled by the time IMAGE_READ() is handled and “ended”.

Automatic recording

Up to this point we’ve described manually inserting PLI calls into designs and verification environments to create and record transactions. Although an improved interface with straightforward application, this method suffers from being tedious and error prone; as can be seen in the simplest tasks implementations. Automation of transaction recording instrumentation is a required feature in any transaction recording system.

The automation proposed in [6] is a good start, but needs to be extended for SystemVerilog constructs.

Any function or task can be thought of as a transaction. Individual tasks and functions or groups of tasks and functions can be identified as transactions by using a Verilog attribute.

Tasks and functions are often collected in modules, interfaces or classes. Automatic transaction recording can be enabled for collections or individual tasks or functions.

Interface

An interface in SystemVerilog is a collection of wires, and optional tasks and functions to manage those wires. Each interface task and function can be viewed as a transaction, and can be automatically annotated with a Verilog attribute. Once annotated, each call to the task or function becomes a transaction.

interface;
reg clk;
bit rw;
bit [31:0] data_bus;
bit [31:0] addr_bus;
handle stream;

task read (input bit[31:0] addr,
output bit[31:0] data);
handle tr;
tr = $create_transaction(stream, “READ”);
$add_attribute(tr, addr);
addr_bus = addr;
rw = 1;
@(posedge clk);
data = data_bus;
$add_attribute(tr, data);
$end_transaction(tr);
endtask
endinterface


The code above has been instrumented manually to record transactions. The code below is the same code, this time with the equivalent PLI calls generated automatically based on the transaction attribute marking the SystemVerilog interface.

(* transaction *) interface;
  reg clk;
  bit rw;
  bit [31:0] data_bus;
  bit [31:0] addr_bus;

task read (input bit[31:0]addr,
          output bit[31:0] data);
    addr_bus = addr;
    rw = 1;
    @(posedge clk);
    data = data_bus;
  endtask
endinterface


Each interface could be assigned a unique name in the stream-hierarchy; automatically named as part of the existing context tree. All transactions on this interface will be grouped and displayed on this auto-generated stream.

Classes

Below is an example SystemVerilog class used as a transaction from the AVM [1]. A SystemVerilog class is defined in the LRM, but has similar characteristics to C++ and Java classes. This class is annotated manually with transaction recording code, but could have been automatically instrumented with the same attribute as used on interfaces. For this class, we choose to create a transaction when the transaction class is constructed. Later when a call to the READ task occurs we’ll add attributes.

enum BT_TYPE { READ, WRITE, IDLE};
class bus_transation extends avm_transaction;
 
bit [31:0] data_bus;
bit [31:0] addr_bus;
handle stream;
handle tr;

function new(enum BT_TYPE t);
tr = $create_transaction(stream, t.name);
endfunction

task read (input bit[31:0]addr,
output bit[31:0] data);
$add_attribute(tr, addr);
data = mem[addr]; // Behavioral memory
$add_attribute(tr, data);
$end_transaction(tr);
endtask
endclass


The code above has been instrumented manually to record transactions in a class. The code below is the same code, this time with the equivalent PLI calls generated automatically based on the transaction attribute marking the SystemVerilog class construct.

enum BT_TYPE { READ, WRITE, IDLE};
(* transaction *) class bus_transation
extends avm_transaction;
 
bit [31:0] data_bus;
bit [31:0] addr_bus;

function new(enum BT_TYPE t);
endfunction

task read (input bit[31:0]addr,
output bit[31:0] data);
data = mem[addr]; // Behavioral memory
endtask
endclass


In addition to interfaces and classes, other SystemVerilog constructs should be considered for automatic recording.

Conclusions

The transaction recording API presented, based on SystemVerilog PLI, is easy to use, and easy to understand, promoting adoption and encouraging use. Flexibility in modeling transactions is also achieved by this simplified proposal.

A necessary second step to further promote adoption is automation of transaction recording. This is an area for further discussion and investigation. Taking advantage of natural SystemVerilog language constructs such as tasks, functions, interface and class can help promote clean and consistent interfaces and modeling.

References

[1]    Mark Glasser, Adam Rose, Tom Fitzpatrick, Dave Rich, Harry Foster, “The Verification Cookbook”,

2006, Mentor Graphics Corporation, http://www.mentor.com/go/cookbook.

[2]    Frank Ghenassia (editor), “Transaction-level Modeling with SystemC: TLM Concepts and Applications for Embedded Systems”, Springer, 2005.

[3]    Verilog LRM. IEEE 1364-2005.

[4]    SystemVerilog LRM. IEEE 1800-2005.

[5]    OSCI TLM Standard, www.systemc.org

[6]    Draft Standard for Verilog Transaction Recording, www.boyd.com/1364_btf/report/full_pr/attach/435_IEEE_TR_Proposal_04.pdf

[7]    Rich Edelman, IPSOC 2005, “A SystemVerilog DPI Framework for reusable transaction level testing, debug and analysis of SOC designs”.

[8]    Rich Edelman, Mark Glasser, Bill Cox, IPSOC2004, “Debugging SOC Designs with Transactions”.


Appendix 1

Re-working the simple example in the front of [6] to use the proposed PLI recording API produces the following simulation results and source code.

 

Figure 4 - Two simple transactions on a stream

module top;
wire [7:0] addr, data;
wire do_write, do_read;
cpu_testbench cpu_tb_inst(
addr, data, do_write, do_read);
endmodule

module cpu_testbench(addr, data,
do_write, do_read);
output [7:0] addr, data;
output do_write, do_read;
reg [7:0] addr, data;
reg do_write, do_read;
integer cpu0_stream;
integer transaction_handle;

initial begin
cpu0_stream =
$create_transaction_stream(
"cpu0_stream", "kind");

// Do a write to the DUV:
addr = 10; data = 15; do_write = 1;
transaction_handle =
$begin_transaction(cpu0_stream, "write");
$add_attribute(transaction_handle, addr);
$add_attribute(transaction_handle, data);
// wait until the write is complete
do_write = 0;
#10;
$end_transaction(transaction_handle);

// Do a read from the DUV:
addr = 11; do_read = 1;
transaction_handle =
$begin_transaction(cpu0_stream, "read");
$add_attribute(transaction_handle, addr);
// wait until the read is complete
do_read = 0;
#10;
$add_attribute(transaction_handle, data);
$end_transaction(transaction_handle);
end
endmodule

 

Appendix 2

Using hierarchically arranged tasks creates hierarchical transactions. Relationships and phased transactions are created.

 
Figure 5 - Hierarchy of related transactions


task automatic bus_write(
input int addr, input int data,
input HANDLE p);
HANDLE tr;
tr = $begin_transaction(s, "write",
$time, p);
$add_attribute(tr, addr);
$add_attribute(tr, data);
#10 mem[addr] = data;
$end_transaction(tr);
#1;
endtask
 
task automatic burst_write(
input int addr, input int data[4],
HANDLE p);
HANDLE tr;
tr = $begin_transaction(s, "burst_write");
$add_relation(tr, p, "parent");
$add_relation(p, tr, "child");
$add_attribute(tr, addr);
$add_attribute(tr, data);
for (int i = 0; i < 4; i++)
bus_write(addr+i, data[i], tr);
$end_transaction(tr);
#5;
endtask

task automatic image_write(
input int addr, input int data[2][4],
HANDLE p);
HANDLE tr;
tr = $begin_transaction(s, "image_write");
$add_relation(p, tr, "parent");
$add_relation(tr, p, "child");
$add_attribute(tr, addr);
$add_attribute(tr, data);
for (int i = 0; i < 2; i++)
burst_write(addr+i, data[i], tr);
$end_transaction(tr);
#20;
endtask

task automatic burst_image_write(
input int addr, input int data[10][2][4]);
HANDLE tr;
tr = $begin_transaction(s,
"burst_image_write");
$add_attribute(tr, addr);
$add_attribute(tr, data);
for (int i = 0; i < 10; i++)
image_write(addr+i, data[i], tr);
$end_transaction(tr);
#20;
endtask

// remaining code not shown...


×
Semiconductor IP