thundium Posted January 27, 2014 Report Share Posted January 27, 2014 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 Quote Link to comment Share on other sites More sharing options...
apfitch Posted January 27, 2014 Report Share Posted January 27, 2014 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 Quote Link to comment Share on other sites More sharing options...
dakupoto Posted January 28, 2014 Report Share Posted January 28, 2014 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. Quote Link to comment Share on other sites More sharing options...
David Black Posted February 12, 2014 Report Share Posted February 12, 2014 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 Quote Link to comment Share on other sites More sharing options...
dakupoto Posted February 14, 2014 Report Share Posted February 14, 2014 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. Quote Link to comment Share on other sites More sharing options...
David Black Posted February 14, 2014 Report Share Posted February 14, 2014 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. apfitch 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.