Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

System Verification with SCV


George works on ESL technologies and SystemC at Cadence Design Systems Inc. He can be reached at [email protected].


Testing and debugging embedded software systems and hardware designs provides challenges similar to software test and debug. Logic is logic, whether it is implemented in software or silicon. However, although this is certainly not universally true, the stakes can be higher for hardware systems because of several factors. Market forces are driving much of the electronics industry into the profitable consumer arena. Consumer products need to be easy to use and cheap, and time-to-market is as critical today as ever. Finally, chip designs need to be innovative and differentiated from competitors. These goals, which are often at odds with each other, are running into another technological force—the ever-shrinking transistor. Despite rumors of its imminent demise, Moore's Law continues with frenzy and ever smaller geometries: Gate dimensions have marched down the "submicron" path from 0.65 microns, to 0.5 microns, to 0.25 microns, to 0.18 microns, and now 0.13 microns. The process continues. With each step, the factories that print chips have to retool, and that cost is passed along to chip designers. To provide reasonable cost and function, new chip designs are commonly entire "Systems-on-a-Chip," with previously uncoupled subsystems residing on a single chip that might total over 15 million gates. The cost of failure is high. If a chip comes back faulty from the fabricating plant, the average cost for a respin is approaching $1 million. Recent market research indicates more than half of all new chips require at least one respin, and an even higher percentage of final products have major functional flaws.

The Electronic Design Automation (EDA) industry provides tools that hardware and embedded systems designers use to produce new designs. EDA tools have evolved along with customer demands. On the design side, the last 15 years have been marked by the dominance of hardware-design languages—principally Verilog and VHDL, which support modeling at the RTL-level of abstraction. The Register Transfer Level (RTL-level) tracks system changes at the clock cycle. This is turning out to be too computationally intense for the design and verification of Systems-on-a-Chip that contain over 10 million gates. A new level of abstraction, the Transaction Level (or more generally, the Electronic System Level, ESL), is emerging where simulation is viewed as a behavioral flow of data through a system. Granularity shifts from clock bursts to flow events such as data bursts across a bus. The single transfer of an MPEG from memory to a speaker can be modeled in one discrete event with an associated cost, rather than being broken down into the individual clock bursts and bit transfers that make up the physical event. This provides faster simulation and a quicker turn-around for design decisions.

This new ESL is not only important at the design level but is increasingly more critical at the verification stage that occurs before a chip design is masked into silicon. Historically, it was possible to exhaustively test a circuit model by trying all possible test vectors against a known matrix of results. This black-box testing has parallels in software testing and the same issues arise there—time. With large designs, an exhaustive approach is impossible. Techniques familiar to software testers are being modified and translated to the hardware and embedded software arenas with specific emphasis being placed on the particular problems of massive design spaces. Several ESL technologies have arisen to implement these techniques. I address one of them in this article—SystemC Verification (SCV).

