scc 2026.07
SystemC components library
sc_time_syncronizer.cpp
1#include "sc_time_syncronizer.h"
2#include "global_time_keeper.h"
3#include <atomic>
4#include <systemc>
5#include <tlm_utils/tlm_quantumkeeper.h>
6
7namespace tlm {
8namespace scc {
9namespace qk {
10sc_time_syncronizer::sc_time_syncronizer(global_time_keeper& gtk)
11: gtk(gtk) {
12 sc_core::sc_register_stage_callback(*this, sc_core::SC_POST_END_OF_ELABORATION | sc_core::SC_POST_START_OF_SIMULATION |
13 sc_core::SC_PRE_TIMESTEP);
14 sc_core::sc_spawn_options opt;
15 opt.spawn_method();
16 sc_core::sc_spawn([this]() { sc_method(); }, nullptr, &opt);
17}
18
19bool sc_time_syncronizer::process_pending_tasks() {
20 auto res = this->gtk.get_sc_time_ticks();
21 auto& pending_tasks = this->gtk.pending_tasks;
22 if(pending_tasks.size()) {
23 while(pending_tasks.size()) {
24 auto res = pending_tasks.front();
25 auto idx = std::get<0>(*res);
26 auto t = std::get<1>(*res);
27 if(t > sc_core::sc_time_stamp().value()) {
28 auto d = sc_core::sc_time::from_value(t) - sc_core::sc_time_stamp();
29#ifdef DEBUG_MT_SCHEDULING
30 SCCDEBUG(__PRETTY_FUNCTION__) << "scheduling task from client " << idx << " with t=" << t << " with delay " << d;
31#endif
32 notify_task(idx, std::move(std::get<2>(*res)), d);
33 } else {
34#ifdef DEBUG_MT_SCHEDULING
35 SCCDEBUG(__PRETTY_FUNCTION__) << "scheduling task from client " << idx << " with t=" << t << " now";
36#endif
37 notify_task(idx, std::move(std::get<2>(*res)), sc_core::SC_ZERO_TIME);
38 }
39 pending_tasks.pop();
40 }
41 return true;
42 }
43 return false;
44}
45
46void sc_time_syncronizer::sc_method() {
47 if(process_pending_tasks()) {
48 sc_core::next_trigger(sc_core::SC_ZERO_TIME);
49 return;
50 }
51 if(state == sync_state::INITIAL)
52 state = sync_state::PROCESSING;
53#ifdef NCSC
54 auto time_to_next_evt = sc_core::sc_time_to_pending_activity();
55#else
56 auto time_to_next_evt = sc_core::sc_time_to_pending_activity(sc_core::sc_get_curr_simcontext());
57#endif
58 if(sc_core::sc_get_curr_simcontext()->pending_activity_at_current_time()) {
59#ifdef DEBUG_MT_SCHEDULING
60 SCCTRACEALL(__PRETTY_FUNCTION__) << "yield to next delta cycle (pending activity time: " << time_to_next_evt << ")";
61#endif
62 sc_core::next_trigger(sc_core::SC_ZERO_TIME);
63 return;
64 }
65 if(!sc_is_free_running) {
66 auto min_time = sc_core::sc_time::from_value(gtk.get_client_min_time_ticks());
67 auto abs_time_of_next_evt = sc_core::sc_time_stamp() + time_to_next_evt;
68 if(min_time < abs_time_of_next_evt) {
69 if(min_time > sc_core::sc_time_stamp()) {
70#ifdef DEBUG_MT_SCHEDULING
71 SCCTRACEALL(__PRETTY_FUNCTION__) << "advancing SC time lockstepped to " << min_time;
72#endif
73 sc_core::next_trigger(min_time - sc_core::sc_time_stamp());
74 return;
75 }
76 // slow down systemc execution to be the slower than client threads
77 std::this_thread::yield();
78 // std::this_thread::sleep_for(std::chrono::microseconds{1});
79 sc_core::next_trigger(sc_core::SC_ZERO_TIME); // play it again, Sam
80 return;
81 }
82#ifdef DEBUG_MT_SCHEDULING
83 SCCTRACEALL(__PRETTY_FUNCTION__) << "advancing SC time lockstepped to " << abs_time_of_next_evt;
84#endif
85 sc_core::next_trigger(time_to_next_evt);
86 return;
87 }
88 if(state == sync_state::PROCESSING) {
89 state = sync_state::ARMED;
90 sc_core::next_trigger(sc_core::SC_ZERO_TIME);
91 return;
92 }
93 // all threads are blocked by SystemC, so we can run freely but to avoid starving the kernel we only proceed to the
94 // time of the slowest client thread as this one is waiting to be served
95 auto next_time_point = sc_core::sc_time_stamp() + time_to_next_evt;
96 if(next_time_point.value() == std::numeric_limits<uint64_t>::max()) {
97 time_to_next_evt = tlm_utils::tlm_quantumkeeper::get_global_quantum();
98 if(time_to_next_evt == sc_core::SC_ZERO_TIME)
99 time_to_next_evt = 1_us;
100 }
101#ifdef DEBUG_MT_SCHEDULING
102 SCCTRACEALL(__PRETTY_FUNCTION__) << "advancing SC time free running to " << next_time_point;
103#endif
104 sc_core::next_trigger(time_to_next_evt);
105}
106
107void sc_time_syncronizer::stage_callback(const sc_core::sc_stage& stage) {
108 switch(stage) {
109 case sc_core::SC_PRE_TIMESTEP: {
110 sc_core::sc_time next_time;
111 if(sc_core::sc_get_curr_simcontext()->next_time(next_time)) {
112 gtk.update_sc_time_ticks(next_time.value());
113 while(gtk.get_sc_time_ticks() != next_time.value())
114 std::this_thread::yield();
115 }
116 state = sync_state::INITIAL;
117#ifdef DEBUG_MT_SCHEDULING
118 SCCTRACEALL(__PRETTY_FUNCTION__) << "advancing SystemC kernel time to " << next_time << ", get_min_time()=" << get_min_time()
119 << ", sc_is_free_running=" << sc_is_free_running;
120#endif
121 } break;
122 case sc_core::SC_POST_END_OF_ELABORATION:
123 gtk.start();
124 /* fall thru */
125 case sc_core::SC_POST_START_OF_SIMULATION:
126 gtk.update_sc_time_ticks(sc_core::sc_time_stamp().value());
127 break;
128 default:
129 break;
130 }
131}
132} // namespace qk
133} // namespace scc
134} // namespace tlm
SCC TLM utilities.
Definition axis_tlm.h:56
SystemC TLM.
Definition dmi_mgr.h:19
the global time keeper, a singleton