Friday 16 November 2018

Developing & Delivering KnowHow

Home > Knowhow > Systemc > SystemC FAQ

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.

  1. sc_signal with user defined data types
  2. Parameterized modules using constructor parameters
  3. 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.

back to the top

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.

back to the top

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.

back to the top

Privacy Policy Site Map Contact Us