October 2006


The SystemC Live CD version 2.0 has been released today. It can be downloaded directly from HERE and HERE

This new version features:

  1. SystemC 2.2 beta
  2. SCV 1.0
  3. TLM 1.0
  4. SystemC / C++ tutorial (Esperan)
  5. gcc g++ 3.4.6
  6. gdb 6.3
  7. ddd 3.3.9
  8. gtkwave 1.3.79
  9. vim 7.0
  10. geany 0.7.1
  11. opera 9.02
  12. Beep Media Player 0.9.7
  13. xpdf
  14. xfce4 4.2.3.2
  15. slapt-get 0.9.11b
  16. gslapt 0.3.1.2

The file size is 137MB and can be burnt onto various media such as: Memory stick (USB), Mini CD, CD-R, CD-RW.

You will find SCLive 2.0 to be a more integrated, complete and slicker distribution with tools that will facilitate SystemC development (gdb, ddd, vim) and user customisations (gslapt, slap-get).

The zip file also contains pre-prepared configuration files to run SCLive 2.0 painlessly inside the VMWare player software.

Have a lot of fun with SCLive and don’t hesitate to send me your comments (Francais/ English) at: systemclive@googlemail.com

David Cabanis

Forewords

C++ implements Object-Orientation on the C language. For most Hardware Engineers, the principles of Object-Orientation seem fairly remote from the creation of Hardware components. However ironically, Object-Orientation was created from design techniques used in Hardware designs. Data abstraction is the central aspect of Object-Orientation which incidentally, is found in everyday hardware designs through the use of publicly visible “ports” and private “internal signals”. Furthermore, the principle of “composition” used in C++ for creating hierarchical design is almost identical to component instantiation found in hardware designs. The coming sections will introduce the basics of C++ by looking at the creation of an hardware component.

The Class

The class in C++ is called an Abstract Data Type (ADT); it defines both data members and access functions (also called methods). Both data members and access functions are said to be private by default. In other words, data members and access functions are not visible from the outside world. This ADT mechanism is analogous to a package and package body in VHDL. The designer is responsible for making publicly available the essential set of access functions for manipulating an ADT. The semantics for a C++ class declaration is as follows:

class counter

{

int value;

public:

void do_reset() { value = 0 ; }

void do_count_up() { value++ ; }

int do_read() { return value; }

};

 

In this example we see the declaration of an ADT called: “counter” with a data member “value” and publicly available access functions: “do_reset”, “do_count_up” and “do_read”. Although this class declaration is complete; commonly, a class declaration will also contain specialised functions such as constructors and a destructor. When constructors are used, they provide initial values for the ADT’s data members. This mechanism is the only allowed mean for setting a default value to any data member.
A destructor is used to perform clean-up operations before an instance of the ADT become out of scope. Pragmatically, the destructor is used for closing previously opened files or de-allocating dynamically allocated memory.
An example of an ADT with constructors and a destructor is as follows:

class counter

{

int value;

public:

void do_reset() { value = 0 ; }

void do_count_up() { value++ ; }

int do_read() { return value; }

counter() {

cout << “This a simple constructor” << endl;

value = 10;

}

counter(int arg): value(arg) {

cout << “This a more interesting constructor” << endl;

}

~counter() {

cout << “Destroying a counter object” << endl;}

};

 

In our example all access functions are made visible to the outside world through the use of the “public” keyword. However, not all functions have to be made public; they can be held hidden from the rest of the world through the use of the “private” keyword.

The Object

An object is an instance of an ADT. Any number of instances can be made of a given ADT; each of these instances are initialised individually.
An example of objects instantiation and messages passing is as follows:

void main() {

 

counter first_counter;

counter second_counter(55);

 

// Message passing

first_counter.do_reset();

for (int i=0; i < 10; i++) { first_counter.do_count_up(); }

second_counter.do_count_up();

 

first_counter.do_reset();

second_counter.do_reset();

}

 

In this example two instances of the counter ADT are created. The “first_counter” instance uses the default constructor not requiring any arguments. The “second_counter” instance uses a more sophisticated constructor form that passes an argument.

Messages are send to individual objects via the ‘dot’ notation which is similar to what is used for “struct” data structures.

 

The building of more complex objects can be achieved through the composition mechanism. This mechanism is akin to component instantiation in Hardware design.

