Jump to content

Multiple inheritance in SystemC, is it possible?


katang

Recommended Posts

What is illegal in what I am trying to do?
 

#include "systemc.h"

SC_MODULE (BIT_ADDER)
{
  SC_CTOR (BIT_ADDER)
  {
       SC_METHOD (process);
  }
  void process();
};


class testbench: sc_core::sc_module
{
public:
        testbench(sc_core::sc_module_name nm);
        virtual void test(void) = 0;
};

class testbench_adder: public  testbench,   BIT_ADDER
{
public:
    testbench_adder(sc_core::sc_module_name nm);
        void test();
};

void BIT_ADDER::process()
{}

SC_HAS_PROCESS(testbench);
testbench::testbench(sc_core::sc_module_name nm)
: sc_core::sc_module(nm)
{}


SC_HAS_PROCESS(testbench_adder);
testbench_adder::testbench_adder(sc_core::sc_module_name nm)
:  testbench::testbench(nm), BIT_ADDER("UUT")
{
   SC_THREAD(test);
}

void testbench_adder::test()
{
}

 

Link to comment
Share on other sites

No, multiple inheritance is not supported in this case 

Here is quote from IEEE 1666-2011

Quote

NOTE 2—Since the classes having sc_object as a direct base class (that is, sc_module, sc_port, sc_export, and
sc_prim_channel) have class sc_object as a non-virtual base class, any class derived from these classes can have at
most one direct base class derived from class sc_object. In other words, multiple inheritance from the classes derived
from class sc_object is not permitted.

 

So you have two options:

  1. Use composition instead of inheritance : in SystemC case this means you need to instantiate modules and bind their ports
  2. In some cases you can put some sc_objects in pure C++ classes (not sc_modules). This technique is commonly used for "port bundles". For example:
struct clock_reset_if {
	sc_in_clk clk{"clk"};
	sc_in<bool> rstn{"rstn"};
}

struct some_module: sc_module, clock_reset_if {
// ...         
}

  

Unfortunately this approach does not work with SC_METHOD/SC_THREAD macros.  But I think it should work with sc_spawn.

 

Link to comment
Share on other sites

Roman is right with his statements and suggested solutions. However, you should also reconsider the structure of your test bench. A test bench should primarily interact with the DUT through its public interface. Therefore, composition is the right choice, i.e., instantiate your BIT_ADDER inside your test bench module as a member variable. Then, bind its ports to signals and drive/monitor them from SC_METHODs/SC_THREADs. This setup resembles test benches in VHDL. You may still use single inheritance in this case to overload certain functions. However, you should pay attention that only one sc_module_name object gets constructed during the construction of you class, e.g., by taking it as a const reference (see clause 5.3 in IEEE Std 1666-2011).

You may also instantiate the test bench module (or separate stimuli/monitor modules) and the DUT on the same hierarchical level from within sc_main() and then connect them via channels.

Link to comment
Share on other sites

  • 2 years later...
On 6/14/2017 at 6:33 PM, Roman Popov said:
  1. In some cases you can put some sc_objects in pure C++ classes (not sc_modules). This technique is commonly used for "port bundles". For example:

struct clock_reset_if {
	sc_in_clk clk{"clk"};
	sc_in<bool> rstn{"rstn"};
}

struct some_module: sc_module, clock_reset_if {
// ...         
}

  

 

I'm trying to use this approach for my problem. 

I created a header with two classes as you mentioned. Then I inherit them into the module where I want the "port bundle" to be.

I get a lot of these Warnings:

"Warning: (W505) object already exists: monitor.R_OP_MODE. Latter declaration will be renamed to monitor.R_OP_MODE_0
In file: c:\xx\xx\xx\xx\systemc-2.3.3\src\sysc\kernel\sc_object_manager.cpp:153"

And one error, which probably causes the abortion.

Error: (E109) complete binding failed: port not bound: port 'monitor.T_PRESET_MANUAL' (sc_inout)
In file: c:\xx\xx\xx\xx\systemc-2.3.3\src\sysc\communication\sc_port.cpp:235

