From a6bc02f46f01ffd4b758c8ae0ffcf31db0f34590 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Thu, 15 May 2025 11:07:59 +0200 Subject: [PATCH 1/6] Make simcontext thread local, handle sub-simcontext construction in simcontext::init Signed-off-by: Mark Burton --- src/sysc/kernel/sc_process.cpp | 6 +++--- src/sysc/kernel/sc_process.h | 2 +- src/sysc/kernel/sc_process_handle.h | 4 ++-- src/sysc/kernel/sc_simcontext.cpp | 18 +++++++++++++----- src/sysc/kernel/sc_simcontext.h | 6 ++++-- 5 files changed, 23 insertions(+), 13 deletions(-) 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..6a29b94c4 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; @@ -661,7 +669,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 +1561,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! 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() From 1e12567e3cb0dba374777228500754b0ff6f0634 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Mon, 12 May 2025 11:02:51 +0200 Subject: [PATCH 2/6] Make sc_event thread safe Signed-off-by: Mark Burton --- src/sysc/communication/sc_prim_channel.cpp | 38 +++++++++---- src/sysc/communication/sc_prim_channel.h | 3 ++ src/sysc/kernel/sc_event.cpp | 17 ++++++ src/sysc/kernel/sc_event.h | 63 ++++++++++++++++++++++ 4 files changed, 110 insertions(+), 11 deletions(-) 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..d0d7d4b6a 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(SC_ZERO_TIME); + return; + } // immediate notification if( !m_simc->evaluation_phase() ) // coming from @@ -103,6 +112,11 @@ sc_event::notify() void sc_event::notify( const sc_time& t ) { + if (m_simc != sc_get_curr_simcontext()) { + std::cout << name()<<"HERE: ("< #if defined(_MSC_VER) && !defined(SC_WIN_DLL_WARN) #pragma warning(push) @@ -273,6 +275,56 @@ 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 valid=false; + bool cancel=false; + sc_time time; + + void update(void) { + mutex.lock(); + if (valid) { + if (cancel) { + valid=false; + mutex.unlock(); + event->cancel(); + } else { + valid=false; + auto t=time; + mutex.unlock(); + event->cancel(); + event->notify(t); + } + } else { + mutex.unlock(); + } + } + + public: + void pending_notify(sc_time t) { + mutex.lock(); + time=t; + valid=true; + mutex.unlock(); + async_request_update(); + } + void pending_cancel() { + mutex.lock(); + valid=true; + cancel=true; + mutex.unlock(); + async_request_update(); + } + + pending_helper(sc_event *e) : sc_prim_channel(true) { + event=e; + } + + }; + friend class pending_helper; public: sc_event(); @@ -354,6 +406,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 +479,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 +500,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 ); } From a14a3d75bbfe1263003066f9fe51dddfe91ab24e Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Thu, 15 May 2025 09:28:11 +0200 Subject: [PATCH 3/6] Add helper to run on specific context, and use to make b_transport and nb_transport thread safe, other tlm APIs are NOT handled Signed-off-by: Mark Burton --- src/sysc/utils/sc_on_context.h | 269 +++++++++++++++++++++++++++ src/tlm_utils/simple_target_socket.h | 19 +- 2 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 src/sysc/utils/sc_on_context.h diff --git a/src/sysc/utils/sc_on_context.h b/src/sysc/utils/sc_on_context.h new file mode 100644 index 000000000..13c74796f --- /dev/null +++ b/src/sysc/utils/sc_on_context.h @@ -0,0 +1,269 @@ +/***************************************************************************** + + 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 + +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(); + + future.wait(); + + 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 (!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(); + } + + 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); + m_async_jobs.push(job); + } + + 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/tlm_utils/simple_target_socket.h b/src/tlm_utils/simple_target_socket.h index 82198fbb6..68fb1c44f 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,12 @@ 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()) { + m_on_ctx.run( + [this, &trans, &t]() { (m_mod->*m_b_transport_ptr)(trans, t); }); + } else { + (m_mod->*m_b_transport_ptr)(trans, t); + } return; } From c2d4c18aa115ee826695803562eeea7fbccd9453 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Thu, 15 May 2025 18:02:54 +0200 Subject: [PATCH 4/6] Add sc_ob_event, sc_sync_window, sc_parallel and a test case Signed-off-by: Mark Burton --- src/sysc/utils/sc_ob_event.h | 117 ++++++++++ src/sysc/utils/sc_parallel.h | 129 +++++++++++ src/sysc/utils/sc_sync_window.h | 219 ++++++++++++++++++ .../examples/psystemc/psystemc_test.cpp | 159 +++++++++++++ 4 files changed, 624 insertions(+) create mode 100644 src/sysc/utils/sc_ob_event.h create mode 100644 src/sysc/utils/sc_parallel.h create mode 100644 src/sysc/utils/sc_sync_window.h create mode 100644 tests/systemc/examples/psystemc/psystemc_test.cpp diff --git a/src/sysc/utils/sc_ob_event.h b/src/sysc/utils/sc_ob_event.h new file mode 100644 index 000000000..f09a2af56 --- /dev/null +++ b/src/sysc/utils/sc_ob_event.h @@ -0,0 +1,117 @@ +/***************************************************************************** + + 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 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 +#include + +#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_event m_update; + sc_core::sc_time m_scheduled; + sc_core::sc_process_handle m_th; + + void update_m() + { + auto now = sc_core::sc_time_stamp(); + if (now >= m_scheduled) { + sc_core::sc_event::notify(); + } else { + sc_core::sc_time pending = now; + if (sc_core::sc_pending_activity_at_future_time()) { + pending = now + sc_core::sc_time_to_pending_activity(); + } + if (pending >= m_scheduled) { + sc_core::sc_event::notify(m_scheduled - now); + } else { + if (pending > now) { + m_update.notify(pending - now); + } else { + sc_core::sc_register_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + } + } + } + + void future_events_notify_th() + { + while (true) { + m_th.suspend(); + update_m(); + } + } + +public: + sc_ob_event(const sc_core::sc_module_name& n = sc_core::sc_gen_unique_name("sc_ob_event")) + : sc_module(n) + , sc_core::sc_event((std::string(n) + "_notifier_event").c_str()) + , m_update((std::string(n) + "_update_event").c_str()) + , m_scheduled(sc_core::SC_ZERO_TIME) + { + SC_METHOD(update_m); + sensitive << m_update; + dont_initialize(); + SC_THREAD(future_events_notify_th); + m_th = sc_core::sc_get_current_process_handle(); + } + void notify() + { + m_scheduled = sc_core::sc_time_stamp(); + m_update.notify(sc_core::SC_ZERO_TIME); + } + void notify(double delay, sc_core::sc_time_unit unit) + { + m_scheduled = sc_core::sc_time_stamp() + sc_core::sc_time(delay, unit); + m_update.notify(sc_core::SC_ZERO_TIME); + } + void notify(sc_core::sc_time delay) + { + m_scheduled = sc_core::sc_time_stamp() + delay; + m_update.notify(sc_core::SC_ZERO_TIME); + } + + void stage_callback(const sc_core::sc_stage& stage) + { + if (sc_core::sc_pending_activity_at_future_time()) { + m_th.resume(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + } + + ~sc_ob_event() {} +}; +} // namespace + +#endif // _SC_OB_EVENT_ \ 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..2d3e773b9 --- /dev/null +++ b/src/sysc/utils/sc_parallel.h @@ -0,0 +1,129 @@ +/***************************************************************************** + + 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 + *****************************************************************************/ +#include +#include "sc_sync_window.h" + +#include + +namespace sc_core { + + +/** + * @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 +{ + + /** + * @brief Helper to capture start of simulation and start std::thread + * + */ + SC_MODULE (control_module) { + class _internal_simcontext_handler* m_simcontext_h; + + std::thread m_thread; + void sc_context_start() + { + m_simcontext_h->use_simcontext(); + + sc_core::sc_start(); + } + + public: + void start_of_simulation() { m_thread = std::thread(&control_module::sc_context_start, this); } + void end_of_simulation() + { + m_thread.join(); + } + + 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_simcontext_h->reset_simcontext(); + } + sc_sync_windowed* m_sync_child; + }; + + sc_core::sc_simcontext m_simcontext; + sc_core::sc_simcontext* m_old_simcontext; + + control_module m_ctrl_module; + sc_sync_windowed* m_sync_parent; + +public: + void use_simcontext() + { + assert(sc_curr_simcontext != &m_simcontext); + m_old_simcontext = sc_curr_simcontext; + sc_curr_simcontext = &m_simcontext; + } + + void reset_simcontext() + { + sc_curr_simcontext = m_old_simcontext; + } + + _internal_simcontext_handler(): m_ctrl_module("ctrl_module", this) + { + SYNC_POLICY sync_policy; + m_sync_parent = new sc_sync_windowed("sync_parent"); + + 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::reset_simcontext(); + } +}; + +#define SC_PARALLEL(mod, policy) sc_core::sc_parallel + +} // namespace sc_core \ 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..dd178e945 --- /dev/null +++ b/src/sysc/utils/sc_sync_window.h @@ -0,0 +1,219 @@ + +/***************************************************************************** + + 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_sc_zero_time : sc_sync_policy_base { + sc_core::sc_time quantum() { return SC_ZERO_TIME; } + bool keep_alive() { return false; } +}; +struct sc_sync_policy_tlm_quantum : sc_sync_policy_base { + sc_core::sc_time quantum() { + return tlm_utils::tlm_quantumkeeper::get_global_quantum(); + } + bool keep_alive() { return true; } +}; + +/** + * @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; + std::mutex m_mutex; + sc_sync_policy policy; + +public: + struct window { + sc_core::sc_time from; + sc_core::sc_time to; + bool operator==(window other) { + return other.to == to && other.from == from; + } + }; + + const struct window zero_window = {sc_core::SC_ZERO_TIME, + sc_core::SC_ZERO_TIME}; + const struct window open_window = {sc_core::SC_ZERO_TIME, + sc_core::sc_max_time()}; + +private: + window m_window; + std::function m_other_async_set_window_fn; + + void do_other_async_set_window_fn(const window &p_w) { + if (m_other_async_set_window_fn) { + m_other_async_set_window_fn(p_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; + + 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 */ + auto q = (policy.quantum() == sc_core::SC_ZERO_TIME) + ? sc_core::sc_time_to_pending_activity() + : policy.quantum(); + + do_other_async_set_window_fn({now, now + q}); + + if (!policy.keep_alive()) + async_attach_suspending(); + sc_core::sc_suspend_all(); + + } else { + sc_core::sc_unsuspend_all(); + if (!policy.keep_alive()) + async_detach_suspending(); + + auto q = (policy.quantum() == sc_core::SC_ZERO_TIME) + ? sc_core::sc_time_to_pending_activity() + : policy.quantum(); + do_other_async_set_window_fn({now, now + q}); + /* Re-notify event - maybe presumably moved */ + m_step_ev.notify(std::min(q, to - now)); + } + } + + /* + * Handle all sweep requests, once we arrive at a sweep point, tell the other + * side. + */ + virtual void sweep_helper() { + auto now = sc_core::sc_time_stamp(); + + auto q = (policy.quantum() == sc_core::SC_ZERO_TIME) + ? sc_core::sc_time_to_pending_activity() + : policy.quantum(); + do_other_async_set_window_fn({now, now + q}); + } + + /* Manage the Sync aync update */ + void update() { + std::lock_guard lg(m_mutex); + 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 */ + m_step_ev.notify(sc_core::SC_ZERO_TIME); + if (m_window == open_window) { + /* The other side is signalling that they have no more events */ + sc_stop(); + } + } + +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(window w) { + /* Only accept updated windows so we dont re-send redundant updates */ + std::lock_guard lg(m_mutex); + if (!(w == m_window)) { + m_window = w; + async_request_update(); + } + } + + void end_of_simulation() { do_other_async_set_window_fn(open_window); } + + void start_of_simulation() { m_step_ev.notify(sc_core::SC_ZERO_TIME); } + 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 = [other](const window &w) { + other->async_set_window(w); + }; + } + 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; + + this->sc_core::sc_prim_channel::async_attach_suspending(); + } +}; + +} // namespace sc_core \ No newline at end of file diff --git a/tests/systemc/examples/psystemc/psystemc_test.cpp b/tests/systemc/examples/psystemc/psystemc_test.cpp new file mode 100644 index 000000000..1c9a12240 --- /dev/null +++ b/tests/systemc/examples/psystemc/psystemc_test.cpp @@ -0,0 +1,159 @@ +#include + +#include +#include + +#include "sysc/utils/sc_parallel.h" + +std::mutex mutex; + +int do_work() { + int f = 0; + for (int j = 0; j < 50000; j++) { + for (int k = 0; k < 10000; k++) { + f += (j * k); + } + } + return f; +} + +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 < 5; i++) { + SC_REPORT_INFO(name(), "T3 waiting"); + wait(100, sc_core::SC_MS); + + 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 m2() { + 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_METHOD(m1); + sensitive << in; + dont_initialize(); + + SC_METHOD(m2); + sensitive << send_txn; + dont_initialize(); + SC_REPORT_INFO(name(), "Constructor done"); + } + + 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 < 15; i++) { + SC_REPORT_INFO(name(), "T3 waiting"); + wait(50, sc_core::SC_MS); + 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 report_handler(const sc_core::sc_report &rep, + const sc_core::sc_actions &actions) { + std::lock_guard lock(mutex); + cout << "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; +} + +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_ZERO_TIME); + // tlm_utils::tlm_quantumkeeper::set_global_quantum( + // sc_core::sc_time(100, sc_core::SC_MS)); + + SC_PARALLEL(my_parallel_module, sc_core::sc_sync_policy_tlm_quantum) + mp("Parallel"); + My_normal_module mn("Normal"); + sc_signal sig; + mn.out(sig); + mp.in(sig); + + mp.socket.bind(mn.socket); + + SC_REPORT_INFO("main", "before start"); + sc_start(); + SC_REPORT_INFO("main", "finished"); + + return 0; +} \ No newline at end of file From 727e66f81be6cd2bdb911b6e9f29636a6477079e Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Wed, 21 May 2025 17:29:03 +0200 Subject: [PATCH 5/6] Additional thead safety and cleanups Signed-off-by: Mark Burton --- src/sysc/kernel/sc_event.cpp | 14 +- src/sysc/kernel/sc_event.h | 22 +- src/sysc/kernel/sc_name_gen.cpp | 2 + src/sysc/kernel/sc_name_gen.h | 3 +- src/sysc/kernel/sc_object_manager.cpp | 16 +- src/sysc/kernel/sc_object_manager.h | 2 + src/sysc/kernel/sc_simcontext.cpp | 9 +- src/sysc/utils/sc_mempool.cpp | 2 +- src/sysc/utils/sc_ob_event.h | 124 +++++------- src/sysc/utils/sc_on_context.h | 17 +- src/sysc/utils/sc_parallel.h | 189 +++++++++++------- src/sysc/utils/sc_sync_window.h | 90 +++++---- .../examples/psystemc/psystemc_test.cpp | 25 ++- 13 files changed, 308 insertions(+), 207 deletions(-) diff --git a/src/sysc/kernel/sc_event.cpp b/src/sysc/kernel/sc_event.cpp index d0d7d4b6a..0a6314eec 100644 --- a/src/sysc/kernel/sc_event.cpp +++ b/src/sysc/kernel/sc_event.cpp @@ -92,7 +92,7 @@ void sc_event::notify() { if (m_simc != sc_get_curr_simcontext()) { - m_async_helper.pending_notify(SC_ZERO_TIME); + m_async_helper.pending_notify(); return; } // immediate notification @@ -113,7 +113,6 @@ void sc_event::notify( const sc_time& t ) { if (m_simc != sc_get_curr_simcontext()) { - std::cout << name()<<"HERE: ("<cancel(); - event->notify(t); + if (t_valid) { + event->notify(t); + } else { + event->notify(); + } } } else { mutex.unlock(); @@ -305,17 +310,22 @@ class SC_API sc_event public: void pending_notify(sc_time t) { - mutex.lock(); + std::lock_guard lg(mutex); time=t; + timed=true; + valid=true; + async_request_update(); + } + void pending_notify() { + std::lock_guard lg(mutex); + timed=false; valid=true; - mutex.unlock(); async_request_update(); } void pending_cancel() { - mutex.lock(); + std::lock_guard lg(mutex); valid=true; cancel=true; - mutex.unlock(); async_request_update(); } 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_simcontext.cpp b/src/sysc/kernel/sc_simcontext.cpp index 6a29b94c4..45fdd696d 100644 --- a/src/sysc/kernel/sc_simcontext.cpp +++ b/src/sysc/kernel/sc_simcontext.cpp @@ -372,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(); @@ -385,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(); @@ -1703,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/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 index f09a2af56..4bc09984b 100644 --- a/src/sysc/utils/sc_ob_event.h +++ b/src/sysc/utils/sc_ob_event.h @@ -35,83 +35,63 @@ #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 -{ +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_event m_update; - sc_core::sc_time m_scheduled; - sc_core::sc_process_handle m_th; - - void update_m() - { - auto now = sc_core::sc_time_stamp(); - if (now >= m_scheduled) { - sc_core::sc_event::notify(); - } else { - sc_core::sc_time pending = now; - if (sc_core::sc_pending_activity_at_future_time()) { - pending = now + sc_core::sc_time_to_pending_activity(); - } - if (pending >= m_scheduled) { - sc_core::sc_event::notify(m_scheduled - now); - } else { - if (pending > now) { - m_update.notify(pending - now); - } else { - sc_core::sc_register_stage_callback(*this, sc_core::SC_POST_UPDATE); - } - } - } - } - - void future_events_notify_th() - { - while (true) { - m_th.suspend(); - update_m(); - } + 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) - , sc_core::sc_event((std::string(n) + "_notifier_event").c_str()) - , m_update((std::string(n) + "_update_event").c_str()) - , m_scheduled(sc_core::SC_ZERO_TIME) - { - SC_METHOD(update_m); - sensitive << m_update; - dont_initialize(); - SC_THREAD(future_events_notify_th); - m_th = sc_core::sc_get_current_process_handle(); - } - void notify() - { - m_scheduled = sc_core::sc_time_stamp(); - m_update.notify(sc_core::SC_ZERO_TIME); - } - void notify(double delay, sc_core::sc_time_unit unit) - { - m_scheduled = sc_core::sc_time_stamp() + sc_core::sc_time(delay, unit); - m_update.notify(sc_core::SC_ZERO_TIME); - } - void notify(sc_core::sc_time delay) - { - m_scheduled = sc_core::sc_time_stamp() + delay; - m_update.notify(sc_core::SC_ZERO_TIME); - } - - void stage_callback(const sc_core::sc_stage& stage) - { - if (sc_core::sc_pending_activity_at_future_time()) { - m_th.resume(); - sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); - } - } - - ~sc_ob_event() {} + 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 stage_callback(const sc_core::sc_stage &stage) { +// if (sc_core::sc_pending_activity()) { // should the event fire is there is pending activity, or if we arrive at the time? + 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 +} // 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 index 13c74796f..b4443c0b3 100644 --- a/src/sysc/utils/sc_on_context.h +++ b/src/sysc/utils/sc_on_context.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -81,7 +82,9 @@ class sc_on_context : public sc_core::sc_module { auto future = m_task.get_future(); - future.wait(); + while (!m_cancelled && + future.wait_for(std::chrono::milliseconds(500))==std::future_status::timeout) + {} if (!m_cancelled) { future.get(); @@ -110,7 +113,7 @@ class sc_on_context : public sc_core::sc_module std::unique_lock lock(m_async_jobs_mutex); running = true; for (; running;) { - while (!m_async_jobs.empty()) { + while (running && !m_async_jobs.empty()) { m_running_job = m_async_jobs.front(); m_async_jobs.pop(); @@ -181,7 +184,7 @@ class sc_on_context : public sc_core::sc_module void stop() { running = false; - m_jobs_handler_event.notify(); + m_jobs_handler_event.notify(sc_core::SC_ZERO_TIME); } void end_of_simulation() @@ -213,10 +216,14 @@ class sc_on_context : public sc_core::sc_module { std::lock_guard lock(m_async_jobs_mutex); - m_async_jobs.push(job); + if (running) { + m_async_jobs.push(job); + } else { + return false; + } } - m_jobs_handler_event.notify(); + m_jobs_handler_event.notify(sc_core::SC_ZERO_TIME); if (wait) { /* Wait for job completion */ diff --git a/src/sysc/utils/sc_parallel.h b/src/sysc/utils/sc_parallel.h index 2d3e773b9..b6c897701 100644 --- a/src/sysc/utils/sc_parallel.h +++ b/src/sysc/utils/sc_parallel.h @@ -25,105 +25,158 @@ Unless required by applicable CHANGE LOG AT END OF FILE *****************************************************************************/ -#include + +#ifndef SC_PARALLEL_H +#define SC_PARALLEL_H + #include "sc_sync_window.h" +#include +#include +#include #include namespace sc_core { - /** * @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 + * 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 -{ - - /** - * @brief Helper to capture start of simulation and start std::thread - * - */ - SC_MODULE (control_module) { - class _internal_simcontext_handler* m_simcontext_h; - - std::thread m_thread; - void sc_context_start() - { - m_simcontext_h->use_simcontext(); - - sc_core::sc_start(); - } +template +class _internal_simcontext_handler { - public: - void start_of_simulation() { m_thread = std::thread(&control_module::sc_context_start, this); } - void end_of_simulation() - { - m_thread.join(); - } + /** + * @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; - 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_simcontext_h->reset_simcontext(); - } - sc_sync_windowed* m_sync_child; - }; + std::thread m_thread; + std::mutex m_mutex; + std::condition_variable m_cv; - sc_core::sc_simcontext m_simcontext; - sc_core::sc_simcontext* m_old_simcontext; + // 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(); } - control_module m_ctrl_module; - sc_sync_windowed* m_sync_parent; + 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 use_simcontext() - { - assert(sc_curr_simcontext != &m_simcontext); - m_old_simcontext = sc_curr_simcontext; - sc_curr_simcontext = &m_simcontext; + 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; + } } - void reset_simcontext() - { - sc_curr_simcontext = m_old_simcontext; + ~control_module() { + delete m_end_ev; + delete m_terminator; } - _internal_simcontext_handler(): m_ctrl_module("ctrl_module", this) - { - SYNC_POLICY sync_policy; - m_sync_parent = new sc_sync_windowed("sync_parent"); + 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_ctrl_module.m_sync_child->bind(m_sync_parent); - m_sync_parent->bind(m_ctrl_module.m_sync_child); + m_simcontext_h->revert_simcontext(); - use_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; + }; + + 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; + +public: + 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; } + + _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 + * @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 -{ +template +class sc_parallel : public _internal_simcontext_handler, public T { public: - template - sc_parallel(A... a): T(a...) - { - _internal_simcontext_handler::reset_simcontext(); - } + template sc_parallel(A... a) : T(a...) { + _internal_simcontext_handler::revert_simcontext(); + } }; #define SC_PARALLEL(mod, policy) sc_core::sc_parallel -} // namespace sc_core \ No newline at end of file +} // 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 index dd178e945..8f429adbd 100644 --- a/src/sysc/utils/sc_sync_window.h +++ b/src/sysc/utils/sc_sync_window.h @@ -1,4 +1,3 @@ - /***************************************************************************** Licensed to Accellera Systems Initiative Inc. (Accellera) under one or @@ -43,17 +42,20 @@ struct sc_sync_policy_base { virtual sc_core::sc_time quantum() = 0; virtual bool keep_alive() = 0; }; -struct sc_sync_policy_sc_zero_time : sc_sync_policy_base { - sc_core::sc_time quantum() { return SC_ZERO_TIME; } - bool keep_alive() { return false; } -}; -struct sc_sync_policy_tlm_quantum : sc_sync_policy_base { +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 @@ -61,7 +63,7 @@ struct sc_sync_policy_tlm_quantum : sc_sync_policy_base { * * @tparam sc_sync_policy */ -template +template class sc_sync_windowed : public sc_core::sc_module, public sc_core::sc_prim_channel { static_assert(std::is_base_of::value, @@ -69,6 +71,7 @@ class sc_sync_windowed : public sc_core::sc_module, 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; @@ -88,11 +91,15 @@ class sc_sync_windowed : public sc_core::sc_module, 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(const window &p_w) { + void do_other_async_set_window_fn(window w) { if (m_other_async_set_window_fn) { - m_other_async_set_window_fn(p_w); + auto now = sc_core::sc_time_stamp(); + m_other_async_set_window_fn(w); } } @@ -108,11 +115,7 @@ class sc_sync_windowed : public sc_core::sc_module, /* We should suspend at this point, and wait for the other side to catch * up */ - auto q = (policy.quantum() == sc_core::SC_ZERO_TIME) - ? sc_core::sc_time_to_pending_activity() - : policy.quantum(); - - do_other_async_set_window_fn({now, now + q}); + do_other_async_set_window_fn({now, now + policy.quantum()}); if (!policy.keep_alive()) async_attach_suspending(); @@ -123,12 +126,16 @@ class sc_sync_windowed : public sc_core::sc_module, if (!policy.keep_alive()) async_detach_suspending(); - auto q = (policy.quantum() == sc_core::SC_ZERO_TIME) - ? sc_core::sc_time_to_pending_activity() - : policy.quantum(); - do_other_async_set_window_fn({now, now + q}); + // We are about to advance to the next event, so may as well set that as + // the window now + do_other_async_set_window_fn( + {now + (sc_core::sc_pending_activity() + ? sc_core::sc_time_to_pending_activity() + : sc_core::SC_ZERO_TIME), + now + policy.quantum()}); + /* Re-notify event - maybe presumably moved */ - m_step_ev.notify(std::min(q, to - now)); + m_step_ev.notify(to - now); } } @@ -136,18 +143,16 @@ class sc_sync_windowed : public sc_core::sc_module, * Handle all sweep requests, once we arrive at a sweep point, tell the other * side. */ - virtual void sweep_helper() { + void sweep_helper() { auto now = sc_core::sc_time_stamp(); - - auto q = (policy.quantum() == sc_core::SC_ZERO_TIME) - ? sc_core::sc_time_to_pending_activity() - : policy.quantum(); - do_other_async_set_window_fn({now, now + q}); + 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) { @@ -155,13 +160,8 @@ class sc_sync_windowed : public sc_core::sc_module, } else { m_sweep_ev.cancel(); // no need to fire event. } - - /* let stepper handle suspend/resume */ - m_step_ev.notify(sc_core::SC_ZERO_TIME); - if (m_window == open_window) { - /* The other side is signalling that they have no more events */ - sc_stop(); - } + /* let stepper handle suspend/resume, must time notify */ + m_update_ev.notify(sc_core::SC_ZERO_TIME); } public: @@ -172,20 +172,23 @@ class sc_sync_windowed : public sc_core::sc_module, * the 'to'. */ void async_set_window(window w) { - /* Only accept updated windows so we dont re-send redundant updates */ + /* 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)) { - m_window = w; async_request_update(); } } - - void end_of_simulation() { do_other_async_set_window_fn(open_window); } - - void start_of_simulation() { m_step_ev.notify(sc_core::SC_ZERO_TIME); } + 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", + SC_REPORT_WARNING( + "sc_sync_window", "m_other_async_set_window_fn was already registered or other " "sc_sync_windowed was already bound!"); } @@ -195,7 +198,8 @@ class sc_sync_windowed : public sc_core::sc_module, } void register_sync_cb(std::function fn) { if (m_other_async_set_window_fn) { - SC_REPORT_WARNING("sc_sync_window", + SC_REPORT_WARNING( + "sc_sync_window", "m_other_async_set_window_fn was already registered or other " "sc_sync_windowed was already bound!"); } @@ -210,7 +214,9 @@ class sc_sync_windowed : public sc_core::sc_module, SC_METHOD(step_helper); dont_initialize(); - sensitive << m_step_ev; + sensitive << m_step_ev << m_update_ev; + + m_step_ev.notify(sc_core::SC_ZERO_TIME); this->sc_core::sc_prim_channel::async_attach_suspending(); } diff --git a/tests/systemc/examples/psystemc/psystemc_test.cpp b/tests/systemc/examples/psystemc/psystemc_test.cpp index 1c9a12240..a6e505270 100644 --- a/tests/systemc/examples/psystemc/psystemc_test.cpp +++ b/tests/systemc/examples/psystemc/psystemc_test.cpp @@ -4,10 +4,12 @@ #include #include "sysc/utils/sc_parallel.h" +#include std::mutex mutex; int do_work() { + return 1; int f = 0; for (int j = 0; j < 50000; j++) { for (int k = 0; k < 10000; k++) { @@ -76,6 +78,12 @@ SC_MODULE(my_parallel_module) { 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; @@ -122,6 +130,13 @@ SC_MODULE(My_normal_module) { 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, @@ -138,13 +153,13 @@ 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_ZERO_TIME); - // tlm_utils::tlm_quantumkeeper::set_global_quantum( - // sc_core::sc_time(100, sc_core::SC_MS)); + tlm_utils::tlm_quantumkeeper::set_global_quantum(sc_core::sc_time(100, sc_core::SC_MS)); - SC_PARALLEL(my_parallel_module, sc_core::sc_sync_policy_tlm_quantum) - mp("Parallel"); 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); From c60e62c3d358f6472692f1b9f3ebd9eaaf205086 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Mon, 26 May 2025 15:45:45 +0200 Subject: [PATCH 6/6] Syntactic cleanups Signed-off-by: Mark Burton --- src/sysc/kernel/sc_event.h | 90 ++++++++----------- src/sysc/utils/sc_ob_event.h | 29 +++--- src/sysc/utils/sc_on_context.h | 2 +- src/sysc/utils/sc_parallel.h | 6 +- src/sysc/utils/sc_sync_window.h | 31 +++---- src/tlm_utils/simple_target_socket.h | 5 +- .../examples/psystemc/psystemc_test.cpp | 80 ++++++++++------- 7 files changed, 121 insertions(+), 122 deletions(-) diff --git a/src/sysc/kernel/sc_event.h b/src/sysc/kernel/sc_event.h index fada1b1fe..84094e2a4 100644 --- a/src/sysc/kernel/sc_event.h +++ b/src/sysc/kernel/sc_event.h @@ -275,64 +275,44 @@ 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 valid=false; - bool cancel=false; - bool timed=false; - sc_time time; - - void update(void) { - mutex.lock(); - if (valid) { - if (cancel) { - valid=false; - mutex.unlock(); - event->cancel(); - } else { - valid=false; - auto t=time; - auto t_valid=timed; - mutex.unlock(); - if (t_valid) { - event->notify(t); - } else { - event->notify(); - } - } + 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 { - mutex.unlock(); + event->notify(time-sc_time_stamp()); } } - - public: - void pending_notify(sc_time t) { - std::lock_guard lg(mutex); - time=t; - timed=true; - valid=true; - async_request_update(); - } - void pending_notify() { - std::lock_guard lg(mutex); - timed=false; - valid=true; - async_request_update(); - } - void pending_cancel() { - std::lock_guard lg(mutex); - valid=true; - cancel=true; - async_request_update(); - } - - pending_helper(sc_event *e) : sc_prim_channel(true) { - event=e; - } - + } + + 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: diff --git a/src/sysc/utils/sc_ob_event.h b/src/sysc/utils/sc_ob_event.h index 4bc09984b..75a0c4812 100644 --- a/src/sysc/utils/sc_ob_event.h +++ b/src/sysc/utils/sc_ob_event.h @@ -16,7 +16,8 @@ /***************************************************************************** - sc_ob_event.h -- Event that fires only when there are subsequent events + 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 @@ -30,9 +31,6 @@ #define _SC_OB_EVENT_ #include -#include - -#include namespace sc_core { class sc_ob_event : public sc_core::sc_module, @@ -75,19 +73,20 @@ class sc_ob_event : public sc_core::sc_module, 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) { -// if (sc_core::sc_pending_activity()) { // should the event fire is there is pending activity, or if we arrive at the time? - 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(); - } + 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); - } -// } + if (pending >= m_scheduled) { + m_th.resume(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } } ~sc_ob_event() {} diff --git a/src/sysc/utils/sc_on_context.h b/src/sysc/utils/sc_on_context.h index b4443c0b3..911cb83af 100644 --- a/src/sysc/utils/sc_on_context.h +++ b/src/sysc/utils/sc_on_context.h @@ -223,7 +223,7 @@ class sc_on_context : public sc_core::sc_module } } - m_jobs_handler_event.notify(sc_core::SC_ZERO_TIME); + m_jobs_handler_event.notify(); if (wait) { /* Wait for job completion */ diff --git a/src/sysc/utils/sc_parallel.h b/src/sysc/utils/sc_parallel.h index b6c897701..23f4369d8 100644 --- a/src/sysc/utils/sc_parallel.h +++ b/src/sysc/utils/sc_parallel.h @@ -38,6 +38,7 @@ 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 @@ -46,7 +47,7 @@ namespace sc_core { */ template class _internal_simcontext_handler { - + template friend class sc_parallel; /** * @brief Helper to capture start of simulation and start std::thread * @@ -131,6 +132,7 @@ class _internal_simcontext_handler { 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 @@ -139,7 +141,6 @@ class _internal_simcontext_handler { control_module m_ctrl_module; sc_sync_windowed m_sync_parent; -public: void use_simcontext() { assert(sc_curr_simcontext != &m_simcontext); m_old_simcontext = sc_curr_simcontext; @@ -148,6 +149,7 @@ class _internal_simcontext_handler { 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; diff --git a/src/sysc/utils/sc_sync_window.h b/src/sysc/utils/sc_sync_window.h index 8f429adbd..0bfab66ec 100644 --- a/src/sysc/utils/sc_sync_window.h +++ b/src/sysc/utils/sc_sync_window.h @@ -79,14 +79,14 @@ class sc_sync_windowed : public sc_core::sc_module, struct window { sc_core::sc_time from; sc_core::sc_time to; - bool operator==(window other) { + bool operator==(const window &other) const { return other.to == to && other.from == from; } }; - const struct window zero_window = {sc_core::SC_ZERO_TIME, + static inline const struct window zero_window = {sc_core::SC_ZERO_TIME, sc_core::SC_ZERO_TIME}; - const struct window open_window = {sc_core::SC_ZERO_TIME, + static inline const struct window open_window = {sc_core::SC_ZERO_TIME, sc_core::sc_max_time()}; private: @@ -98,7 +98,6 @@ class sc_sync_windowed : public sc_core::sc_module, void do_other_async_set_window_fn(window w) { if (m_other_async_set_window_fn) { - auto now = sc_core::sc_time_stamp(); m_other_async_set_window_fn(w); } } @@ -109,6 +108,8 @@ class sc_sync_windowed : public sc_core::sc_module, 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. @@ -122,17 +123,12 @@ class sc_sync_windowed : public sc_core::sc_module, 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(); - - // We are about to advance to the next event, so may as well set that as - // the window now - do_other_async_set_window_fn( - {now + (sc_core::sc_pending_activity() - ? sc_core::sc_time_to_pending_activity() - : sc_core::SC_ZERO_TIME), - now + policy.quantum()}); + //do_other_async_set_window_fn({now, now + policy.quantum()}); /* Re-notify event - maybe presumably moved */ m_step_ev.notify(to - now); @@ -162,6 +158,9 @@ class sc_sync_windowed : public sc_core::sc_module, } /* 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: @@ -171,7 +170,7 @@ class sc_sync_windowed : public sc_core::sc_module, * Input: window - Window to set for sync. Sweep till the 'from' and step to * the 'to'. */ - void async_set_window(window w) { + 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 */ @@ -192,9 +191,7 @@ class sc_sync_windowed : public sc_core::sc_module, "m_other_async_set_window_fn was already registered or other " "sc_sync_windowed was already bound!"); } - m_other_async_set_window_fn = [other](const window &w) { - other->async_set_window(w); - }; + 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) { @@ -216,7 +213,7 @@ class sc_sync_windowed : public sc_core::sc_module, dont_initialize(); sensitive << m_step_ev << m_update_ev; - m_step_ev.notify(sc_core::SC_ZERO_TIME); + m_step_ev.notify(policy.quantum()); this->sc_core::sc_prim_channel::async_attach_suspending(); } diff --git a/src/tlm_utils/simple_target_socket.h b/src/tlm_utils/simple_target_socket.h index 68fb1c44f..e645ec324 100644 --- a/src/tlm_utils/simple_target_socket.h +++ b/src/tlm_utils/simple_target_socket.h @@ -313,8 +313,11 @@ class simple_target_socket_b // forward call sc_assert(m_mod); 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]() { (m_mod->*m_b_transport_ptr)(trans, t); }); + [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); } diff --git a/tests/systemc/examples/psystemc/psystemc_test.cpp b/tests/systemc/examples/psystemc/psystemc_test.cpp index a6e505270..5affd3e2f 100644 --- a/tests/systemc/examples/psystemc/psystemc_test.cpp +++ b/tests/systemc/examples/psystemc/psystemc_test.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -9,15 +10,26 @@ std::mutex mutex; int do_work() { - return 1; - int f = 0; - for (int j = 0; j < 50000; j++) { - for (int k = 0; k < 10000; k++) { + 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; @@ -31,9 +43,10 @@ SC_MODULE(my_parallel_module) { void t3() { SC_REPORT_INFO(name(), "T3"); - for (int i = 0; i < 5; i++) { + 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(); @@ -44,13 +57,17 @@ SC_MODULE(my_parallel_module) { SC_REPORT_INFO(name(), "T3 finished loop"); } - void m2() { - SC_REPORT_INFO(name(), "Send TXN"); - tlm::tlm_generic_payload trans; - sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + 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"); + socket->b_transport(trans, delay); + SC_REPORT_INFO(name(), "done TXN"); + } } void m1() { @@ -67,23 +84,17 @@ SC_MODULE(my_parallel_module) { SC_THREAD(t1); SC_THREAD(t2); SC_THREAD(t3); + SC_THREAD(t4); SC_METHOD(m1); sensitive << in; dont_initialize(); - SC_METHOD(m2); - sensitive << send_txn; - 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"); - } + 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; @@ -103,9 +114,11 @@ SC_MODULE(My_normal_module) { void t3() { SC_REPORT_INFO(name(), "T3"); - for (int i = 0; i < 15; i++) { + for (int i = 0; i < 150000; i++) { SC_REPORT_INFO(name(), "T3 waiting"); - wait(50, sc_core::SC_MS); + wait(100, sc_core::SC_MS); + snaptime(time2); + SC_REPORT_INFO(name(), "T3 do work"); int f = do_work(); @@ -114,7 +127,7 @@ SC_MODULE(My_normal_module) { SC_REPORT_INFO( name(), ("Sending signal" + std::string((i & 0x1) ? "True" : "False")) .c_str()); - out.write(i & 0x1); + // out.write(i & 0x1); SC_REPORT_INFO(name(), "T3 done waiting"); } SC_REPORT_INFO(name(), "T3 finished loop"); @@ -131,21 +144,24 @@ SC_MODULE(My_normal_module) { } 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 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); - cout << "thread:" << std::this_thread::get_id() + 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[]) { @@ -153,7 +169,8 @@ 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(100, sc_core::SC_MS)); + 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"); @@ -168,6 +185,7 @@ int sc_main(int argc, char *argv[]) { SC_REPORT_INFO("main", "before start"); sc_start(); + std::cout << "Worst diff: " << worst << "\n"; SC_REPORT_INFO("main", "finished"); return 0;