The following example illustrates the principle of hierarchical design through composition.

 

class modulo_counter

{

counter internal_counter;

int terminal_count;

public:

void do_reset() { internal_counter.do_reset(); }

void do_count_up() {

if ( internal_counter.do_read() < terminal_count ) {

internal_counter.do_count_up();

} else { internal_counter.do_reset(); }

}

int do_read() { return internal_counter.do_read(); }

modulo_counter(int tc):

terminal_count(tc),

internal_counter(0) {

cout << “A new modulo_counter instance” << endl; }

};

 

In this example a new ADT “modulo_counter” is created form an instance of a counter ADT called “internal_counter”. An additional data member “terminal_count” was added to provide the modulo value for this new ADT. The access function “do_count_up” is customised to take in considerations of the modulo counter specifications. As it can be observed, most actions are delegated to the “internal_counter” instance via message passing. It is interesting to notice how the constructor function is used to initialise both the “terminal_count” value and the instantiated object “internal_counter”. The argument “tc” is used to set the value of “terminal_count” and the value “0” is passed to the constructor of the “internal_counter” object.

Inheritance

Along with composition, C++ provides a sophisticated mechanism for reusing code called: inheritance. With inheritance designers are able to create new ADTs from existing ones by accessing all public elements of a parent class into a child class. This principle can be more appropriate than composition since it usually requires less effort to achieve the same goal. However inheritance does not replace composition but complements it. The following example illustrates the creation of a modulo counter ADT from an existing counter ADT whilst using inheritance.

class modulo_counter : public counter {

int terminal_count;

public:

void do_count_up() {

if ( do_read() < terminal_count ) {

counter::do_count_up(); }

else { do_reset(); }

}

modulo_counter(int tc): terminal_count(tc), counter(0) {

cout << “A new modulo_counter instance” << endl; }

};

 

In this example a new access function called “do_count_up” is created. This overrides the inherited function from the parent class “counter”. The remaining public access functions (do_reset, do_read) found in the parent class are now available to the child class: “modulo_counter”. As it can be observed the creation of the “modulo_counter” class is greatly simplified through the use of inheritance. Nevertheless, the “do_read” and “counter::do_count_up” functions had to be used in the newly created “do_count_up” function to access the private data members inside the parent class. Although this is a valid solution, C++ offers a more pragmatic alternative, consisting in using a protected encapsulation in place of a private one. Unlike private members, protected members are inherited along with public ones when a parent class is derived, however, protected data members remain hidden from the outside world in the same way as private members do. A new implementation of the counter and modulo_counter class using protected data members is as follows:

class counter

{

protected:

int value;

public:

void do_reset() { value = 0 ; }

void do_count_up() { value++ ; }

int do_read() { return value; }

counter() {

cout << “This a simple constructor” << endl;

value = 10;

}

counter(int arg): value(arg) {

cout << “This a more interesting constructor” << endl;

}

~counter() { cout << “Destroying a counter object”

<< endl;}

};

 

class modulo_counter : public counter {

protected:

int terminal_count;

public:

void do_count_up() {

if ( value < terminal_count ) { value++; }

else { value = 0; }

}

modulo_counter(int tc): terminal_count(tc), counter(0) {

cout << “A new modulo_counter instance” << endl; }

};

 

In this last example the data member “value” is directly accessed from within the “modulo_counter” class. The use of the protected encapsulation has simplified the creation of the derived class by making data members from the parent class accessible.
The principle of single inheritance can be extended to allow a child class to be built from multiple parents. This principle is referred as multiple inheritance. Multiple inheritance enables designers to rapidly create new sophisticated classes from a multitude of existing ADTs. The use of multiple inheritance commonly requires careful planning since it can create numerous undesirable side effects. An example of multiple inheritance is as follows:

class reg {

protected:

int value;

public:

void do_reset() { value = 0; }

int do_read() { return value; }

void do_write(int arg) { value = arg; }

reg(): value(0) {}

};

 

class up_counter: virtual public reg {

public:

void do_count_up() { value++; }

};

 

class down_counter: virtual public reg {

public:

void do_count_down() { value–; }

};

 

class up_down_counter: public up_counter, public down_counter { };

 