A minimal example is provided at the of my post with zipped source files.

//interface.h
#pragma once
#include <systemc.h>

// Class is meant to be inherited from, to have all Interface inputs
struct if_inputs
{
	// port declaration
	sc_inout<sc_uint<4> > R_OP_MODE{ "R_OP_MODE" };
	sc_inout<sc_uint<8> > R_PRESET_MANUAL{ "R_PRESET_MANUAL" };
};

// Class is meant to be inherited from, to have all Interface outputs
struct if_outputs
{
	// port declaration
	sc_inout<sc_uint<2> > T_BIT{ "T_BIT" };
	sc_inout<sc_uint<4> > T_OP_MODE{ "T_OP_MODE" };
	sc_inout<sc_uint<8> > T_PRESET_MANUAL{ "T_PRESET_MANUAL" };
};

 

//A.h
#pragma once
#include <systemc.h>
#include "interface.h"

class A : public sc_module, public if_inputs, 
	 public if_outputs
{
public:

public:
SC_CTOR(A)
	{
	//SC_METHOD(T01_SBIT_sig_forward);
	//sensitive << T01_SBIT_in;
	}
};
//B.h
#pragma once
#define SC_INCLUDE_DYNAMIC_PROCESSES //for sc_spawn
#include <systemc.h>
#include "interface.h"
#include <functional>

class B : public sc_module, public if_inputs, public if_outputs
{
public:
	// port declaration
	sc_in<bool> POWER_ON{ "POWER_ON" };

	// process functionality
	void B::some_process();
	
private:
	std::vector<sc_time> timings{sc_time(3,SC_SEC), sc_time(6,SC_SEC),
		sc_time(9,SC_SEC) };

public:
	//constructor
	SC_CTOR(B) {
		//register processes

		SC_THREAD(some_process);
		sensitive << POWER_ON;
	}
};
//stimulator.h
#pragma once
#define SC_INCLUDE_DYNAMIC_PROCESSES //for sc_spawn
#include <systemc.h>
#include "interface.h"

struct if_inputs; 
struct if_outputs;

class stimulator: public sc_module, public if_inputs, public if_outputs
{
public:
	sc_out<bool> POWER_ON;
	//processes
	void stimulator::stimulus_generator();

public:
	// constructor
	SC_CTOR(stimulator):
		// name ports for easier debugging
		POWER_ON("POWER_ON")
	{
		// Register processes
		SC_THREAD(stimulus_generator); // waits on fifo write_event
	}
};
// monitor.h
#pragma once
#define SC_INCLUDE_DYNAMIC_PROCESSES //for sc_spawn
#include <systemc.h>
#include"interface.h"

class monitor : public sc_module, public if_inputs, public if_outputs
{
public:
	//// port declaration
	sc_in<bool> POWER_ON;

public:
	class sim: public if_inputs, public if_outputs
	{

	}sim;
	class stub : public if_inputs, public if_outputs
	{

	}stub;

public:
	SC_HAS_PROCESS(monitor);
	//// constructor
	monitor::monitor(sc_module_name name_);
};
//stimulator.cpp
#include "stimulator.h"

void stimulator::stimulus_generator()
{
	POWER_ON = true;
}
//source file: blackbox.cpp
#include "B.h"

void B::some_process()
{
}
//monitor.cpp
#include "monitor.h"

monitor::monitor(sc_module_name name_)
	:sc_module(name_)
{
}
// main.cpp
#pragma once
#include <systemc.h>
#include "B.h"
#include "A.h"
#include "stimulator.h"
#include "monitor.h"

