Channels ▼


SystemC: Hardware-Oriented Constructs in C++

January, 2005: SystemC: Hardware-Oriented Constructs in C++

George Frazier works on ESL technologies and SystemC at Cadence Design Systems Inc. and is a contributing editor to Dr. Dobb's Journal. He can be reached at [email protected].

OSCI: The Open SystemC Initiative

Commerical SystemC Products

Almost 20-years-old and just emerging from adolescence, C++ continues to colonize areas once considered safe hideouts for bit nostalgics and the cycle-retentive crowd. One such inroad has emerged with the adoption of the open-source C++ library SystemC—a C++-syntax "language" for Electronic System-Level (ESL) Design and Verification.

Championed by the Open SystemC Initiative [1], a not-for-profit group of Electronic Design Automation (EDA) and electronics companies, universities, and other enthusiasts, SystemC is beginning to gain real acceptance in a community that has at times been skeptical, not only of C-based technologies in particular, but of ESL in general.

As an object-oriented language that offers the kind of runtime performance necessary for large system simulations, C++ was the obvious choice for hardware and embedded-system designers looking for a language that would allow a Standards-based approach for developing and exchanging IP defined at the ESL—a level of logical abstraction for system specification more general than the widely used Register Transaction Level (RTL) that is based on viewing system simulation as a sequence of register state changes. In this article, I examine the motivation for this jump in abstraction and take a look at other languages that are competing in this space with SystemC. A canonical example of a simple FIFO shows the basic features of the library. Finally, going beyond the OSCI reference standard, NC-SystemC is presented as one example among several commercial offerings that are turning this open standard into an industry that is fueling innovative activity at not only large companies and universities, but also among a next generation of EDA startups.

Motivation: Millions and Millions of Gates

As process geometries for new chips have sunk into the "deep-submicron" region (where each transistor is 0.18 microns wide or smaller), a new degree of semiconductor integration is now possible that has led to the emergence of very complex and versatile "systems on a chip." This has given rise to many new challenges for the electronics design industry. Some complexities, such as the effect of substrate coupling of noise and crosstalk and the use of very low-threshold devices, are physical in nature. However, the effect on tools used to design and test such systems has been no less profound. The explosion of the cardinality of gates in a typical system is outpacing the possibility of making new computers that are fast enough to simulate an adequate number of cycles in either the design or testing (verification) phases of system development. The sheer number of transistors on a chip is quickly making it impossible for electronics designers to use them all.

In the early days of EDA, a simulator could track the state of every gate on a circuit board. As complexity blossomed, newer methodologies emerged for simulating systems at a courser grain—most recently, the RTL-level simulators and related HDLs such as VHDL and Verilog. At the RTL level, state changes are tracked at each clock cycle.

Over the last decade or so, a handful of new methodologies has emerged intent on generalizing system representation to a higher level, where the simulation of systems is viewed as a behavioral flow. Behavior is tracked as a series of inputs and outputs, not clock cycles. For example, transferring a JPEG object across a bus in a multimedia system might be simulated in one step by assigning a cost and delay—one discrete action—rather than breaking down the system to individual bits transmitted as clock-driven bursts across the bus.

For many reasons, the adoption of this new ESL has been slow and given to fits of great anticipation and buzz that haven't been followed up with widespread adoption. The debate has been reminiscent of early hesitation about modular programming languages such as Fortran and C. However, the realities of the deep-submicron level of design and testing are forcing engineers to take another look at ESL because of a productivity gap that has been caused by this complexity crisis. After some scrambling, OSCI emerged with the first standard ESL technology offering: SystemC. There is compelling evidence that SystemC is about to cross the chasm into the mainstream of EDA technologies. The road has not been completely smooth, however.

ESL Language Skirmishes

Original attempts to think at the ESL were based on readily available technologies: spreadsheets, whiteboards, or even paper napkins. These early experiments usually focused on how to partition software and hardware in a system based on a cost/benefit analysis that could be roughly guessed at with tools such as Microsoft Excel. Of course, these ad hoc methods were unacceptable for the purpose of developing ESL models of IP that could be exchanged, reused, and grouped to construct modular systems. This last requirement—that of reuse and modularity—spurred interest in developing an object-oriented technology for ESL. Along with the fact that many systems already contained software, much of it in some dialect of C, several C++-based technologies emerged culminating with SystemC.