The SCV library is an open-source class library that works in conjunction with the open-source library SystemC. Both libraries are built on Standard C++. SystemC and SCV are governed by the Open SystemC Initiative (http://www.systemc.org/). SCV is being widely used to verify not only SystemC designs but designs written in Verilog and VHDL, or a combination of all three. Note the similarity to software testing. Chip designs are written in programming languages that are expressive of hardware constructs, the design is verified at a certain level of abstraction, and then other programs translate (similar to "compile") or synthesize the higher level programmatic representations of System function down to a gate-level design that can be fabricated into a chip.

Verification at the ESL

SystemC was developed to standardize an ad hoc collection of C-based ESL technologies so that EDA vendors and IP designers could exchange models and standardize interfaces for better interoperability between products and product flows. This is important for system design, but especially important for system verification, where the goal is to build a reusable infrastructure of test stimuli and models. For a more detailed discussion of SystemC, see my article "SystemC: Hardware Constructs in C++" (C/C++ Users Journal, January 2005).

As systems become increasingly more complex, it is no longer possible to exhaustively test designs from a black box perspective. Modern verification techniques are designed to be integrated with the development process and implemented by experts who know most about what should and should not be happening at any stage of the design. SystemC itself lacks some of these methods. However, C++ in general is a convenient language for generating test benches, even if the IP models are written in Verilog or VHDL. With SystemC, ESL verification can be done across the entire lifecycle of a project, and verification blocks can be reused between projects and for both block verification and examination of design tradeoffs. These facts led to the development of a separate set of libraries on top of SystemC for Verification. Figure 1 illustrates a transaction-based verification scheme based on SCV. Here, Tests communicate via a Transactor with the model of the system design. Tasks are Transaction-level events; that is, above the RTL-level. If the design is at the RTL-level because, for example, it is written in Verilog or VHDL, the signals between the Transactor and the model are RTL-level signals. A Transactor can be thought of as an adaptor that translates communications between the various levels of abstraction. For example, a Transactor can translate a high-level operation, such as a function call or bus transfer into its component signals or data bursts that are clock accurate.

Randomization

Because it is not possible to test all combinations of possible inputs (or stimuli) of a design, a subset of stimuli is chosen for system verification. One approach to this is to construct tests by hand. This is helpful for bug tracking (a unit test is introduced to ensure that subsequent changes don't "unfix" the improvement), but for thorough coverage, hand-constructed tests can be both limiting and biased. Randomization lets test vectors be picked randomly with values ascribed possibly based on certain bounding criteria.

Unconstrained randomization is unbounded: Data values have an equal probability of occurring anywhere in the legal space of the data type. This is similar to using C's rand() function to generate a random integer value between 0 and RAND_MAX.

Weighted randomization weights the probability so that the distribution of data values is not uniform. This is tunable. An example would be setting a 75 percent probability that an integer input will take on a value between 0 and 50 and a 25 percent probability that the input will be between 51 and 100.

A more sophisticated randomization is constraint-based randomization where an input is constrained to a range of its legal values by a set of rules specified by constraint expressions. The constraint expressions can be simple ranges or a complex expression that may include other variables under constraint and subexpressions, and so on.

The first class of importance in SCV randomization is the scv_smart_ptr<T> class, a container that provides multithreaded memory management and access and automatic garbage collection for both built-in C++ types and SystemC types, such as sc_int, sc_uint, and the like. Smart pointers are essential in SCV because SystemC is a multithreaded library where memory might be accessed by more than one thread or even more than one process. The get_instance() method of scv_smart_ ptr<T> provides direct access to the underlying data object (this should be used with caution). Because it is problematic to apply the core algorithms of randomization to data structures that can dynamically change in size, SCV randomization is limited to data types with a fixed size (this excludes lists, trees, and so on). However, users can create smart pointers for all extended types, including user-defined structs. Finally SCV provides many general-purpose methods for manipulating smart pointers such as copying a smart pointer (include "deep" copy).

Listing One is an example of using weighted randomization in SCV. A system is simulated where operations exist to RECEIVE, STORE, DECODE, MANIPULATE, ENCODE, and SEND a jpeg object. The operations are named in an enum that is used as a smart pointer. Weighted randomization is implemented in SCV with the class scv_bag, which creates distributions, and the smart pointer method set_mode(), which assigns a distribution to an input. I assign weights to each operation with the add() method of scv_bag (for convenience, a total of 100 is used, so it is obvious that, for example, there is a 40 percent probability in any event cycle of variable jpg taking on the value of MANIPULATE). For clarity, the example omits the extensions definition that would be required to do randomization on the user's enum. Weighted randomization is most useful when an input has a known set of legal values and it is desired to favor certain ranges differently than others.

The richness of randomization in SCV is achieved through the constraint classes, which are containers for randomization objects that have complex constraints. Constructing a class that contains both a data type and the constraints on that type separates the two and allows the use of C++ class inheritance. While a detailed treatment of constraint-based randomization in SCV and the mechanisms of an efficient constraint solver is beyond the scope of this article, I can share one small example.

Constraint classes derive from scv_constraint_base and must contain at least one scv_smart_ptr (each such smart pointer must be instantiated on a simple object, no nested smart pointers or hierarchy is allowed). Listing Two shows the creation and use of a constraint class. Here, a simple bounded integer is represented by three constraints. Note that the use of the scv_constraint_base::next() generates instances of the constrained object. This call randomizes all smart pointers in the constraint class.

Transaction Monitoring and Recording

One of the basic debugging and analysis functions of verification is to record and report the results of operations within the Transactor. This is done via transaction monitoring and recording with the SCV Transaction API. The output in SCV is text based. In SCV, you control what happens during transaction recording by registering callbacks. For example, to do text recording, users call scv_tr_text_init(), which registers the appropriate callbacks for text recording. Similar strategies can be used to change how transactions are recorded. For instance, to record to an SST2 database (a vendor-specific signal database provided by Cadence Design Systems) you call cve_tr_sdi_init() to register those callbacks. Text recording can be slow, so this is a powerful way to extend transaction recording. Monitoring can be done dynamically or the output can be dumped out for postprocessing.

Transactions are events that have a beginning and ending time and an associated data set. Important classes in SCV transaction recording are:

  • scv_tr_generator, a class for generating transactions of a specific type.
  • scv_tr_stream, a grouping of related or overlapping transactions.
  • scv_tr_db, a transaction database that collects a group of transaction streams.
  • scv_tr_handle, a handle to a transaction.

Data Introspection

An important facilitator for randomization, constrained randomization, and transaction recording is the ability to perform certain operations on complex C++ and SystemC data types. The C function rand() generates a random integer. SCV allows similar randomization of higher order C++, SystemC, and SCV classes and types with a form of data introspection. Introspection lets a program gain knowledge about the properties of an initially unknown data object. Thus, in the example of rand(), this can be extended to complex types by using introspection to determine the name and data types of the data members of an object, and applying type-appropriate customizations of rand() on those composite members (often wrapped in an scv_smart_ptr). In SCV, data introspection is implemented with template specialization so code can work with a data object without explicit type information at compile time. Without this powerful SCV feature, users would need to implement this with custom code for every class in the verification design.

The data introspection facility provides a standard abstract interface, scv_extensions_if, from which a data object can be analyzed and manipulated. The scv_extensions template extends data objects so that they support the abstract interface via partial template specialization. SystemC, C++, and C built-in types have corresponding data introspection types, including:

  • Class scv_extensions<bool> for bool.
  • Class scv_extensions<char> for char.
  • Class scv_extensions<sc_string> for sc_string.
  • Class scv_extensions<T*> for pointer.

scv_extensions has a host of member functions for introspecting the data type. Listing Three is an example where a user-defined type is queried for the number of fields. This is just a small primer on data introspection. Besides static analysis, such as in the aforementioned example, a rich implementation of dynamic analysis is also available in SCV.

Conclusion

SCV is widely gaining support from the ESL community and has already been used to verify chips that have made it to tape out. It is not without competitors in the ESL verification space, namely SystemVerilog and the "e" language. However, as SystemC continues to gain prominence, SCV and the libraries that simplify its use should gain traction and help spur the adoption of Electronic System Level, "preimplementation" Verification in hardware and embedded system design.

DDJ



Listing One

enum PROCESS_JPEG_EVENTS {RECEIVE, STORE, DECODE, MANIPULATE, ENCODE, SEND};

int jpeg_stream()
{
   scv_smart_ptr<PROCESS_JPEG_EVENTS> jpg;
   scv_bag<PROCESS_JPEG_EVENTS>       jpg_dist;
   jpg_dist.add(RECEIVE, 10);
   jpg_dist.add(STORE, 10);
   jpg_dist.add(DECODE, 20);
   jpg_dist.add(MANIPULATE, 40);
   jpg_dist.add(ENCODE, 10);
   jpg_dist.add(SEND, 10);
       
   jpg->set_mode(jpg_dist);

   while (1)
   {
       jpg->next();
       switch (jpg->read())
       {
           case RECEIVE:  jpg_receive(); break;
           case STORE: jpg_store(); break;
           case DECODE: jpg_decode(); break;
           case MANIPULATE: jpg_manipulate(); break;
           case ENCODE: jpg_encode(); break;
           case SEND: jpg_send(); break;
           default: return 1;
       }
   }
   return 1; // never return
}
Back to article


Listing Two
// An SCV Constraint Base Class with 3 constraints

class boundary_constraint_class : public scv_constraint_base 
{
public:
    scv_smart_ptr<sc_uint> burst;
    scv_smart_ptr<uint> lower;
    scv_smart_ptr<uint> upper;

    SCV_CONSTRAINT_CTOR(boundary_constraint_class) 
    {
        SCV_CONSTRAINT (lower() > 100);
        SCV_CONSTRAINT (upper() < 500);
        SCV_CONSTRAINT (burst() >= lower() 
                        && burst() <= upper() );
    }
};
// using the boundary class
int use_boundary() 
{
    boundary_constraint_class bc ("boundary_constraint_instance"); 
    // The argument is a name string required by SystemC
    for(int i=0; i < DESIRED_NUMBER_OF_CONSTRAINED_RANDOM_VALUES; ++i) 
    {
        bc.next(); //generate values
        cout << "Value of burst: " << bc.burst() << endl;
    }
    return 0;
}
Back to article


Listing Three
template <typename T> void fields(const T&obj)
{
    scv_extensions<T> ext = scv_extensions(obj);
    cout << "Our object has " << ext.get_num_fields() << " fields." << endl;
};
class aClass
{
public:
    long I;
    int t;
    char *p;
};
SCV_EXTENSIONS(aClass) {
public:
  scv_extensions<long> I;
  scv_extensions<int> t;
  scv_extensions<char*> p;
  SCV_EXTENSIONS_CTOR(aClass) {
    SCV_FIELD(I);
    SCV_FIELD(t);
    SCV_FIELD(p);
  }
};

void example()
{
  aClass ac;
  fields(ac);
}
Back to article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.