int sc_main(int argc, char** argv)
{
	// Set some SystemC time parameters (maybe not necessarily needed).  
	sc_set_time_resolution(1.0, SC_US);

	// signal declaration
	sc_signal<bool > POWER_ON{ "POWER_ON" };
	sc_signal<sc_uint<2>, SC_MANY_WRITERS> T_BIT_sim{"T_BIT_sim"};
	sc_signal<sc_uint<2>, SC_MANY_WRITERS> T_BIT_stub{"T_BIT_stub"};
	sc_signal<sc_uint<4>, SC_MANY_WRITERS> T_OP_MODE_sim{ "T_OP_MODE_sim" };
	sc_signal<sc_uint<4>, SC_MANY_WRITERS> T_OP_MODE_stub{ "T_OP_MODE_stub" };
	sc_signal<sc_uint<8>, SC_MANY_WRITERS> T_PRESET_MANUAL_sim{ "T_PRESET_MANUAL_sim" };
	sc_signal<sc_uint<8>, SC_MANY_WRITERS> T_PRESET_MANUAL_stub{ "T_PRESET_MANUAL_stub" };
	sc_signal<sc_uint<4>, SC_MANY_WRITERS> R_OP_MODE_sim{ "R_OP_MODE_sim" };
	sc_signal<sc_uint<4>, SC_MANY_WRITERS> R_OP_MODE_stub{ "R_OP_MODE_stub" };
	sc_signal<sc_uint<8>, SC_MANY_WRITERS> R_PRESET_MANUAL_sim{ "R_PRESET_MANUAL_sim" };
	sc_signal<sc_uint<8>, SC_MANY_WRITERS> R_PRESET_MANUAL_stub{ "R_PRESET_MANUAL_stub" };

	B sim("sim");
	sim.POWER_ON(POWER_ON);
	sim.T_BIT(T_BIT_sim);
	sim.T_OP_MODE(T_OP_MODE_sim);
	sim.T_PRESET_MANUAL(T_PRESET_MANUAL_sim);
	sim.R_OP_MODE(R_OP_MODE_sim);
	sim.R_PRESET_MANUAL(R_PRESET_MANUAL_sim);

	A stub("stub");
	stub.T_BIT(T_BIT_stub);
	stub.T_OP_MODE(T_OP_MODE_stub);
	stub.T_PRESET_MANUAL(T_PRESET_MANUAL_stub);
	stub.R_OP_MODE(R_OP_MODE_stub);
	stub.R_PRESET_MANUAL(R_PRESET_MANUAL_stub);

	monitor mon("monitor");
	mon.POWER_ON(POWER_ON);
	mon.sim.T_BIT(T_BIT_sim);
	mon.sim.T_OP_MODE(T_OP_MODE_sim);
	mon.sim.T_PRESET_MANUAL(T_PRESET_MANUAL_sim);
	mon.sim.R_OP_MODE(R_OP_MODE_sim);
	mon.sim.R_PRESET_MANUAL(R_PRESET_MANUAL_sim);
	mon.stub.T_BIT(T_BIT_stub);
	mon.stub.T_OP_MODE(T_OP_MODE_stub);
	mon.stub.T_PRESET_MANUAL(T_PRESET_MANUAL_stub);
	mon.stub.R_OP_MODE(R_OP_MODE_stub);
	mon.stub.R_PRESET_MANUAL(R_PRESET_MANUAL_stub);

	sc_start();

	return 0;
}

 

MinExample.zip

Link to comment
Share on other sites

You have multiple instances of the "bundles" in your monitor class:

  • inherited directly
  • as additional members in the nested classes sim and stub

To avoid the name clashes, you can make sim and stub modules themselves via:

struct sim : sc_module , if_inputs, if_outputs {
  SC_CTOR(sim) {}
} sim { "sim" };

 

Link to comment
Share on other sites

Quote

I'm trying to use this approach for my problem. 

In general avoid using multiple inheritance for aggregation. It is possible, but has many drawbacks and no major benefits.  Now I regret that I've written original post, but at that time I had no enough experience myself.

Now, if we read any object oriented design book, we will learn that inheritance usually means "is-a" relation ship, and "has-a" relation ship is expressed by composition. Translating into HW modeling : what we want to express is that "some_module has port bundles", and not "some_module is port bundles". 

We can still use single inheritance in limited cases, for example if all modules in design have clock and reset, we can have a common base class like

class clocked_module : public sc_module

Back to your example. I recommend to convert your port bundles into modules:

struct if_inputs : sc_module {
  sc_inout<sc_uint<4>> SC_NAMED(R_OP_MODE);
  sc_inout<sc_uint<8>> SC_NAMED(R_PRESET_MANUAL);