Most hardware designers, though, were not fluent in C++. Their languages of choice were VHDL, Verilog, or possibly C. Many were put off by C++ and its relatively slow compile times, cross-platform inconsistencies that were more glaring before standardization (but still exist today), and the learning curve of advanced C++ topics such as templates, multiple inheritance, and class libraries.

An ESL extension to Verilog—System Verilog—emerged as a competitor to SystemC [2]. There is at times fervent debate going on between proponents of the two languages. Also, many Verilog activists worried about possible deleterious effects that a new standardized language would have on the more mature Verilog Standard (IEEE 1364-2001). While a full discussion of this interesting topic is beyond the scope of this article, for now it appears that SystemC and System Verilog will be viewed as technologies serving mostly separate but, at times, overlapping communities and complement rather than directly compete with one another. However, for IP interface specification and reuse in general, and the construction of large modular verification platforms in particular, a strong argument can be made for SystemC as the most able ESL technology. Much of this comes from how it harnesses the underlying strengths of C++.

SystemC Features

SystemC is a C++ class library for system design based on Standard C++. Currently, OSCI provides a free "reference implementation" distributed with an open-source license. (The complete source code can be downloaded at [1].) SystemC has constructs for simulating at the ESL as well as the RTL level. Commercial implementations such as Cadence's NC-SystemC/Incisive [3] and CoWare's ConvergenSC System Designer [4] support mixed-language integrations that allow a step-wise refinement of behavioral blocks, which can start at the ESL in SystemC and, via a multitool flow, end up as gate-level representations suitable for use with back-end EDA tools that will generate a tape-out for fabrication.

Figure 1 is a summary of SystemC's structure. The highest level of the SystemC hierarchy consists of a set of libraries and technologies that are based on the core SystemC library. Some of these libraries are feature specific (such as the master/slave and bus model libraries) and some are general purpose (the TLM and Verification libraries, among others). Below that are the primitive channels of SystemC. Examples include semaphores, mutexes, and FIFOs. Primitive channels are derived from SystemC data types and structural elements. The data types include HDL-like types such as four-valued logic types, bits and bit vectors, fixed-point types, as well as arbitrary precision integers, and the like. The structural elements are modules, ports, interfaces, and channels. These are the elements that model typical constructs in a chip or system design. Modules are the basic building blocks of a design that allow partitioning the system into modular chunks. A module can contain ports, processes, channels, internal data, and other modules. A port is the boundary through which a module communicates with the outside world. A module's interfaces are the set of operations that it supports. Interfaces are similar to (and implemented as) abstract methods in C++. Finally, channels implement the interfaces that define the module's signature. Below this is the event-driven simulation engine that ties the system design to the simulator. The basic construct in the simulator is a set of conceptually concurrently running processes. Finally, all of SystemC is based and derived from C++.

A C++ class hierarchy is constructed to model a system. At runtime, the system is first "elaborated" (the ports of the modules are connected to the proper channels) after which simulations can be run based on data fed to the model. The simulation is controlled by discrete event-simulation constructs in SystemC, such as mechanisms for specifying time granularity, port sensitivity, runtime semantics of processes (if the process is a separate thread, for example), a central scheduler, and so on. Advanced facilities exist for specifying different models of computation (RTL, Transaction Level, Functional, Dataflow) and communication refinements.

To illustrate, a simple model consisting of a single behavioral block can be constructed where two inputs are multiplied to produce an output. SystemC provides macros where possible that save typing. The following code uses the SC_MODULE macro that expands to produce a class called multiplier, which inherits from class sc_module:

    // local objects
        // constructor contents

I add integer input and output ports and a computation function that does the multiplication. This is all couched in C++, of course, so the semantics are straightforward:

    sc_in<int>   in1;
    sc_in<int>   in2;
    sc_out<out> out1;
    void multiply()
        out1 = in1 * in2;
        sensitive << a << b;

In the constructor, the SC_METHOD macro registers the multiply method with the scheduler and the statement:

sensitive << a << b;

which indicates that multiply is called whenever the values of a or b change. The ordering of these statements in the SC_CTOR is important, and it is here that SystemC begins to look like a language—you can write legal C++ that is not legal SystemC. Commercial implementations provide SystemC-aware parsers that recognize error conditions that would be legal in straight C++.