In this example the “up_down_counter” ADT is created from two parent classes: “up_counter” and “down_counter”. The “up_down_counter” does not require any code since it inherits all of its implementation from its parent classes. It is important to point out that both the “up_counter” and “down_conter” are inheriting virtually the register class. The virtual inheritance is used here to prevent multiple declarations of the “value”, “do_reset”, “do_read”, “do_write” inside the “up_down_counter” since this ADT inherits those attributes twice, though both the “up_counter” and “down_counter” inheritance.

Template Class

The template mechanism is used for creating more versatile ADTs. Templates can be used for variable types or values. An example of a simple template class is as follows:

template<int min, int max> class barrel_counter {

private:

int value;

public:

void do_reset() { value = min; }

void do_count_up() {

if (value< max ) { value++; }

else {value = min; }

}

int do_read() { return value; }

barrel_counter(): value(min) { }

};

 

 

int main(int argc, char *argv[])

{

barrel_counter<10, 50> first_counter;

barrel_counter<0, 10> second_counter;

 

for (int i=0; i < 60; i++) {

first_counter.do_count_up();

second_counter.do_count_up();

cout << first_counter.do_read() << endl;

cout << second_counter.do_read() << endl;

}

return 0;

}

 

This counter implementation defines two templates “min” and “max”; these variables are then used inside the class to define the boundaries of the barrel counter. This example uses templates to set variable values; alternatively templates can be used to set a variable type. An example of templates used for variable types is as follows:

template<class T> class adder {

private:

T result;

public:

T add( T a, T b ) {

result = a + b ;

return result ;

}

};

 

int main(int argc, char *argv[]) {

adder<int> integer_adder;

adder<float> float_adder;

 

int int_result = integer_adder.add(3, 5);

float float_result = float_adder.add(6.7, 10.2);

cout << int_result << endl;

cout <<float_result << endl;

return 0;

}

 

This example uses a variable type “T” used throughout the class to provide a versatile ADT implementation. At a later stage two objects of type adder are declared however, both adders use different types for its operations.

Summary

As we illustrated in this section, Hardware components can be modeled in C++ and to some extent the mechanisms used are similar to those used in HDLs. Additionally C++ provides inheritance as a way to complement the composition mechanism and promotes design reuse.
Nevertheless, C++ does not provide for concurrency which is an essential aspect of systems modeling. Furthermore, timing and propagation delays cannot easily expressed in C++. The SystemC library provides additional mechanisms such as processes and dedicated data types to tackle C++ modeling deficiencies.

The pdf file and the source files for this tutorial can be found HERE.

Et Voila !

David Cabanis

Forewords

These tutorials do not try to teach you everything about SystemC in large chapters taken from the language reference manual (LRM). Instead they focus on the most important aspects of the language, basic and advanced covering them in tutorials designed to take around ten minutes each.

Ports

A Port is an essential element of any SystemC model. Ports are used by modules as a gateway to and from the outside world. In a simplistic way, one can consider a port like the pin of a hardware component.
In HDLs such as VHDL or Verilog, ports are very much like the pins metaphor; In the case of SystemC ports have a substantially more generalized purpose and as a result they are far more sophisticated and useful than their HDLs counterparts.

A simple SystemC port declaration would look like the following code:

sc_in<bool> my_input;

As can be observed the semantic is kept to a minimum level of complexity; The port has a name my_input and in this particular instance, it is of input mode since we used the sc_in<> port type (or port mode if you prefer). The last observation that we can make from this simple line of code is the use of the bool data type inside the <> of the sc_in port type. This data type refers to the kind of data that will be exchanged on that port. In other words we are expecting to receive boolean values on the my_input port.

As you would expect, numerous predefined port types exist in SystemC; Such as: sc_in<Type>, sc_out<Type>, sc_inout<Type>, etc. All of those ports are almost identical to their HDL equivalents in VHDL or Verilog; they have a name, a type and a mode (in, out, etc). As a mater of fact, these kinds of ports are commonly used in RTL SystemC. However, as we indicated earlier, SystemC ports are much more than just RTL like ports; This is because SystemC ports not only have a name and a type but most importantly they define the access mechanisms that should be used on them. Pragmatically the access mechanisms are just a list of allowed messages that can be used on them. If we consider the sc_in<bool> port of the previous example, SystemC defines that one can use the read() message on it. Not surprisingly, an sc_out<> port would allow the use of the write() message. The following code illustrates the use of an sc_in<bool> and an sc_out<bool> ports.

