Jump to content
Sign in to follow this  
thundium

sequence of execution for different "start_of_simulation"s

Recommended Posts

Hi All:

 

I have some modules which do different operations during "start_of_simulation" period. However, it turns that they are scheduled randomly.(?)

 

And I would wish that one specified module to have its "start_of_simulation" always run 1st, is there a way to guarantee this?

 

Thx

Share this post


Link to post
Share on other sites

There's no guarantee of the order that start_of_simulation() is called in different modules.

 

Perhaps you can put the thing you want to happen first in end_of_elaboration(), and the thing you want to happen second in start_of_simulation()?

 

regards

Alan

Share this post


Link to post
Share on other sites

Hi All:

 

I have some modules which do different operations during "start_of_simulation" period. However, it turns that they are scheduled randomly.(?)

 

And I would wish that one specified module to have its "start_of_simulation" always run 1st, is there a way to guarantee this?

 

Thx

 

Hello,

The modules are executed as threads, and it is entirely up to the operating

system's in-built thread scheduler to decide which thread to run first and

so on. Hope that helps.

Share this post


Link to post
Share on other sites

Dakupoto, the question regards start_of_simulation(), which occurs before processes start, and itself is not a process. It is still true that ordering is up to the implementation of SystemC and not prescribed by the standard.

 

Thundium, if Alan's answer doesn't work for you, you might consider writing your own mini-scheduler. It's actually rather easy if you use a std::multimap to store function pointers indexed by their priority or perhaps create a queue of function pointers (harder to manage I think). You could then register methods or lambdas to execute under control of the mini-scheduler during construction and later invoke the mini-scheduler to execute them from a single start_of_simulation (e.g. top module). I've written one of these using C++11 features (need GCC 4.7 or VS2013 to run):

 

#ifdef __MSC_VER
#  if __MSC_VER < 1700 /* Microsoft Visual Studio 2012 */
#    pragma message("ERROR: Requires C++11 vis avi Microsoft Visual Studio 2012 or later")
#  endif
#else
#  if __cplusplus < 201103L
#    ifdef WIN32
#      pragma message("ERROR: Requires C++11")
#    else
#      error "Requires C++11"
#    endif
#  endif
#endif


////////////////////////////////////////////////////////////////////////////////
// Ordered_execution allows registration of lambdas to be executed at a later
// time with specified priority.


#include <map>
#include <utility>
#include <functional>


using priority_t = size_t;
class Ordered_execution
{
  using lambda_t = std::function<void(priority_t)>;
  std::multimap<priority_t,lambda_t> execution_queue;
  public:
    void schedule(priority_t priority, const lambda_t& lambda)
    {
      execution_queue.insert(std::make_pair(priority,lambda));
    }
    void execute(void)
    {
      for (const auto& item : execution_queue)
      {
        (item.second)(item.first);
      }
      execution_queue.clear();
    }
};


// Define UNIT_TEST to run the following code
#ifdef UNIT_TEST
#include <systemc>
#include <ostream>
#include <iomanip>
#include <memory>
using namespace sc_core;
using namespace std;


namespace {
  Ordered_execution end_elab;
  Ordered_execution start_sim;
  const int max_modules = 8;
}