A FIFO Example

Listing 1, which can also be found at the OSCI web site (and used with permission of its author, Stuart Swan), demonstrates how to extend the simple read and write interfaces, and shows the use of one of SystemC's thread classes: SC_THREAD. A FIFO is a channel that implements two interfaces: write_if and read_if:

class write_if : virtual public sc_interface
     virtual void write(char) = 0;
     virtual void reset() = 0;
class read_if : virtual public sc_interface
     virtual void read(char &) = 0;
     virtual int num_available() = 0;

You can write to a FIFO or reset its contents via the write_if interface. You can read from the FIFO and query its cardinality via the read_if interface. Two sc_events are used to synchronize access to the FIFO: write_event and read_event. If you are writing to a full FIFO then wait(read_event) suspends execution until the notify method of read_event is invoked in read (that is, a read will take one element out of the FIFO so there is room to write another). sc_events are a simple method of synchronization in SystemC. Similarly, read blocks if the FIFO is empty and waits for notification via write_event.notify() that an element has been written to the FIFO.

The SystemC scheduler handles the scheduling of events during a simulation. The scheduler follows an eight-step process:

  1. Initialize. Initialize each SC_METHOD or execute each SC_THREAD until a wait is reached.
  2. Evaluate. Select a process that can run and then execute it. This can, in turn, cause other events to call notify, which can result in new processes being ready to run in this event cycle. Care must be taken not to cause infinite loops by allowing a single event to repeatedly get notified in the same time slice.
  3. While there are processes ready to run, go to step 2.
  4. Update. Update channels by calling the update() function for all processes in step 2 that called request_update().
  5. Process "delta-cycles." A delta-cycle is an event cycle that occurs in 0 time, a construct used in event simulation to simulate concurrent behavior. While delta-delayed events are being processed, time does not advance.
  6. If there are no more nonzero delay events, simulation ends.
  7. If there are, advance simulation time to the time of the next event that can run.
  8. Determine the list of events that can run at this time and go to step 2.

Continuing the example, the producer and consumer class communicate with one another via a FIFO. The producer writes a string to the FIFO one character at a time. The class is an sc_module with an output port that has the write_if interface (for instance, you can write to the FIFO via this port). Note the use of the SC_HAS_PROCESS and SC_THREAD macros. The semantics here are that the producer class has a method called main, which is the main process of the SystemC "thread" used to implement concurrency between the producer and consumer. SystemC uses several technologies to implement threads (more properly, Coroutines) including QuickThreads, Windows NT Fibers, and Posix Threads. QuickThreads [5] and Fibers are lightweight pseudothread packages that are much faster than Posix Threads and allow fast context switches when one Coroutine yields to another (where the values of registers and the contents of the thread's stack are essentially stashed until the thread awakens). The Consumer reads data while it is available and adds some output of its own in its main method.

Finally, the example has a top module that runs the show. It is instantiated in the special SystemC main module called sc_main, which is the entry point for every SystemC model. After the top is instantiated, the simulation is started with -1, indicating that the scheduler should run until no events remain. sc_start also can be invoked with periods so simulations can run for constrained lengths of time. The constructor of the top method connects the design as follows:

top(sc_module_name name) : sc_module(name)
  fifo_inst = new fifo("Fifo1");

  prod_inst = new producer("Producer1");

  cons_inst = new consumer("Consumer1");

The producer's output port out and the consumer's input port in are both connected to the FIFO top::fifo_inst. The example produces the following output:

V<9>isit www<1>.s<9>ystemc.o<1>rg<9> and see<1> 
   w<9>hat Syst<1>em<9>C can do<1> f<9>or you t<1>oday!<1>

Final Thoughts

SystemC is now the leading candidate to take ESL design and verification to the mainstream of EDA technologies. C++ is the backbone that lets the language be both expressive and fast enough to satisfy the computational demands of simulating deep-submicron designs at the front end of the production flow. The next two years should be an interesting time for the language as commercial customers start to deploy the technology to build verification platforms, large modular designs, and reusable libraries of ESL IP and the industry awaits the first commercial implementations of the competing System Verilog technology.







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.