scc 2025.09
SystemC components library
quantum_keeper.cpp
1#include "quantum_keeper.h"
2#include <atomic>
3#include <cstdint>
4#include <limits>
5#include <scc/report.h>
6#include <stdexcept>
7#include <sysc/kernel/sc_object.h>
8#include <sysc/kernel/sc_simcontext.h>
9#include <sysc/kernel/sc_stage_callback_if.h>
10#include <sysc/kernel/sc_time.h>
11#include <thread>
12#include <tlm_utils/tlm_quantumkeeper.h>
13
14namespace tlm {
15namespace scc {
17//
19global_time_keeper::global_time_keeper() = default;
20
21global_time_keeper::~global_time_keeper() {
22 // shutting down time keeper thread
23 stop_it.store(true, std::memory_order_acq_rel);
24 update.notify_all();
25}
26// this function will be called from the systemc thread
27void global_time_keeper::start() {
28 std::thread t{&global_time_keeper::sync_local_times, this};
29 t.detach();
30 started = true;
31}
32// this function will be called from the systemc thread
33size_t global_time_keeper::get_channel_index() {
34 if(started)
35 throw std::runtime_error("global_time_keeper already started");
36 client_coms_channels.emplace_back(client_coms_channels.size());
37 update_it = true;
38 return client_coms_channels.size() - 1;
39}
40
41// runs in a background thread
42void global_time_keeper::sync_local_times() {
43 std::unique_lock<std::mutex> lock{upd_mtx};
44 while(true) {
45 update.wait_for(lock, std::chrono::milliseconds(1),
46 [this]() -> bool { return update_it.load(std::memory_order_relaxed) || stop_it.load(std::memory_order_relaxed); });
47 if(update_it.exchange(false)) {
48#ifdef DEBUG_MT_SCHDULING
49 SCCTRACEALL("global_time_keeper::sync_local_times") << "update loop";
50#endif
51 uint64_t min_local_time = std::numeric_limits<uint64_t>::max();
52 bool tail = false;
53 while(auto res = sc_coms_channel.client2time_keeper.front()) {
54 sc_coms_channel.thread_local_time = res->time_tick;
55 sc_coms_channel.client2time_keeper.pop();
56 }
57 for(auto& client_coms_channel : client_coms_channels) {
58 while(auto res = client_coms_channel.client2time_keeper.front()) {
59 client_coms_channel.thread_local_time = res->time_tick;
60 if(res->task.valid()) {
61 pending_tasks.emplace(client_coms_channel.my_id, res->time_tick, std::move(res->task));
62 }
63 client_coms_channel.client2time_keeper.pop();
64 }
65 min_local_time = std::min(client_coms_channel.thread_local_time, min_local_time);
66#ifdef DEBUG_MT_SCHDULING
67 SCCTRACEALL("global_time_keeper::sync_local_times")
68 << "thread_local_time=" << sc_core::sc_time::from_value(client_coms_channel.thread_local_time);
69#endif
70 }
71 this->all_threads_blocked.store(all_threads_blocked);
72 window_min_time = min_local_time;
73 sc_coms_channel.time_keeper2client.store(min_local_time);
74 auto window_max_time = min_local_time + std::max(tlm::tlm_global_quantum::instance().get().value(), sc_time_step.value());
75 for(auto& client_coms_channel : client_coms_channels) {
76 client_coms_channel.time_keeper2client.store(window_max_time);
77 }
78#ifdef DEBUG_MT_SCHDULING
79 SCCTRACEALL("global_time_keeper::sync_local_times")
80 << "sc_freerunning=" << all_threads_blocked << ", window_min_time=" << window_min_time;
81#endif
82 } else if(stop_it.load()) {
83 break;
84 }
85 }
86}
88//
90sc_time_syncronizer::sc_time_syncronizer(global_time_keeper& gtk)
91: sc_core::sc_object("sc_time_syncronizer")
92, gtk(gtk) {
93 sc_core::sc_register_stage_callback(*this, sc_core::SC_PRE_TIMESTEP | sc_core::SC_POST_END_OF_ELABORATION);
94 sc_core::sc_spawn_options opt;
95 opt.spawn_method();
96 sc_core::sc_spawn([this]() { method_callback(); }, nullptr, &opt);
97}
98
99void sc_time_syncronizer::method_callback() {
100 auto res = this->gtk.get_max_sc_time_ticks();
101 if(res != std::numeric_limits<uint64_t>::max()) {
102 sc_max_time.store(res, std::memory_order_seq_cst);
103 }
104 auto& pending_tasks = this->gtk.pending_tasks;
105 if(pending_tasks.size()) {
106#ifdef DEBUG_MT_SCHDULING
107 SCCTRACEALL(__PRETTY_FUNCTION__) << "updating pending tasks";
108#endif
109 while(pending_tasks.size()) {
110 auto res = pending_tasks.front();
111 auto& peq = sc_task_que[std::get<0>(*res)];
112 auto t = std::get<1>(*res);
113#ifdef DEBUG_MT_SCHDULING
114 SCCDEBUG(__PRETTY_FUNCTION__) << "scheduling task at " << t;
115#endif
116 if(t > sc_core::sc_time_stamp().value())
117 peq.notify(std::move(std::get<2>(*res)), sc_core::sc_time::from_value(t) - sc_core::sc_time_stamp());
118 else
119 peq.notify(std::move(std::get<2>(*res)));
120#ifdef DEBUG_MT_SCHDULING
121 SCCTRACEALL(__PRETTY_FUNCTION__) << "setting thread_blocked[" << std::get<0>(*res) << "]=1";
122#endif
123 pending_tasks.pop();
124 }
125 sc_core::next_trigger(sc_core::SC_ZERO_TIME);
126 } else if(sc_core::sc_get_curr_simcontext()->pending_activity_at_current_time()) {
127#ifdef DEBUG_MT_SCHDULING
128 SCCTRACEALL(__PRETTY_FUNCTION__) << "yield to next delta cycle";
129#endif
130 sc_core::next_trigger(sc_core::SC_ZERO_TIME);
131 } else {
132 auto time_to_next_evt = sc_core::sc_time_to_pending_activity(sc_core::sc_get_curr_simcontext());
133 auto min_time = get_min_time();
134 if(!sc_is_free_running) {
135 auto abs_time_to_next_evt = time_to_next_evt + sc_core::sc_time_stamp();
136 if(min_time < abs_time_to_next_evt) {
137 if(min_time > sc_core::sc_time_stamp()) {
138 sc_core::next_trigger(min_time - sc_core::sc_time_stamp());
139 } else {
140 // slow down systemc execution to be the slower than client threads
141 std::this_thread::yield();
142 // std::this_thread::sleep_for(std::chrono::microseconds{1});
143 sc_core::next_trigger(sc_core::SC_ZERO_TIME); // play it again, Sam
144 }
145 } else {
146#ifdef DEBUG_MT_SCHDULING
147 SCCTRACEALL(__PRETTY_FUNCTION__) << "advancing SC time lockstepped to " << time_to_next_evt;
148#endif
149 sc_core::next_trigger(time_to_next_evt);
150 }
151 } else {
152 // all threads are blocked by SystemC, so we can run freely but to avoid starving the kernel we only proceed to the
153 // time of the slowest client thread as this one is waiting to be served
154 auto next_time_point = sc_core::sc_time_stamp() + time_to_next_evt;
155 if(next_time_point.value() == std::numeric_limits<uint64_t>::max()) {
156 time_to_next_evt = tlm_utils::tlm_quantumkeeper::get_global_quantum();
157 if(time_to_next_evt == sc_core::SC_ZERO_TIME)
158 time_to_next_evt = 1_us;
159 }
160 sc_core::next_trigger(time_to_next_evt);
161 }
162 }
163}
164
165void sc_time_syncronizer::stage_callback(const sc_core::sc_stage& stage) {
166 switch(stage) {
167 case sc_core::SC_PRE_TIMESTEP: {
168 sc_core::sc_time next_time;
169 if(sc_core::sc_get_curr_simcontext()->next_time(next_time)) {
170 gtk.update_sc_time_ticks(next_time.value());
171 }
172#ifdef DEBUG_MT_SCHDULING
173 SCCTRACEALL(SCMOD) << "advancing SystemC kernel time to " << next_time << ", get_min_time()=" << get_min_time();
174#endif
175 } break;
176 case sc_core::SC_POST_END_OF_ELABORATION:
177 gtk.start();
178 break;
179 default:
180 break;
181 }
182}
183
184} // namespace scc
185} // namespace tlm
SCC TLM utilities.
Definition axis_tlm.h:56
SystemC TLM.
Definition dmi_mgr.h:19