// More code not shown here

sc_in<bool> my_input;
sc_out<bool> my_output;

// More code not shown here

void do_run() {
if (my_input.read() == true) {
my_output.write(false);
} else {
my_output.write(true);
}
}
};

// More code not shown here


For the sake of simplicity the creators of the SystemC language have provided operators that can be used instead of the read() and write() messages. Consequently the following code is also acceptable:

// More code not shown…

void do_run() {
if (my_input == true) {
my_output = false;
} else {
my_output = true;
}
}

// More code not shown…

The list of access messages allowed on specific ports has already been defined for the existing SystemC ports. However SystemC allows the user to define his/her very own set of messages for his/her very own ports. For instance one could imagine the existence of a high-level port used on a CPU model that could receive messages such as: dma_request() or interrupt_request() form the outside world.
The creation of user defined ports goes beyond the scope of this tutorial and can only be covered when you will have gained an understanding of the notion of SystemC interfaces.

Ports and Modules

Up until now we only considered ports in isolation. Prosaically ports are parts of modules and are used to pass data of some form in and out of the modules. SystemC ports are by definition object instances of predefined classes (sc_in<>, sc_out<>, etc.) . As a result they can be accessed from within any member function defined inside the module. For instance the following code would be a valid way of using ports:

class portsTutorial: public sc_module {
public:
sc_in<bool> my_input;
sc_out<bool> my_output;

SC_HAS_PROCESS(portsTutorial);

portsTutorial(sc_module_name nm): sc_module(nm) {
SC_METHOD(do_run);
sensitive << my_input;
}

protected:
void do_run() {
if (my_input.read() == true) {
my_output.write(false);
} else {
my_output.write(true);
}
}
};

Ports and Signals

Ports and signals have a special relationship since ports will connect to other ports via signals (also known as channels). More importantly, ports and signals share a common language. All the messages that can be used on a port should also be available on the signals (channels) connected to that port. As a result specific ports can only be connected to compatible signals; for instance an sc_in<> or an sc_out<> port can be connected to an sc_signal<> however and sc_fifo_in<> or an sc_fifo_out<> port can only be connected to an sc_fifo<> kind of channel. The following code illustrates the use of ports with signals:

// Top level
class top: public sc_module {
public:
sc_signal<bool> sig_1, sig_2;

portsTutorial uut;

top(sc_module_name nm): sc_module(nm), uut(”uut“) {
uut.my_input(sig_1);
uut.my_output(sig_2);
}
};


As can be observed the connection of a port and a signal is a simple operation. Nevertheless this semantics may look surprising since a usual object-oriented message is of the form: object_name.message(parameters) . In this case, uut.my_input(sig_1) is composed of two objects: uut which is the component’s name and my_input which is the port’s name and finally sig_1 is a parameter. In other words we have: object_name.object_name(parameter) but no message call on the object. The reason is a simple one; the creators of SystemC used an overloading trick to make ports/signals connections appear simpler. In fact if you wanted to be a object-orientation purist you could write the following code to the same effect:

top(sc_module_name nm): sc_module(nm), uut(”uut“) {
uut.my_input.bind(sig_1);
uut.my_output.bind(sig_2);
}

The bind() message is effectively what is being used to connect a port to a signal (channel) although in the previous example it was hidden through an overloading trick.
Lastly, there is yet anther way to connect your ports to your signals this is done using positional mapping where the signals are mapped to the ports by position. An example of positional mapping is as follows:

top(sc_module_name nm): sc_module(nm), uut(”uut“) {
uut(sig_1, sig_2);
}

This style is certainly the most succinct way of connecting a component to a set of signals however it is usually seen as a more error prone way since the signals can easily be miss-ordered.

Summary

This tutorial covered the basic uses of ports in a SystemC model. Ports are part of modules and are used to connect modules together via signals (channels). In this tutorial wefocused on the predefined SystemC ports however, most commonly the user creates it’s own port kind defining it’s own set of messages that can be exchanged on those ports. The subject of user defined port we be covered in an other tutorial.

The pdf file and the source files for this tutorial can be found HERE.

David Cabanis.