diff --git a/src/sysc/communication/sc_prim_channel.cpp b/src/sysc/communication/sc_prim_channel.cpp index 88afac4b6..e2cb35357 100644 --- a/src/sysc/communication/sc_prim_channel.cpp +++ b/src/sysc/communication/sc_prim_channel.cpp @@ -63,6 +63,17 @@ sc_prim_channel::sc_prim_channel( const char* name_ ) m_registry->insert( *this ); } +sc_prim_channel::sc_prim_channel( bool is_kernel ) +: sc_object( ), + m_registry( simcontext()->get_prim_channel_registry() ), + m_update_next_p( 0 ) +{ + if (is_kernel) { + m_registry->insert_internal( *this ); + } else { + m_registry->insert( *this ); + } +} // destructor @@ -237,18 +248,8 @@ class sc_prim_channel_registry::async_update_list // ---------------------------------------------------------------------------- void -sc_prim_channel_registry::insert( sc_prim_channel& prim_channel_ ) +sc_prim_channel_registry::insert_internal( sc_prim_channel& prim_channel_ ) { - if( sc_is_running() ) { - SC_REPORT_ERROR( SC_ID_INSERT_PRIM_CHANNEL_, "simulation running" ); - return; - } - - if( m_simc->elaboration_done() ) { - SC_REPORT_ERROR( SC_ID_INSERT_PRIM_CHANNEL_, "elaboration done" ); - return; - } - #ifdef DEBUG_SYSTEMC // check if prim_channel_ is already inserted for( int i = 0; i < size(); ++ i ) { @@ -264,6 +265,21 @@ sc_prim_channel_registry::insert( sc_prim_channel& prim_channel_ ) } +void +sc_prim_channel_registry::insert( sc_prim_channel& prim_channel_ ) +{ + if( sc_is_running() ) { + SC_REPORT_ERROR( SC_ID_INSERT_PRIM_CHANNEL_, "simulation running" ); + return; + } + + if( m_simc->elaboration_done() ) { + SC_REPORT_ERROR( SC_ID_INSERT_PRIM_CHANNEL_, "elaboration done" ); + return; + } + insert_internal( prim_channel_ ); +} + void sc_prim_channel_registry::remove( sc_prim_channel& prim_channel_ ) { diff --git a/src/sysc/communication/sc_prim_channel.h b/src/sysc/communication/sc_prim_channel.h index 917563709..d32a904e1 100644 --- a/src/sysc/communication/sc_prim_channel.h +++ b/src/sysc/communication/sc_prim_channel.h @@ -66,6 +66,7 @@ class SC_API sc_prim_channel // constructors sc_prim_channel(); explicit sc_prim_channel( const char* ); + explicit sc_prim_channel( bool ); // destructor virtual ~sc_prim_channel(); @@ -229,7 +230,9 @@ class SC_API sc_prim_channel class sc_prim_channel_registry { friend class sc_simcontext; + friend class sc_prim_channel; + void insert_internal( sc_prim_channel& ); public: void insert( sc_prim_channel& ); diff --git a/src/sysc/kernel/sc_event.cpp b/src/sysc/kernel/sc_event.cpp index 74b6cae3f..0a6314eec 100644 --- a/src/sysc/kernel/sc_event.cpp +++ b/src/sysc/kernel/sc_event.cpp @@ -30,6 +30,7 @@ #include #include "sysc/kernel/sc_event.h" +#include "sc_simcontext.h" #include "sysc/kernel/sc_kernel_ids.h" #include "sysc/kernel/sc_stage_callback_registry.h" #include "sysc/kernel/sc_process.h" @@ -61,6 +62,10 @@ sc_event::basename() const void sc_event::cancel() { + if (m_simc != sc_get_curr_simcontext()) { + m_async_helper.pending_cancel(); + return; + } // cancel a delta or timed notification switch( m_notify_type ) { case DELTA: { @@ -86,6 +91,10 @@ sc_event::cancel() void sc_event::notify() { + if (m_simc != sc_get_curr_simcontext()) { + m_async_helper.pending_notify(); + return; + } // immediate notification if( !m_simc->evaluation_phase() ) // coming from @@ -103,6 +112,10 @@ sc_event::notify() void sc_event::notify( const sc_time& t ) { + if (m_simc != sc_get_curr_simcontext()) { + m_async_helper.pending_notify(t); + return; + } if( m_notify_type == DELTA ) { return; } @@ -168,6 +181,10 @@ static void sc_warn_notify_delayed() void sc_event::notify_delayed() { + if (m_simc != sc_get_curr_simcontext()) { + m_async_helper.pending_notify(); + return; + } sc_warn_notify_delayed(); if( m_notify_type != NONE ) { SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 ); @@ -180,6 +197,11 @@ sc_event::notify_delayed() void sc_event::notify_delayed( const sc_time& t ) { + if (m_simc != sc_get_curr_simcontext()) { + m_async_helper.pending_notify(t); + return; + } + sc_warn_notify_delayed(); if( m_notify_type != NONE ) { SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 ); @@ -287,6 +309,7 @@ sc_event::sc_event( const char* name ) , m_threads_dynamic() , m_name() , m_parent_with_hierarchy_flag(NULL) + , m_async_helper(this) { register_event( name ); } @@ -310,6 +333,7 @@ sc_event::sc_event() , m_threads_dynamic() , m_name() , m_parent_with_hierarchy_flag(NULL) + , m_async_helper(this) { register_event( NULL ); } @@ -333,6 +357,7 @@ sc_event::sc_event( kernel_tag, const char* name ) , m_threads_dynamic() , m_name() , m_parent_with_hierarchy_flag(NULL) + , m_async_helper(this) { register_event( name, /* is_kernel_event = */ true ); } @@ -540,7 +565,7 @@ union sc_event_timed_u char dummy[sizeof( sc_event_timed )]; }; -static +thread_local static sc_event_timed_u* free_list = 0; void* diff --git a/src/sysc/kernel/sc_event.h b/src/sysc/kernel/sc_event.h index d7884f6b3..84094e2a4 100644 --- a/src/sysc/kernel/sc_event.h +++ b/src/sysc/kernel/sc_event.h @@ -34,7 +34,9 @@ #include "sysc/kernel/sc_kernel_ids.h" #include "sysc/kernel/sc_simcontext.h" #include "sysc/communication/sc_writer_policy.h" +#include "sysc/communication/sc_prim_channel.h" #include "sysc/utils/sc_ptr_flag.h" +#include #if defined(_MSC_VER) && !defined(SC_WIN_DLL_WARN) #pragma warning(push) @@ -273,6 +275,46 @@ class SC_API sc_event friend class sc_join; friend class sc_trace_file; + class pending_helper : public sc_prim_channel { + std::mutex mutex; + class sc_event *event; + + bool cancel = false; + sc_time time; + + void update(void) { + std::lock_guard lg(mutex); + if (cancel) { + event->cancel(); + } else { + if (time < sc_time_stamp()) { + event->notify(SC_ZERO_TIME); + } else { + event->notify(time-sc_time_stamp()); + } + } + } + + public: + void pending_notify(sc_time t) { + std::lock_guard lg(mutex); + time = t+sc_time_stamp(); + async_request_update(); + } + void pending_notify() { + std::lock_guard lg(mutex); + time = SC_ZERO_TIME; + async_request_update(); + } + void pending_cancel() { + std::lock_guard lg(mutex); + cancel = true; + async_request_update(); + } + + pending_helper(sc_event *e) : sc_prim_channel(true) { event = e; } + }; + friend class pending_helper; public: sc_event(); @@ -354,6 +396,9 @@ class SC_API sc_event // disabled sc_event( const sc_event& ); sc_event& operator = ( const sc_event& ); + + pending_helper m_async_helper; + }; // ---------------------------------------------------------------------------- @@ -424,6 +469,10 @@ inline void sc_event::notify_internal( const sc_time& t ) { + if (m_simc != sc_get_curr_simcontext()) { + m_async_helper.pending_notify(t); + return; + } if( t == SC_ZERO_TIME ) { // add this event to the delta events set m_delta_event_index = m_simc->add_delta_event( this ); @@ -441,6 +490,10 @@ inline void sc_event::notify_next_delta() { + if (m_simc != sc_get_curr_simcontext()) { + m_async_helper.pending_notify(SC_ZERO_TIME); + return; + } if( m_notify_type != NONE ) { SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 ); } diff --git a/src/sysc/kernel/sc_name_gen.cpp b/src/sysc/kernel/sc_name_gen.cpp index 72380bad3..b479b4ef8 100644 --- a/src/sysc/kernel/sc_name_gen.cpp +++ b/src/sysc/kernel/sc_name_gen.cpp @@ -45,6 +45,7 @@ sc_name_gen::sc_name_gen() : m_unique_name_map(), m_unique_name() sc_name_gen::~sc_name_gen() { + std::unique_lock lock(m_mutex); sc_strhash::iterator it( m_unique_name_map ); for( ; ! it.empty(); it ++ ) { delete it.contents(); @@ -58,6 +59,7 @@ sc_name_gen::~sc_name_gen() const char* sc_name_gen::gen_unique_name( const char* basename_, bool preserve_first ) { + std::unique_lock lock(m_mutex); if( basename_ == 0 || *basename_ == 0 ) { SC_REPORT_ERROR( SC_ID_GEN_UNIQUE_NAME_, 0 ); basename_ = "unnamed"; // usually not reached diff --git a/src/sysc/kernel/sc_name_gen.h b/src/sysc/kernel/sc_name_gen.h index 2e5c7622a..8928a0781 100644 --- a/src/sysc/kernel/sc_name_gen.h +++ b/src/sysc/kernel/sc_name_gen.h @@ -30,6 +30,7 @@ #ifndef SC_NAME_GEN #define SC_NAME_GEN +#include #include #include "sysc/utils/sc_hash.h" @@ -52,7 +53,7 @@ class sc_name_gen bool preserve_first = false ); private: - + std::mutex m_mutex; sc_strhash m_unique_name_map; std::string m_unique_name; diff --git a/src/sysc/kernel/sc_object_manager.cpp b/src/sysc/kernel/sc_object_manager.cpp index abcb69b36..b7f22d723 100644 --- a/src/sysc/kernel/sc_object_manager.cpp +++ b/src/sysc/kernel/sc_object_manager.cpp @@ -98,7 +98,9 @@ sc_object_manager::~sc_object_manager() // | Result is an std::string containing the name. // +---------------------------------------------------------------------------- std::string sc_object_manager::create_name(const char* leaf_name) -{ +{ + std::unique_lock lock(m_mutex); + bool clash; // true if path name exists in obj table std::string leafname_string; // string containing the leaf name. std::string parentname_string; // parent path name @@ -337,6 +339,8 @@ sc_object_manager::hierarchy_size() bool sc_object_manager::insert_external_name(const std::string& name) { + std::unique_lock lock(m_mutex); + if(!name_exists(name)) { m_instance_table[name].m_element_p = NULL; m_instance_table[name].m_name_origin = SC_NAME_EXTERNAL; @@ -368,6 +372,8 @@ sc_object_manager::insert_external_name(const std::string& name) void sc_object_manager::insert_event(const std::string& name, sc_event* event_p) { + std::unique_lock lock(m_mutex); + m_instance_table[name].m_element_p = static_cast(event_p); m_instance_table[name].m_name_origin = SC_NAME_EVENT; } @@ -385,6 +391,8 @@ sc_object_manager::insert_event(const std::string& name, sc_event* event_p) void sc_object_manager::insert_object(const std::string& name, sc_object* object_p) { + std::unique_lock lock(m_mutex); + m_instance_table[name].m_element_p = static_cast(object_p); m_instance_table[name].m_name_origin = SC_NAME_OBJECT; } @@ -479,6 +487,8 @@ sc_object_manager::top_of_module_name_stack_name() const void sc_object_manager::remove_event(const std::string& name) { + std::unique_lock lock(m_mutex); + instance_table_t::iterator it; // instance table iterator. it = m_instance_table.find(name); if(it != m_instance_table.end() @@ -501,6 +511,8 @@ sc_object_manager::remove_event(const std::string& name) void sc_object_manager::remove_object(const std::string& name) { + std::unique_lock lock(m_mutex); + instance_table_t::iterator it; // instance table iterator. it = m_instance_table.find(name); if(it != m_instance_table.end() @@ -523,6 +535,8 @@ sc_object_manager::remove_object(const std::string& name) bool sc_object_manager::remove_external_name(const std::string& name) { + std::unique_lock lock(m_mutex); + instance_table_t::iterator it; // instance table iterator. it = m_instance_table.find(name); if(it != m_instance_table.end() diff --git a/src/sysc/kernel/sc_object_manager.h b/src/sysc/kernel/sc_object_manager.h index fabc38d42..eccad562e 100644 --- a/src/sysc/kernel/sc_object_manager.h +++ b/src/sysc/kernel/sc_object_manager.h @@ -31,6 +31,7 @@ #define SC_OBJECT_MANAGER_H #include +#include #include namespace sc_core { @@ -110,6 +111,7 @@ class sc_object_manager bool m_event_walk_ok; // true if can walk events. instance_table_t m_instance_table; // table of instances. sc_module_name* m_module_name_stack; // sc_module_name stack. + std::mutex m_mutex; // Mutex to ensure thread safety. instance_table_t::iterator m_object_it; // object instance iterator. object_vector_t m_object_stack; // sc_object stack. bool m_object_walk_ok; // true if can walk objects. diff --git a/src/sysc/kernel/sc_process.cpp b/src/sysc/kernel/sc_process.cpp index 54d2c646d..f1ccf2788 100644 --- a/src/sysc/kernel/sc_process.cpp +++ b/src/sysc/kernel/sc_process.cpp @@ -47,13 +47,13 @@ namespace sc_core { // Note the special name for 'non_event' - this makes sure it does not // appear as a named event. -std::vector sc_process_handle::empty_event_vector; -std::vector sc_process_handle::empty_object_vector; +thread_local std::vector sc_process_handle::empty_event_vector; +thread_local std::vector sc_process_handle::empty_object_vector; sc_event& sc_process_handle::non_event() { return sc_get_curr_simcontext()->null_event(); } // Last process that was created: -sc_process_b* sc_process_b::m_last_created_process_p = 0; +thread_local sc_process_b* sc_process_b::m_last_created_process_p = 0; //------------------------------------------------------------------------------ //"sc_process_b::add_static_event" diff --git a/src/sysc/kernel/sc_process.h b/src/sysc/kernel/sc_process.h index 87165d2ff..b9be752a2 100644 --- a/src/sysc/kernel/sc_process.h +++ b/src/sysc/kernel/sc_process.h @@ -401,7 +401,7 @@ class SC_API sc_process_b : public sc_object_host { // requesting global suspension. protected: - static sc_process_b* m_last_created_process_p; // Last process created. + thread_local static sc_process_b* m_last_created_process_p; // Last process created. }; diff --git a/src/sysc/kernel/sc_process_handle.h b/src/sysc/kernel/sc_process_handle.h index 4b5657c5c..73bb43210 100644 --- a/src/sysc/kernel/sc_process_handle.h +++ b/src/sysc/kernel/sc_process_handle.h @@ -147,8 +147,8 @@ class SC_API sc_process_handle { sc_process_b* m_target_p; // Target for this object instance. protected: - static std::vector empty_event_vector; // If m_target_p == 0. - static std::vector empty_object_vector; // If m_target_p == 0. + thread_local static std::vector empty_event_vector; // If m_target_p == 0. + thread_local static std::vector empty_object_vector; // If m_target_p == 0. static sc_event& non_event(); // If m_target_p == 0. }; diff --git a/src/sysc/kernel/sc_simcontext.cpp b/src/sysc/kernel/sc_simcontext.cpp index 26d876108..45fdd696d 100644 --- a/src/sysc/kernel/sc_simcontext.cpp +++ b/src/sysc/kernel/sc_simcontext.cpp @@ -305,14 +305,22 @@ sc_simcontext::init() // ALLOCATE VARIOUS MANAGERS AND REGISTRIES: - m_object_manager = new sc_object_manager; + if (sc_curr_simcontext && this!=sc_curr_simcontext) { // Requesting a 'parallel' simcontext + m_object_manager = sc_curr_simcontext->get_object_manager(); // Keep the SAME object manager + m_name_gen = sc_curr_simcontext->m_name_gen; + //dynamic_log_verbosity = sc_curr_simcontext->dynamic_log_verbosity; remember to put this in for SC_LOG! + m_parent_context=sc_curr_simcontext; + } else { + m_object_manager = new sc_object_manager; + m_name_gen = new sc_name_gen; + m_parent_context=nullptr; + } m_module_registry = new sc_module_registry( *this ); m_port_registry = new sc_port_registry( *this ); m_export_registry = new sc_export_registry( *this ); m_prim_channel_registry = new sc_prim_channel_registry( *this ); m_stage_cb_registry = new sc_stage_callback_registry( *this ); m_stub_registry = new sc_stub_registry( *this ); - m_name_gen = new sc_name_gen; m_process_table = new sc_process_table; m_current_writer = 0; @@ -364,6 +372,7 @@ sc_simcontext::init() void sc_simcontext::clean() { + if (m_parent_context) return; // assume the parent will delete us // remove remaining zombie processes do_collect_processes(); @@ -377,13 +386,15 @@ sc_simcontext::clean() delete m_null_event_p; delete m_timed_events; delete m_process_table; - delete m_name_gen; delete m_stage_cb_registry; delete m_prim_channel_registry; delete m_export_registry; delete m_port_registry; delete m_module_registry; - delete m_object_manager; + if (!m_parent_context) { + delete m_name_gen; + delete m_object_manager; + } m_delta_events.clear(); m_child_objects.clear(); @@ -661,7 +672,7 @@ sc_simcontext::elaborate() // (not added to public object hierarchy) m_method_invoker_p = - new sc_invoke_method("$$$$kernel_module$$$$_invoke_method" ); + new sc_invoke_method(("$$$$kernel_module$$$$_invoke_method$" + std::to_string((uint64_t)((void*)this))).c_str()); set_simulation_status(SC_BEFORE_END_OF_ELABORATION); for( int cd = 0; cd != 4; /* empty */ ) @@ -1553,8 +1564,8 @@ void sc_simcontext::post_suspend() const static sc_simcontext sc_default_global_context; sc_simcontext* sc_curr_simcontext = &sc_default_global_context; #else - SC_API sc_simcontext* sc_curr_simcontext = 0; - SC_API sc_simcontext* sc_default_global_context = 0; + thread_local SC_API sc_simcontext* sc_curr_simcontext = 0; + thread_local SC_API sc_simcontext* sc_default_global_context = 0; #endif #else // Not MT-safe! @@ -1695,7 +1706,7 @@ sc_start( const sc_time& duration, sc_starvation_policy p ) exit_time = context_p->m_curr_time + duration; // called with duration = SC_ZERO_TIME for the first time - static bool init_delta_or_pending_updates = + thread_local static bool init_delta_or_pending_updates = ( starting_delta == 0 && exit_time == SC_ZERO_TIME ); // If the simulation status is bad issue the appropriate message: diff --git a/src/sysc/kernel/sc_simcontext.h b/src/sysc/kernel/sc_simcontext.h index 7788281f3..19798273e 100644 --- a/src/sysc/kernel/sc_simcontext.h +++ b/src/sysc/kernel/sc_simcontext.h @@ -447,6 +447,8 @@ class SC_API sc_simcontext int m_suspend; int m_unsuspendable; + sc_simcontext* m_parent_context; + private: // disabled @@ -459,8 +461,8 @@ class SC_API sc_simcontext // Not MT safe. #if 1 -extern SC_API sc_simcontext* sc_curr_simcontext; -extern SC_API sc_simcontext* sc_default_global_context; +extern thread_local SC_API sc_simcontext* sc_curr_simcontext; +extern thread_local SC_API sc_simcontext* sc_default_global_context; inline sc_simcontext* sc_get_curr_simcontext() diff --git a/src/sysc/utils/sc_mempool.cpp b/src/sysc/utils/sc_mempool.cpp index dc0442d1d..b595ad3d9 100644 --- a/src/sysc/utils/sc_mempool.cpp +++ b/src/sysc/utils/sc_mempool.cpp @@ -244,7 +244,7 @@ sc_mempool_int::~sc_mempool_int() delete[] allocators; } -static sc_mempool_int* the_mempool = 0; +thread_local static sc_mempool_int* the_mempool = 0; void* sc_mempool_int::do_allocate(std::size_t sz) diff --git a/src/sysc/utils/sc_ob_event.h b/src/sysc/utils/sc_ob_event.h new file mode 100644 index 000000000..75a0c4812 --- /dev/null +++ b/src/sysc/utils/sc_ob_event.h @@ -0,0 +1,96 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_ob_event.h -- Event that fires only when the simulator arrives at the + notification time, or there are subsequent events + + Original Author: Mark burton + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#ifndef _SC_OB_EVENT_ +#define _SC_OB_EVENT_ + +#include + +namespace sc_core { +class sc_ob_event : public sc_core::sc_module, + public sc_core::sc_event, + public sc_core::sc_stage_callback_if { + +private: + sc_core::sc_time m_scheduled; + sc_core::sc_process_handle m_th; + + void future_events_notify_th() { + while (true) { + m_th.suspend(); + + auto now = sc_core::sc_time_stamp(); + if (now >= m_scheduled) { + sc_core::sc_event::notify(); + } else { + sc_core::sc_event::notify(m_scheduled - now); + } + } + } + +public: + sc_ob_event(const sc_core::sc_module_name &n = + sc_core::sc_gen_unique_name("sc_ob_event")) + : sc_module(n), m_scheduled(sc_core::SC_ZERO_TIME) { + SC_THREAD(future_events_notify_th); + m_th = sc_core::sc_get_current_process_handle(); + } + void notify() { + sc_core::sc_event::notify(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + void notify(double delay, sc_core::sc_time_unit unit) { + notify(sc_core::sc_time(delay, unit)); + } + void notify(sc_core::sc_time delay) { + sc_core::sc_event::cancel(); + m_scheduled = sc_core::sc_time_stamp() + delay; + sc_core::sc_register_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + void cancel() { + sc_core::sc_event::cancel(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + void stage_callback(const sc_core::sc_stage &stage) { + sc_core::sc_time pending = sc_core::sc_time_stamp(); + if (sc_core::sc_pending_activity_at_future_time()) { + pending += sc_core::sc_time_to_pending_activity(); + } + + if (pending >= m_scheduled) { + m_th.resume(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + } + + ~sc_ob_event() {} +}; +} // namespace sc_core + +#endif // _SC_OB_EVENT_ \ No newline at end of file diff --git a/src/sysc/utils/sc_on_context.h b/src/sysc/utils/sc_on_context.h new file mode 100644 index 000000000..911cb83af --- /dev/null +++ b/src/sysc/utils/sc_on_context.h @@ -0,0 +1,276 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_on_context.h -- Job handler to run on context + + Original Author: Mark burton + + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#ifndef SC_ON_CONTEXT_H +#define SC_ON_CONTEXT_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace sc_core { + +class sc_on_context : public sc_core::sc_module +{ +protected: + class AsyncJob + { + public: + using Ptr = std::shared_ptr; + + private: + std::packaged_task m_task; + + bool m_cancelled = false; + + void run_job() { m_task(); } + + public: + AsyncJob(std::function&& job): m_task(job) {} + + AsyncJob(std::function& job): m_task(job) {} + + AsyncJob() = delete; + AsyncJob(const AsyncJob&) = delete; + + void operator()() { run_job(); } + + /** + * @brief Cancel a job + * + * @details Cancel a job by setting m_cancelled to true and by + * resetting the task. Any waiter will then be unblocked immediately. + */ + void cancel() + { + m_cancelled = true; + m_task.reset(); + } + + void wait() + { + auto future = m_task.get_future(); + + while (!m_cancelled && + future.wait_for(std::chrono::milliseconds(500))==std::future_status::timeout) + {} + + if (!m_cancelled) { + future.get(); + } + } + + bool is_cancelled() const { return m_cancelled; } + }; + + + sc_core::sc_simcontext *m_simc=nullptr; + + /* Async job queue */ + std::queue m_async_jobs; + AsyncJob::Ptr m_running_job; + std::mutex m_async_jobs_mutex; + + sc_event m_jobs_handler_event; // NB this event must be threadsafe. + std::atomic running = true; + + // Process inside a thread incase the job calls wait + void jobs_handler() + { + m_simc=sc_core::sc_get_curr_simcontext(); + + std::unique_lock lock(m_async_jobs_mutex); + running = true; + for (; running;) { + while (running && !m_async_jobs.empty()) { + m_running_job = m_async_jobs.front(); + m_async_jobs.pop(); + + lock.unlock(); + sc_core::sc_unsuspendable(); // a wait in the job will cause systemc time to advance + (*m_running_job)(); + sc_core::sc_suspendable(); + lock.lock(); + + m_running_job.reset(); + } + lock.unlock(); + wait(m_jobs_handler_event); + lock.lock(); + } + SC_REPORT_WARNING("sc_on_context", "Stopped"); + sc_core::sc_stop(); + } + + void cancel_pendings_locked() + { + while (!m_async_jobs.empty()) { + m_async_jobs.front()->cancel(); + m_async_jobs.pop(); + } + } + +public: + sc_on_context(const sc_core::sc_module_name& n = sc_core::sc_module_name("sc_on_ctx")) + : sc_module(n) + { + SC_THREAD(jobs_handler); + } + + /** + * @brief Cancel all pending jobs + * + * @detail Cancel all the pending jobs. The callers will be unblocked + * if they are waiting for the job. + */ + void cancel_pendings() + { + std::lock_guard lock(m_async_jobs_mutex); + + cancel_pendings_locked(); + } + + /** + * @brief Cancel all pending and running jobs + * + * @detail Cancel all the pending jobs and the currently running job. + * The callers will be unblocked if they are waiting for the + * job. Note that if the currently running job is resumed, the + * behaviour is undefined. This method is meant to be called + * after simulation has ended. + */ + void cancel_all() + { + std::lock_guard lock(m_async_jobs_mutex); + + cancel_pendings_locked(); + + if (m_running_job) { + m_running_job->cancel(); + m_running_job.reset(); + } + } + void stop() + { + running = false; + m_jobs_handler_event.notify(sc_core::SC_ZERO_TIME); + } + + void end_of_simulation() + { + running = false; + cancel_all(); + } + + void fork(std::function job_entry) { run(job_entry, false); } + + /** + * @brief Run a job on the SystemC kernel thread + * + * @param[in] job_entry The job to run + * @param[in] wait If true, wait for job completion + * + * @return true if the job has been succesfully executed or if `wait` + * was false, false if it has been cancelled (see + * `Sc_on_context::cancel_all`). + */ + bool run(std::function job_entry, bool wait = true) + { + if (!running) return false; + if (is_on_sysc()) { + job_entry(); + return true; + } else { + AsyncJob::Ptr job(new AsyncJob(job_entry)); + + { + std::lock_guard lock(m_async_jobs_mutex); + if (running) { + m_async_jobs.push(job); + } else { + return false; + } + } + + m_jobs_handler_event.notify(); + + if (wait) { + /* Wait for job completion */ + try { + job->wait(); + } catch (std::runtime_error const& e) { + /* Report unknown runtime errors, without causing a futher excetion */ + auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, + sc_core::SC_LOG | sc_core::SC_DISPLAY); + SC_REPORT_ERROR( + "Sc_on_context", + ("Run on systemc received a runtime error from job: " + std::string(e.what())).c_str()); + sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old); + stop(); + return false; + } catch (const std::exception& exc) { + if (sc_core::sc_report_handler::get_count(sc_core::SC_ERROR) == 0) { + /* Report exceptions that were not caused by SC_ERRORS (which have already been reported)*/ + auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, + sc_core::SC_LOG | sc_core::SC_DISPLAY); + SC_REPORT_ERROR( + "Sc_on_context", + ("Run on systemc received an exception from job: " + std::string(exc.what())).c_str()); + sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old); + } + stop(); + return false; + } catch (...) { + auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, + sc_core::SC_LOG | sc_core::SC_DISPLAY); + SC_REPORT_ERROR("Sc_on_context", "Run on systemc received an unknown exception from job"); + sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old); + stop(); + return false; + } + + return !job->is_cancelled(); + } + + return true; + } + } + + /** + * @return Whether we are on the right SystemC context + */ + bool is_on_sysc() const { return sc_core::sc_get_curr_simcontext()==m_simc; } +}; +} +#endif // SC_ON_CONTEXT_H \ No newline at end of file diff --git a/src/sysc/utils/sc_parallel.h b/src/sysc/utils/sc_parallel.h new file mode 100644 index 000000000..23f4369d8 --- /dev/null +++ b/src/sysc/utils/sc_parallel.h @@ -0,0 +1,184 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_parallel.h -- Allow SC_MODULES to be nominated to run in parallel + + Original Author: Mark burton + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#ifndef SC_PARALLEL_H +#define SC_PARALLEL_H + +#include "sc_sync_window.h" +#include + +#include +#include +#include + +namespace sc_core { + +template class sc_parallel; +/** + * @brief Simcontext handler, encapsulates a simcontext + * This is required for the templated class to work, and hence is required in + * the header. It should not be used directly + * @tparam SYNC_POLICY + */ +template +class _internal_simcontext_handler { + template friend class sc_parallel; + /** + * @brief Helper to capture start of simulation and start std::thread + * + */ + class control_module : public sc_core::sc_module, + public sc_core::sc_stage_callback_if { + class _internal_simcontext_handler *m_simcontext_h; + + std::thread m_thread; + std::mutex m_mutex; + std::condition_variable m_cv; + + // Prim channel purely to accept async_request_update, to call sc_stop. + class terminator_pc : public sc_core::sc_prim_channel { + void update() { sc_stop(); } + + public: + // terminator_pc() {} + void terminate() { async_request_update(); } + }; + terminator_pc *m_terminator; + sc_event *m_end_ev; + bool started = false; + void sc_context_start() { + m_simcontext_h->use_simcontext(); + sc_core::sc_register_stage_callback(*this, + sc_core::SC_POST_START_OF_SIMULATION); + sc_core::sc_start(); + m_sync_child->detach(); + } + + public: + void start_of_simulation() { + m_thread = std::thread(&control_module::sc_context_start, this); + std::unique_lock lg(m_mutex); + m_cv.wait(lg, [this]() { return started; }); + } + void end_thread() { sc_core::sc_stop(); } + void end_of_simulation() { m_terminator->terminate(); } + void stage_callback(const sc_core::sc_stage &stage) { + switch (stage) { + // called by the parent thread when during shutdown. + case sc_core::SC_POST_END_OF_SIMULATION: + if (m_thread.get_id() != std::this_thread::get_id()) { + m_thread.join(); + } + break; + // Called by the child thread once it's up and running + case sc_core::SC_POST_START_OF_SIMULATION: { + std::lock_guard lg(m_mutex); + started = true; + m_cv.notify_all(); + break; + } + default: + break; + } + } + + ~control_module() { + delete m_end_ev; + delete m_terminator; + } + + SC_CTOR(control_module, _internal_simcontext_handler *p) + : m_simcontext_h(p) { + m_simcontext_h->use_simcontext(); + SYNC_POLICY sync_policy; + m_sync_child = new sc_sync_windowed("sync_child"); + m_terminator = new terminator_pc(); + + m_simcontext_h->revert_simcontext(); + + sc_core::sc_register_stage_callback(*this, + sc_core::SC_POST_END_OF_SIMULATION); + + m_end_ev = new sc_core::sc_event(); + SC_METHOD(end_thread); + sensitive << *m_end_ev; + dont_initialize(); + } + sc_sync_windowed *m_sync_child; + }; + +private: + sc_core::sc_simcontext *m_old_simcontext = + sc_get_curr_simcontext(); // Side effect create simcontext if we are the + // first module + sc_core::sc_simcontext m_simcontext; + + control_module m_ctrl_module; + sc_sync_windowed m_sync_parent; + + void use_simcontext() { + assert(sc_curr_simcontext != &m_simcontext); + m_old_simcontext = sc_curr_simcontext; + sc_curr_simcontext = &m_simcontext; + } + + void revert_simcontext() { sc_curr_simcontext = m_old_simcontext; } + +public: + _internal_simcontext_handler() + : m_ctrl_module("ctrl_module", this), m_sync_parent("sync_parent") { + SYNC_POLICY sync_policy; + + m_ctrl_module.m_sync_child->bind(&m_sync_parent); + m_sync_parent.bind(m_ctrl_module.m_sync_child); + + use_simcontext(); + } +}; + +/** + * @brief sc_parallel ensures _internal_simcontext_handler is constructed + * first, the simcontext switched, then the actual module constructed, before + * switching back the simcontext + * + * @tparam T + * @tparam SYNC_POLICY + */ +template +class sc_parallel : public _internal_simcontext_handler, public T { +public: + template sc_parallel(A... a) : T(a...) { + _internal_simcontext_handler::revert_simcontext(); + } +}; + +#define SC_PARALLEL(mod, policy) sc_core::sc_parallel + +} // namespace sc_core + +#endif // SC_PARALLEL_H \ No newline at end of file diff --git a/src/sysc/utils/sc_sync_window.h b/src/sysc/utils/sc_sync_window.h new file mode 100644 index 000000000..0bfab66ec --- /dev/null +++ b/src/sysc/utils/sc_sync_window.h @@ -0,0 +1,222 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_sync_window.h -- keeps SystemC within a 'window' of time + + Original Author: Mark burton + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#include +#include +#include +#include + +namespace sc_core { + +/** + * @brief Base class which gives the quantum (dynamically) and specifies if the + * window should stay open indefinitely or detach when there are no more events. + * A SC_ZERO_TIME quantum means sync on every time step + */ +struct sc_sync_policy_base { + virtual sc_core::sc_time quantum() = 0; + virtual bool keep_alive() = 0; +}; +struct sc_sync_policy_tlm_quantum : public sc_sync_policy_base { + sc_core::sc_time quantum() { + return tlm_utils::tlm_quantumkeeper::get_global_quantum(); + } + bool keep_alive() { return true; } +}; +struct sc_sync_policy_in_sync : public sc_sync_policy_base { + sc_core::sc_time quantum() { + return sc_core::sc_pending_activity() + ? sc_core::sc_time_to_pending_activity() + : sc_core::SC_ZERO_TIME; + } + bool keep_alive() { return false; } +}; +/** + * @brief windowed synchronizer, template sc_sync_policy gives the quantum + * (dynamically) and specifies if the window should stay open indefinitely or + * detach when there are no more events. + * + * @tparam sc_sync_policy + */ +template +class sc_sync_windowed : public sc_core::sc_module, + public sc_core::sc_prim_channel { + static_assert(std::is_base_of::value, + "sc_sync_policy must derive from sc_sync_policy_base"); + + sc_core::sc_event m_sweep_ev; + sc_ob_event m_step_ev; + sc_event m_update_ev; + std::mutex m_mutex; + sc_sync_policy policy; + +public: + struct window { + sc_core::sc_time from; + sc_core::sc_time to; + bool operator==(const window &other) const { + return other.to == to && other.from == from; + } + }; + + static inline const struct window zero_window = {sc_core::SC_ZERO_TIME, + sc_core::SC_ZERO_TIME}; + static inline const struct window open_window = {sc_core::SC_ZERO_TIME, + sc_core::sc_max_time()}; + +private: + window m_window; + window m_incomming_window; // used to hold the window values coming in from + // the other side. + + std::function m_other_async_set_window_fn; + + void do_other_async_set_window_fn(window w) { + if (m_other_async_set_window_fn) { + m_other_async_set_window_fn(w); + } + } + + /* Handle suspending/resuming at the end of the window, also inform other side + * if we reach end of window */ + void step_helper() { + auto now = sc_core::sc_time_stamp(); + auto to = m_window.to; + + /* The step helper has to handle both suspend and resume (because of + * SystemC) */ + if (now >= to) { + sc_core::sc_unsuspend_all(); // such that pending activity is valid if + // it's needed below. + + /* We should suspend at this point, and wait for the other side to catch + * up */ + do_other_async_set_window_fn({now, now + policy.quantum()}); + + if (!policy.keep_alive()) + async_attach_suspending(); + sc_core::sc_suspend_all(); + + } else { + /* the only way to get here is if we have a 'new' window from the other + * side. we are here just to unsuspend */ + sc_core::sc_unsuspend_all(); + if (!policy.keep_alive()) + async_detach_suspending(); + //do_other_async_set_window_fn({now, now + policy.quantum()}); + + /* Re-notify event - maybe presumably moved */ + m_step_ev.notify(to - now); + } + } + + /* + * Handle all sweep requests, once we arrive at a sweep point, tell the other + * side. + */ + void sweep_helper() { + auto now = sc_core::sc_time_stamp(); + do_other_async_set_window_fn({now, now + policy.quantum()}); + } + + /* Manage the Sync aync update */ + void update() { + std::lock_guard lg(m_mutex); + // Now we are on our thread, it's safe to update our window. + m_window = m_incomming_window; + auto now = sc_core::sc_time_stamp(); + + if (m_window.from > now) { + m_sweep_ev.notify(m_window.from - now); // Try to move time forward. + } else { + m_sweep_ev.cancel(); // no need to fire event. + } + /* let stepper handle suspend/resume, must time notify */ + m_update_ev.notify(sc_core::SC_ZERO_TIME); +// std::ostringstream s; +// s << "Got Window: " << m_window.from << " - " << m_window.to; +// SC_REPORT_INFO(sc_core::sc_module::name(), s.str().c_str()); + } + +public: + /* API call from other pair, presumably in a different thread. + * The internal window wil be updated atomically. + * + * Input: window - Window to set for sync. Sweep till the 'from' and step to + * the 'to'. + */ + void async_set_window(const window &w) { + /* Only accept updated windows so we dont re-send redundant updates + * safe at this point to compair against m_window as we took the lock + */ + std::lock_guard lg(m_mutex); + m_incomming_window = w; + if (!(w == m_window)) { + async_request_update(); + } + } + void detach() { + async_detach_suspending(); + m_other_async_set_window_fn(open_window); + } + void bind(sc_sync_windowed *other) { + if (m_other_async_set_window_fn) { + SC_REPORT_WARNING( + "sc_sync_window", + "m_other_async_set_window_fn was already registered or other " + "sc_sync_windowed was already bound!"); + } + m_other_async_set_window_fn = std::bind(&sc_sync_windowed::async_set_window, other, std::placeholders::_1); + } + void register_sync_cb(std::function fn) { + if (m_other_async_set_window_fn) { + SC_REPORT_WARNING( + "sc_sync_window", + "m_other_async_set_window_fn was already registered or other " + "sc_sync_windowed was already bound!"); + } + m_other_async_set_window_fn = fn; + } + SC_CTOR(sc_sync_windowed) + : m_window({sc_core::SC_ZERO_TIME, policy.quantum()}) { + + SC_METHOD(sweep_helper); + dont_initialize(); + sensitive << m_sweep_ev; + + SC_METHOD(step_helper); + dont_initialize(); + sensitive << m_step_ev << m_update_ev; + + m_step_ev.notify(policy.quantum()); + + this->sc_core::sc_prim_channel::async_attach_suspending(); + } +}; + +} // namespace sc_core \ No newline at end of file diff --git a/src/tlm_utils/simple_target_socket.h b/src/tlm_utils/simple_target_socket.h index 82198fbb6..e645ec324 100644 --- a/src/tlm_utils/simple_target_socket.h +++ b/src/tlm_utils/simple_target_socket.h @@ -37,6 +37,7 @@ #include #include "tlm_utils/convenience_socket_bases.h" #include "tlm_utils/peq_with_get.h" +#include "sysc/utils/sc_on_context.h" namespace tlm_utils { @@ -174,6 +175,8 @@ class simple_target_socket_b class fw_process : public tlm::tlm_fw_transport_if, public tlm::tlm_mm_interface { + sc_core::sc_simcontext *m_simc=sc_core::sc_get_curr_simcontext(); // Consider where the socket is constructed to be it's owning simcontext. + sc_core::sc_on_context m_on_ctx; public: typedef sync_enum_type (MODULE::*NBTransportPtr)(transaction_type&, phase_type&, @@ -258,6 +261,15 @@ class simple_target_socket_b // forward call sc_assert(m_mod); return (m_mod->*m_nb_transport_ptr)(trans, phase, t); + + if (!m_on_ctx.is_on_sysc()) + { + sync_enum_type tmp; + m_on_ctx.run([this, &tmp, &trans, &phase, &t](){ tmp=(m_mod->*m_nb_transport_ptr)(trans, phase, t);}); + return tmp; + } else { + return (m_mod->*m_nb_transport_ptr)(trans, phase, t); + } } // nb->b conversion @@ -300,7 +312,15 @@ class simple_target_socket_b if (m_b_transport_ptr) { // forward call sc_assert(m_mod); - (m_mod->*m_b_transport_ptr)(trans, t); + if (!m_on_ctx.is_on_sysc()) { + sc_core::sc_time our_time=sc_core::sc_time_stamp(); + sc_core::sc_time their_time; + m_on_ctx.run( + [this, &trans, &t, &our_time, &their_time]() { if (our_time>sc_core::sc_time_stamp()) wait(our_time-sc_core::sc_time_stamp()); (m_mod->*m_b_transport_ptr)(trans, t); their_time=sc_core::sc_time_stamp(); }); + if (their_time>sc_core::sc_time_stamp()) wait(their_time-sc_core::sc_time_stamp()); + } else { + (m_mod->*m_b_transport_ptr)(trans, t); + } return; } diff --git a/tests/systemc/examples/psystemc/psystemc_test.cpp b/tests/systemc/examples/psystemc/psystemc_test.cpp new file mode 100644 index 000000000..5affd3e2f --- /dev/null +++ b/tests/systemc/examples/psystemc/psystemc_test.cpp @@ -0,0 +1,192 @@ +#include +#include + +#include +#include + +#include "sysc/utils/sc_parallel.h" +#include + +std::mutex mutex; + +int do_work() { + double f = 0; + for (double j = 0; j < 100; j++) { + for (double k = 0; k < 100; k++) { + f += (j * k); + } + } + return f; +} +sc_core::sc_time time1 = SC_ZERO_TIME; +sc_core::sc_time time2 = SC_ZERO_TIME; +sc_core::sc_time worst = SC_ZERO_TIME; + +std::mutex tm; +void snaptime(sc_time &time) { + std::lock_guard lg(tm); + time = sc_time_stamp(); + auto d = (time1 > time2) ? time1 - time2 : time2 - time1; + if (d > worst) + worst = d; +} + +SC_MODULE(my_parallel_module) { + sc_event send_txn; + + void t1() { SC_REPORT_INFO(name(), "T1"); } + void t2() { + SC_REPORT_INFO(name(), "T2"); + wait(1, sc_core::SC_NS); + SC_REPORT_INFO(name(), "T2 1ns finished wait"); + } + + void t3() { + SC_REPORT_INFO(name(), "T3"); + for (int i = 0; i < 150000; i++) { + SC_REPORT_INFO(name(), "T3 waiting"); + wait(100, sc_core::SC_MS); + snaptime(time1); + + SC_REPORT_INFO(name(), "T3 do work"); + int f = do_work(); + SC_REPORT_INFO(name(), "Work done"); + + SC_REPORT_INFO(name(), "T3 done waiting"); + } + SC_REPORT_INFO(name(), "T3 finished loop"); + } + + void t4() { + while (1) { + wait(send_txn); + + SC_REPORT_INFO(name(), "Send TXN"); + tlm::tlm_generic_payload trans; + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + + socket->b_transport(trans, delay); + SC_REPORT_INFO(name(), "done TXN"); + } + } + void m1() { + + SC_REPORT_INFO( + name(), + ("Got signal " + std::string(in.read() ? "True" : "False")).c_str()); + + send_txn.notify(10, sc_core::SC_MS); + } + +public: + SC_CTOR(my_parallel_module) { + SC_REPORT_INFO(name(), "Ading tasks t1,t2 and t3\n"); + SC_THREAD(t1); + SC_THREAD(t2); + SC_THREAD(t3); + SC_THREAD(t4); + + SC_METHOD(m1); + sensitive << in; + dont_initialize(); + + SC_REPORT_INFO(name(), "Constructor done"); + } + + void end_of_elaboration() { SC_REPORT_INFO(name(), "end_of_elaboration"); } + void start_of_simulation() { SC_REPORT_INFO(name(), "start_of_simulation"); } + sc_in in; + + tlm_utils::simple_initiator_socket socket; +}; + +SC_MODULE(My_normal_module) { + + tlm_utils::simple_target_socket socket; + + void t1() { SC_REPORT_INFO(name(), "T1"); } + void t2() { + SC_REPORT_INFO(name(), "T2"); + wait(1, sc_core::SC_NS); + + SC_REPORT_INFO(name(), "T2 1ns finished wait"); + } + + void t3() { + SC_REPORT_INFO(name(), "T3"); + for (int i = 0; i < 150000; i++) { + SC_REPORT_INFO(name(), "T3 waiting"); + wait(100, sc_core::SC_MS); + snaptime(time2); + + SC_REPORT_INFO(name(), "T3 do work"); + + int f = do_work(); + SC_REPORT_INFO(name(), "Work done"); + + SC_REPORT_INFO( + name(), ("Sending signal" + std::string((i & 0x1) ? "True" : "False")) + .c_str()); + // out.write(i & 0x1); + SC_REPORT_INFO(name(), "T3 done waiting"); + } + SC_REPORT_INFO(name(), "T3 finished loop"); + sc_stop(); + } + void b_transport(tlm::tlm_generic_payload & trans, sc_core::sc_time & delay) { + SC_REPORT_INFO(name(), "Got b_transport"); + } + SC_CTOR(My_normal_module) { + SC_THREAD(t1); + SC_THREAD(t2); + SC_THREAD(t3); + socket.register_b_transport(this, &My_normal_module::b_transport); + } + sc_out out; + + void end_of_elaboration() { SC_REPORT_INFO(name(), "end_of_elaboration"); } + void start_of_simulation() { SC_REPORT_INFO(name(), "start_of_simulation"); } +}; + +void report_handler(const sc_core::sc_report &rep, + const sc_core::sc_actions &actions) { +#if DEBUG + std::lock_guard lock(mutex); + auto now = std::chrono::high_resolution_clock::now(); + auto now_ms = std::chrono::duration_cast( + now.time_since_epoch()) + .count(); + + cout << now_ms % 10000 << " thread:" << std::this_thread::get_id() + << " simcontext:" << sc_core::sc_get_curr_simcontext() + << " time:" << sc_core::sc_time_stamp() << " " + << " : [" << rep.get_msg_type() << "] " << rep.get_msg() << std::endl; +#endif +} + +int sc_main(int argc, char *argv[]) { + + ::sc_core::sc_report_handler::set_verbosity_level(sc_core::SC_DEBUG); + ::sc_core::sc_report_handler::set_handler(report_handler); + + tlm_utils::tlm_quantumkeeper::set_global_quantum( + sc_core::sc_time(200, sc_core::SC_MS)); + + My_normal_module mn("Normal"); + //my_parallel_module mp("Parallel"); + SC_PARALLEL(my_parallel_module, sc_core::sc_sync_policy_tlm_quantum) mp("Parallel"); + //SC_PARALLEL(my_parallel_module, sc_core::sc_sync_policy_in_sync) mp("Parallel"); + + sc_signal sig; + mn.out(sig); + mp.in(sig); + + mp.socket.bind(mn.socket); + + SC_REPORT_INFO("main", "before start"); + sc_start(); + std::cout << "Worst diff: " << worst << "\n"; + SC_REPORT_INFO("main", "finished"); + + return 0; +} \ No newline at end of file