//------------------------------------------------------------------------------
// Simple example module registering its code
struct Simple_module
: sc_module
{
  Simple_module(sc_module_name instance_name)
  {
    cout << "  +- Constructing " << name() << "." << __func__ << endl;
    SC_HAS_PROCESS(Simple_module);
    SC_THREAD(main_thread);
    start_sim.schedule( /* Priority */ 2
                      , [&](priority_t p)
                        {
                          cout << "  start_sim " << name() << " with priority " << p << endl;
                        }
                      );
  }
  void start_of_simulation(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
  void main_thread(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
};




//------------------------------------------------------------------------------
// This module is instantiated multiple times to illustrate the general idea
struct Module
: sc_module
{
  Module(sc_module_name instance_name, priority_t priority)
  {
    cout << "  +- Constructing " << name() << "." << __func__ << endl;
    SC_HAS_PROCESS(Module);
    SC_THREAD(main_thread);
    end_elab.schedule( priority
                      , [&](priority_t p)
                        {
                          cout << "  end_elab " << name() << " with priority " << p << endl;
                        }
                      );
    start_sim.schedule( priority
                      , [&](priority_t p)
                        {
                          cout << "  start_sim " << name() << " with priority " << p << endl;
                        }
                      );
  }
  void start_of_simulation(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
  void main_thread(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
};


Module* module_ctor( const char* instance_name, size_t s )
{
  priority_t priority = abs(random())%max_modules + 1;
  cout << "- Construct module_" << s << " with priority " << priority << endl;
  return new Module(instance_name, priority);
}


//------------------------------------------------------------------------------
SC_MODULE(Top_module)
{
  Simple_module* simple;
  sc_vector<Module> module_vec;
  SC_CTOR(Top_module)
  : simple(new Simple_module("simple")), module_vec("module")
  {
    cout << "Constructing " << name() << "." << __func__ << endl;
    module_vec.init(max_modules, module_ctor);
    SC_THREAD(main_thread);
  }
  void end_of_elaboration(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
    cout << "Ordered stuff..." << endl;
    end_elab.execute();
    cout << "Finished " << name() << "." << __func__ << endl;
  }
  void start_of_simulation(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
    cout << "Ordered stuff..." << endl;
    start_sim.execute();
    cout << "Finished " << name() << "." << __func__ << endl;
  }
  void main_thread(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
    wait(1,SC_NS);
    sc_stop();
  }
};


//------------------------------------------------------------------------------
namespace {
  // Declare string used as message identifier in SC_REPORT_* calls
  char const * const MSGID = "/Doulos/example/main";
}


//------------------------------------------------------------------------------
int sc_main(int argc, char* argv[])
{
  SC_REPORT_INFO(MSGID,"Elaborating");
  Top_module* top;
  try {
    top = new Top_module("top");
  } catch (std::exception& e) {
    SC_REPORT_ERROR(MSGID,(string(e.what())+" Please fix elaboration errors and retry.").c_str());
    return 1;
  } catch (...) {
    SC_REPORT_ERROR(MSGID,"Caught exception during elaboration");
    return 1;
  }//endtry


  // Simulate
  try {
    SC_REPORT_INFO(MSGID,"Starting kernal");
    sc_start();
    SC_REPORT_INFO(MSGID,"Exited kernal");
  } catch (std::exception& e) {
    SC_REPORT_WARNING(MSGID,(string("Caught exception ")+e.what()).c_str());
  } catch (...) {
    SC_REPORT_ERROR(MSGID,"Caught exception during simulation.");
  }//endtry
  if (not sc_end_of_simulation_invoked()) {
    SC_REPORT_INFO(MSGID,"ERROR: Simulation stopped without explicit sc_stop()");
    sc_stop();
  }//endif


  size_t info_count     = sc_report_handler::get_count(SC_INFO);
  size_t warning_count  = sc_report_handler::get_count(SC_WARNING);
  size_t error_count    = sc_report_handler::get_count(SC_ERROR);
  size_t fatal_count    = sc_report_handler::get_count(SC_FATAL);
  bool success = ((fatal_count + error_count) == 0);
  cout
    << "  " << setw(4) << warning_count << " warning messages" << "\n"
    << "  " << setw(4) << error_count   << " errors" << "\n"
    << "  " << setw(4) << fatal_count   << " fatalities" << "\n"
    ;


  if (success) {
    cout << "PASSED - No errors detected." << endl;
  } else {
    cout << "FAILED" << endl;
  }//endif


  delete top;
  return (success?0:1);


}

#endif

Share this post


Link to post
Share on other sites

 

Dakupoto, the question regards start_of_simulation(), which occurs before processes start, and itself is not a process. It is still true that ordering is up to the implementation of SystemC and not prescribed by the standard.

 

Thundium, if Alan's answer doesn't work for you, you might consider writing your own mini-scheduler. It's actually rather easy if you use a std::multimap to store function pointers indexed by their priority or perhaps create a queue of function pointers (harder to manage I think). You could then register methods or lambdas to execute under control of the mini-scheduler during construction and later invoke the mini-scheduler to execute them from a single start_of_simulation (e.g. top module). I've written one of these using C++11 features (need GCC 4.7 or VS2013 to run):

 

#ifdef __MSC_VER
#  if __MSC_VER < 1700 /* Microsoft Visual Studio 2012 */
#    pragma message("ERROR: Requires C++11 vis avi Microsoft Visual Studio 2012 or later")
#  endif
#else
#  if __cplusplus < 201103L
#    ifdef WIN32
#      pragma message("ERROR: Requires C++11")
#    else
#      error "Requires C++11"
#    endif
#  endif
#endif


////////////////////////////////////////////////////////////////////////////////
// Ordered_execution allows registration of lambdas to be executed at a later
// time with specified priority.


#include <map>
#include <utility>
#include <functional>


using priority_t = size_t;
class Ordered_execution
{
  using lambda_t = std::function<void(priority_t)>;
  std::multimap<priority_t,lambda_t> execution_queue;
  public:
    void schedule(priority_t priority, const lambda_t& lambda)
    {
      execution_queue.insert(std::make_pair(priority,lambda));
    }
    void execute(void)
    {
      for (const auto& item : execution_queue)
      {
        (item.second)(item.first);
      }
      execution_queue.clear();
    }
};


// Define UNIT_TEST to run the following code
#ifdef UNIT_TEST
#include <systemc>
#include <ostream>
#include <iomanip>
#include <memory>
using namespace sc_core;
using namespace std;


namespace {
  Ordered_execution end_elab;
  Ordered_execution start_sim;
  const int max_modules = 8;
}


//------------------------------------------------------------------------------
// Simple example module registering its code
struct Simple_module
: sc_module
{
  Simple_module(sc_module_name instance_name)
  {
    cout << "  +- Constructing " << name() << "." << __func__ << endl;
    SC_HAS_PROCESS(Simple_module);
    SC_THREAD(main_thread);
    start_sim.schedule( /* Priority */ 2
                      , [&](priority_t p)
                        {
                          cout << "  start_sim " << name() << " with priority " << p << endl;
                        }
                      );
  }
  void start_of_simulation(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
  void main_thread(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
};




//------------------------------------------------------------------------------
// This module is instantiated multiple times to illustrate the general idea
struct Module
: sc_module
{
  Module(sc_module_name instance_name, priority_t priority)
  {
    cout << "  +- Constructing " << name() << "." << __func__ << endl;
    SC_HAS_PROCESS(Module);
    SC_THREAD(main_thread);
    end_elab.schedule( priority
                      , [&](priority_t p)
                        {
                          cout << "  end_elab " << name() << " with priority " << p << endl;
                        }
                      );
    start_sim.schedule( priority
                      , [&](priority_t p)
                        {
                          cout << "  start_sim " << name() << " with priority " << p << endl;
                        }
                      );
  }
  void start_of_simulation(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
  void main_thread(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
  }
};


Module* module_ctor( const char* instance_name, size_t s )
{
  priority_t priority = abs(random())%max_modules + 1;
  cout << "- Construct module_" << s << " with priority " << priority << endl;
  return new Module(instance_name, priority);
}


//------------------------------------------------------------------------------
SC_MODULE(Top_module)
{
  Simple_module* simple;
  sc_vector<Module> module_vec;
  SC_CTOR(Top_module)
  : simple(new Simple_module("simple")), module_vec("module")
  {
    cout << "Constructing " << name() << "." << __func__ << endl;
    module_vec.init(max_modules, module_ctor);
    SC_THREAD(main_thread);
  }
  void end_of_elaboration(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
    cout << "Ordered stuff..." << endl;
    end_elab.execute();
    cout << "Finished " << name() << "." << __func__ << endl;
  }
  void start_of_simulation(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
    cout << "Ordered stuff..." << endl;
    start_sim.execute();
    cout << "Finished " << name() << "." << __func__ << endl;
  }
  void main_thread(void)
  {
    cout << "Running " << name() << "." << __func__ << endl;
    wait(1,SC_NS);
    sc_stop();
  }
};


//------------------------------------------------------------------------------
namespace {
  // Declare string used as message identifier in SC_REPORT_* calls
  char const * const MSGID = "/Doulos/example/main";
}


//------------------------------------------------------------------------------
int sc_main(int argc, char* argv[])
{
  SC_REPORT_INFO(MSGID,"Elaborating");
  Top_module* top;
  try {
    top = new Top_module("top");
  } catch (std::exception& e) {
    SC_REPORT_ERROR(MSGID,(string(e.what())+" Please fix elaboration errors and retry.").c_str());
    return 1;
  } catch (...) {
    SC_REPORT_ERROR(MSGID,"Caught exception during elaboration");
    return 1;
  }//endtry


  // Simulate
  try {
    SC_REPORT_INFO(MSGID,"Starting kernal");
    sc_start();
    SC_REPORT_INFO(MSGID,"Exited kernal");
  } catch (std::exception& e) {
    SC_REPORT_WARNING(MSGID,(string("Caught exception ")+e.what()).c_str());
  } catch (...) {
    SC_REPORT_ERROR(MSGID,"Caught exception during simulation.");
  }//endtry
  if (not sc_end_of_simulation_invoked()) {
    SC_REPORT_INFO(MSGID,"ERROR: Simulation stopped without explicit sc_stop()");
    sc_stop();
  }//endif


  size_t info_count     = sc_report_handler::get_count(SC_INFO);
  size_t warning_count  = sc_report_handler::get_count(SC_WARNING);
  size_t error_count    = sc_report_handler::get_count(SC_ERROR);
  size_t fatal_count    = sc_report_handler::get_count(SC_FATAL);
  bool success = ((fatal_count + error_count) == 0);
  cout
    << "  " << setw(4) << warning_count << " warning messages" << "\n"
    << "  " << setw(4) << error_count   << " errors" << "\n"
    << "  " << setw(4) << fatal_count   << " fatalities" << "\n"
    ;


  if (success) {
    cout << "PASSED - No errors detected." << endl;
  } else {
    cout << "FAILED" << endl;
  }//endif


  delete top;
  return (success?0:1);


}

#endif

Sir,

I fully agree with what you say, but one issue with specialized solutions is

how far are they acceptable/credible to the wider community. For example,

if one were to publish a paper using the technique that you have outlined

here and the reviewer is not familiar with, it is sure to raise eye-brows. So,

in my humble opinion, solutions ought to be general enough so that anyone

can try them out - e.g., if I create a SPICE simulation of a circuit, and the

simulator generates the correct, expected output, I have passed the final

acid test, since the reviewer is free to replicate the circuit on his SPICE

and check the results, if he/she so wishes.

 

Share this post


Link to post
Share on other sites

You are free to use my solution. All you need is a C++ compiler that supports the new 2011 standard (i.e. C++11). There are several free solutions out there including GCC 4.7, Clang 3.4, and Microsoft Studios Visual C++ 2013 Express Edition. They all work with SystemC versions 2.3 and later. I provide the complete code including a testbench. Any practioner proficient in SystemC should be sufficiently proficient with C++ that this is not hard to do. The solution could also have been rewritten using function pointers, but the result would be less compact code.

 

I can point you to many tutorials and documents on C++11, which has many very good features. A few of the more forward thinking EDA vendors are about to introduce C++11 support in the near future; however, I cannot reveal their names due to confidentiality.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×