Jump to content

Generic pointer to sc_out<T>


SiGa

Recommended Posts

I have an SC_MODULE that contains two types(probably multiple types in the future) of sc_out ports. I want to store a reference(or pointer) of the port in a std::map, so that I can later get the right port through lookup of strings and access the write method of it. 

Following post helped me understand the topic better but for me it is still unclear how I can achieve my goal.

I don't understand how I should cast my stored reference.

 

Small example for showing how I want to use it. 

#pragma once
#include <systemc.h>
#include <map>

//typedef std::map<std::string, sc_interface*> Map0;
//typedef std::map<std::string, sc_object*> Map0;
typedef std::map<std::string, (????)*> Map0; // what type should I use since sc_out<T> varies on type T

class stimulator: public sc_module
{
public:
    sc_out<bool> POWER_ON;
    sc_out<sc_uint<2>> T01_SBIT_mil_stub;
  
private:
    Map0 dict;
  
public:
    void stimulator::stimulus_generator() {
        std::string key0;
        for(int i=0; i<=1; i++) {
            if(i==0)
                key0 = "VOLTAGE";
            else
                key0 = "T01_SBIT_mil_stub";
          
            dict::iterator it0 = dict.find(key0);
            if (it0 == dict.end())
                cout << "Map lookup key not found." << endl;
            
            // How to cast it? Type Erasure?
            if(i==0)
                it0->second->write(true); //write method not available
            else
                it0->second->write(1); //write method not available
        }
    }
  
public:
    SC_CTOR(stimulator):
        POWER_ON("POWER_ON"),
        T01_SBIT_mil_stub("T01_SBIT_mil_stub")
    {
        // if type of map is sc_object* use get_parent() instead of get_interface()
        dict.insert( std::make_pair("VOLTAGE", POWER_ON.get_interface()) );
        dict.insert( std::make_pair("T01_SBIT_mil_stub", T01_SBIT_mil_stub.get_interface()) );
          
        SC_THREAD(stimulus_generator); // runs one time at creation, which is sufficient to demonstrate
    }
}
          
          

Is an sc_object* the best way to do it? If yes how do I cast it back to the right sc_out<T>?

 

I know that I could solve my problem with a different approach, like defining enums and each enum indicates a port and thus I know which port I should write to.

But that would involve more lines of code that is not really flexible. I prefer the generic way and would like to understand the SystemC framework better.

Thanks for any help or guidance in advance 🙂

 

Link to comment
Share on other sites

Well, this topic is not easy to solve. In C++ each template instantiation (like sc_in<bool>) is a separate class in the class hierachy. The common base class is sc_port_base and this is in this context more or less useless.

