SystemC FAQ
There is quite a number of questions in the HeadStart section of the website. In this part, we are going to put the most frequently asked questions.
- sc_signal with user defined data types
- Parameterized modules using constructor parameters
- Parameterized modules using templates
1. sc_signal with user defined data types
This must be the most often asked question on the Forum at the OSCI web-site! The basic idea is to create a signal, but using a user defined data type. For instance, I might create a structure, and then use that structure within a signal.
A careful reading of the release notes for SystemC reveals that you must create versions of operator =, operator <<, and operator == . Also, if you need to trace your signal, you need to provide an overloaded sc_trace function.
The following piece of code shows a complex data type and the appropriate functions. This has all been defined in one place (the class MyType) for convenience, i.e. all the definitions are kept together.
#ifndef MYTYPE_H #define MYTYPE_H #include "systemc.h" #include#include class MyType { private: unsigned info; bool flag; public: // constructor MyType (unsigned info_ = 0, bool flag_ = false) { info = info_; flag = flag_; } inline bool operator == (const MyType & rhs) const { return (rhs.info == info && rhs.flag == flag ); } inline MyType& operator = (const MyType& rhs) { info = rhs.info; flag = rhs.flag; return *this; } inline friend void sc_trace(sc_trace_file *tf, const MyType & v, const std::string & NAME ) { sc_trace(tf,v.info, NAME + ".info"); sc_trace(tf,v.flag, NAME + ".flag"); } inline friend ostream& operator << ( ostream& os, MyType const & v ) { os << "(" << v.info << "," << std::boolalpha << v.flag << ")"; return os; } }; #endif
We've made a complete example you can download. The files are in UNIX format here, and in DOS format here.
2. Parameterized modules using constructor parameters.
Sometimes people want to make a parameterized module. There are two basic ways to do this
- Use a constructor parameter
- Use a template class
The easiest to understand is the first. It's useful for parameterizing modules which need e.g. a different amount of dynamic memory when the module is declared. Another common use is to switch on debugging information. By using default values, it's possible to design the module so that some of the parameters may be left out if not needed.
Here is a simple parameterized RAM model. The key is the use of a normal constructor together with the SC_HAS_PROCESS macro, instead of using SC_CTOR.
#ifndef RAM_H
#define RAM_H
#include "systemc.h"
SC_MODULE(ram) {
sc_in<bool> clock;
sc_in<bool> RnW; // ReadNotWrite
sc_in<int> address;
sc_inout<int> data;
void ram_proc();
SC_HAS_PROCESS(ram);
ram(sc_module_name name_, int size_=64, bool debug_ = false) :
sc_module(name_), size(size_), debug(debug_)
{
SC_THREAD(ram_proc);
sensitive << clock.pos();
buffer = new int[size];
if (debug) {
cout << "Running constructor of " << name() << endl;
}
}
private:
int * buffer;
const int size;
const bool debug;
};
void ram::ram_proc()
{
while(true) {
wait(); // synchronous to rising edge
if (RnW) {
data = buffer[address];
}
else {
buffer[address] = data;
}
}
}
#endif
Just copy and paste the code above into your C++ compiler and try compiling it! You'll have to write a testbench to test the ram fully. In the testbench, e.g. in sc_main, you can instance the ram:
ram r1("R1"); // 64 locations, debug off
ram r2("R2", 100); // 100 locations, debug off
ram r3("R3", 64, true); // 64 locations, debug on
This example has a number of interesting points
- Note the use of sc_module(name_) to pass the name of the module to the base class. In fact this is optional due to the way SystemC works, but we recommend including it for clarity.
- The combination of SC_HAS_PROCESS and a normal constructor replaces the use of SC_CTOR
- SC_HAS_PROCESS is only needed if you have SC_THREADs or SC_METHODs in your module! If there are no processes then you don't need SC_HAS_PROCESS.
3. Parameterized modules using templates.
In this question, we look at making a simple RAM model using a template class. This is potentially more flexible than using constructor parameters, as the template class may be parameterized by type, as well be value. For instance we could create a RAM model that could store a particular type of data, as well as specifying the size of the RAM.
Here then is a simple parameterized RAM model where we can set both the data type stored in the RAM and the number of elements (the size).
It is usual that functions that are part of a template class have to be defined in the header file where the template class is created. This is a limitation of current compilers.
#ifndef RAMT_H
#define RAMT_H
#include "systemc.h"
template <class T, int size = 100>
SC_MODULE(ram) {
sc_in<bool> clock;
sc_in<bool> RnW; // ReadNotWrite
sc_in<int> address;
sc_inout<T> data;
void ram_proc();
SC_HAS_PROCESS(ram);
ram(sc_module_name name_, bool debug_ = false) :
sc_module(name_), debug(debug_)
{
SC_THREAD(ram_proc);
sensitive << clock.pos();
buffer = new T[size];
if (debug) {
cout << "Running constructor of " << name() << endl;
cout << "Number of locations is " << size << endl;
}
}
private:
T * buffer;
const bool debug;
};
template <class T, int size>
void ram<T,size>::ram_proc()
{
while(true) {
wait(); // synchronous to rising edge
if (RnW) {
data = buffer[address];
}
else {
buffer[address] = data;
}
}
}
#endif
Just copy and paste the code above into your C++ compiler and try compiling it! You'll have to write a testbench to test the ram fully. In the testbench, e.g. in sc_main, you can instance the ram:
ram<int, 16> r1("R1", true); // int, 16 locations, debug on
ram<float, 16> r2("R2", false); // float, 16 locations, debug off
ram<double> r3("R3", true); // double, 100 locations, debug on
Note the following...
- The member functions must be declared in the header file - they can't be put in a separate .cpp file with current C++ compilers
- It is possible to have a default value on template parameters - note how size is given a default value of 100.