  if_inputs(sc_module_name){}
};

struct if_outputs : sc_module {
  sc_inout<sc_uint<2>> SC_NAMED(T_BIT);
  sc_inout<sc_uint<4>> SC_NAMED(T_OP_MODE);
  sc_inout<sc_uint<8>> SC_NAMED(T_PRESET_MANUAL);
  
  if_outputs(sc_module_name){}
};

And now you can aggregate any number of them inside monitor. Even have a vector of port bundles:

class monitor : public sc_module
{
public:  
  if_inputs  SC_NAMED(sim_inputs);
  if_outputs SC_NAMED(sim_outputs);

  if_inputs  SC_NAMED(stub_inputs);
  if_outputs SC_NAMED(stub_outputs);
  
  sc_vector<if_inputs> SC_NAMED(inputs_vector, 3);

  monitor(sc_module_name name_);
private :
  // implementation details
};

 

Link to comment
Share on other sites

On 10/16/2019 at 3:11 PM, Philipp A Hartmann said:

You have multiple instances of the "bundles" in your monitor class:

  • inherited directly
  • as additional members in the nested classes sim and stub

To avoid the name clashes, you can make sim and stub modules themselves via:


struct sim : sc_module , if_inputs, if_outputs {
  SC_CTOR(sim) {}
} sim { "sim" };

 

Thank you Philipp, this solved my runtime errors. 

I also removed if_inputs/-outputs as parents for the monitor class, since I nested them into stub and sim.

class monitor : public sc_module
{ ... };

_________________________________

On 10/16/2019 at 3:34 PM, Roman Popov said:

In general avoid using multiple inheritance for aggregation. It is possible, but has many drawbacks and no major benefits.  Now I regret that I've written original post, but at that time I had no enough experience myself.

Now, if we read any object oriented design book, we will learn that inheritance usually means "is-a" relation ship, and "has-a" relation ship is expressed by composition. Translating into HW modeling : what we want to express is that "some_module has port bundles", and not "some_module is port bundles". 

We can still use single inheritance in limited cases, for example if all modules in design have clock and reset, we can have a common base class like


class clocked_module : public sc_module

Back to your example. I recommend to convert your port bundles into modules:


struct if_inputs : sc_module {
  sc_inout<sc_uint<4>> SC_NAMED(R_OP_MODE);
  sc_inout<sc_uint<8>> SC_NAMED(R_PRESET_MANUAL);

  if_inputs(sc_module_name){}
};

struct if_outputs : sc_module {
  sc_inout<sc_uint<2>> SC_NAMED(T_BIT);
  sc_inout<sc_uint<4>> SC_NAMED(T_OP_MODE);
  sc_inout<sc_uint<8>> SC_NAMED(T_PRESET_MANUAL);
  
  if_outputs(sc_module_name){}
};

And now you can aggregate any number of them inside monitor. Even have a vector of port bundles:


class monitor : public sc_module
{
public:  
  if_inputs  SC_NAMED(sim_inputs);
  if_outputs SC_NAMED(sim_outputs);

  if_inputs  SC_NAMED(stub_inputs);
  if_outputs SC_NAMED(stub_outputs);
  
  sc_vector<if_inputs> SC_NAMED(inputs_vector, 3);

  monitor(sc_module_name name_);
private :
  // implementation details
};

 

Thank you for the explanation and the provided examples.

Now I know two ways of solving my problem. Your solution seems to have lesse overhead, or is the same with Phlipp's solution?

Philipp's way lets me have inputs and outputs in the same scope when I connect signals to the ports in the main.cpp. Which fits my modeling style more. Is there a way to also archieve this with your solution? Without changing if_inputs/-outputs?

 

 

Link to comment
Share on other sites

Quote

Philipp's way lets me have inputs and outputs in the same scope when I connect signals to the ports in the main.cpp. Which fits my modeling style more. Is there a way to also archieve this with your solution? Without changing if_inputs/-outputs?

No, if you want to keep all ports in same scope you will need to follow Philipp's suggestion. 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...