Actually there I see 2 options:

  1. You store a sc_port_base pointer in your map and upon each write you check for the typeid of the specific template instance and down-cast it using dynamic_cast. This is inflexible and needs additional coding if you want to use new type.
  2. You store a writer function in your map which knows how to translate a generic value (like int or double) to the particular value. This might by a lambda which captures the port reference and therefore 'knows' hwo to write to this port. But in this case you would loose type safety as you have to store a generic function unless you can use std::variant. So your map would have to be declared as
    typedef std::map<std::string, std::function<void(unsigned)> Map0

    or (C++17):

    typedef std::map<std::string, std::function<void(std::variant<bool, sc_dt::sc_uint<2>>> Map0

     

I personally would go for option 2 as it is simpler and more flexible...

HTH

Link to comment
Share on other sites

Hello Eyck,

thank you for the reply.

19 hours ago, Eyck said:

You store a sc_port_base pointer in your map and upon each write you check for the typeid of the specific template instance and down-cast it using dynamic_cast. This is inflexible and needs additional coding if you want to use new type.

I have read that dynamic_cast will cause a perfomance penalty due to runtime checking things. Since I will be doing this a lot of times I should probably avoid this solution.

 

19 hours ago, Eyck said:

You store a writer function in your map which knows how to translate a generic value (like int or double) to the particular value. This might by a lambda which captures the port reference and therefore 'knows' hwo to write to this port. But in this case you would loose type safety as you have to store a generic function unless you can use std::variant. So your map would have to be declared as

This seems like a good solution. In the end I only need to write a function for each type of port instead of a function for each port. With this approach I run into a different problem.

I tried using c++17 through installing VisualStudio2017, but it did not accept the /std::c++17 build parameter. I will need additional time googling it up and will eventually get there.

For now my solution with c++14 would look like this:

#pragma once
#define SC_INCLUDE_DYNAMIC_PROCESSES //for sc_spawn
#include <systemc.h>
#include <map>

typedef std::map < std::string, std::function<void(sc_time,bool,sc_out<bool>)>> Map0;

class stimulator: public sc_module
{
public:
    sc_out<bool> POWER_ON;
    sc_out<sc_uint<2>> T01_SBIT_mil_stub;
  
private:
    Map0 dict;
    void delayed_stimulus_bool(sc_time delay, bool value, sc_out<bool> *port) {
        wait(delay);
        port->write(value);
    }
    void delayed_stimulus_uint2(sc_time delay, sc_uint<2> value, sc_out<sc_uint<2>> *port) {
        wait(delay);
        port->write(value);
    }
  
public:
    void stimulator::stimulus_generator() {
        std::string key0;
        for(int i=0; i<=1; i++) {
            if(i==0)
                key0 = "VOLTAGE";
            else
                key0 = "T01_SBIT_mil_stub";
          
            dict::iterator it0 = dict.find(key0);
            if (it0 == dict.end())
                cout << "Map lookup key not found." << endl;
            
            if(i==0)
                sc_spawn( sc_bind(it0->second, this, true, value, &POWER_ON) );
            else
                sc_spawn( sc_bind(it0->second, this, 1, value, &T01_SBIT_mil_stub) );
        }
    }
  
public:
    SC_CTOR(stimulator):
        POWER_ON("POWER_ON"),
        T01_SBIT_mil_stub("T01_SBIT_mil_stub")
    {
        dict.insert( std::make_pair("VOLTAGE", this->delayed_stimulus_bool) );
        dict.insert( std::make_pair("T01_SBIT_mil_stub", this->delayed_stimulus_uint2) ); // can not add this function pointer because it's type is different
          
        SC_THREAD(stimulus_generator); // runs one time at creation, which is sufficient to demonstrate
    }
}

How can I add two different method pointers into the same list without using std::variant?

Link to comment
Share on other sites

Thank you for the suggestions. In the end I upgraded my project to c++17 and stored lambda functions with a variant as parameter. When I need to create a delayed stimulus I spawn a SC_THREAD function that takes the function pointer, a delay and a variant as parameter. In the spawned function I wait for the delay and then call the function pointer and pass the variant as argument.

#pragma once
#include <systemc.h>
#include <map>

typedef std::variant< bool, unsigned int > value_types;
typedef std::function< void(value_types) > func_ptr_type;
typedef std::map <std::string, func_ptr_type> Map0;

class stimulator: public sc_module
{
public:
    sc_out<bool> POWER_ON;
    sc_out<unsigned int> T01_SBIT_mil_stub;
  
private:
    Map0 dict;
  
public:
    void stimulator::stimulus_generator() {
        std::string key0;
        for(int i=0; i<=1; i++) {
            if(i==0)
                key0 = "VOLTAGE";
            else
                key0 = "T01_SBIT_mil_stub";
          
            dict::iterator it0 = dict.find(key0);
            if (it0 == dict.end())
                cout << "Map lookup key not found." << endl;
            
            func_ptr_type func = it0->second;
            value_types argument;
            if(i==0)
                argument = true;
            else
                argument = 2;
            
            sc_time delay = sc_time(10 ,SC_MS);
            auto temp = sc_bind(&stimulator::delayed_execution, this,
                func, argument, delay);
            sc_spawn( temp );
        }
    }
    
 private: 
    void stimulator::delayed_execution(func_ptr_type func, value_types value, 
        sc_time delay) {
        cout << "stimulator::delayed_stimulus" << endl;
        wait(delay);
        func(value);
    }
  
public:
    SC_CTOR(stimulator):
        POWER_ON("POWER_ON"),
        T01_SBIT_mil_stub("T01_SBIT_mil_stub")
    {
        dict["VOLTAGE"] = [this](value_types val) { 
            POWER_ON.write(std::get<bool>(val)); };
        dict["T01_SBIT_mil_stub"] = [this](value_types val) {
            T01_SBIT_mil_stub.write(std::get<unsigned int>(val)); };
        
        // runs one time at creation, which is sufficient to demonstrate        
        SC_THREAD(stimulus_generator); 
    }
}

 

Thank you fo the help.

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...