1#include "quantum_keeper.h"
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>
12#include <tlm_utils/tlm_quantumkeeper.h>
19global_time_keeper::global_time_keeper() =
default;
21global_time_keeper::~global_time_keeper() {
23 stop_it.store(
true, std::memory_order_acq_rel);
27void global_time_keeper::start() {
28 std::thread t{&global_time_keeper::sync_local_times,
this};
33size_t global_time_keeper::get_channel_index() {
35 throw std::runtime_error(
"global_time_keeper already started");
36 client_coms_channels.emplace_back(client_coms_channels.size());
38 return client_coms_channels.size() - 1;
42void global_time_keeper::sync_local_times() {
43 std::unique_lock<std::mutex> lock{upd_mtx};
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";
51 uint64_t min_local_time = std::numeric_limits<uint64_t>::max();
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();
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));
63 client_coms_channel.client2time_keeper.pop();
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);
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);
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;
82 }
else if(stop_it.load()) {
90sc_time_syncronizer::sc_time_syncronizer(global_time_keeper& gtk)
91: sc_core::sc_object(
"sc_time_syncronizer")
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;
96 sc_core::sc_spawn([
this]() { method_callback(); },
nullptr, &opt);
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);
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";
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;
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());
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";
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";
130 sc_core::next_trigger(sc_core::SC_ZERO_TIME);
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());
141 std::this_thread::yield();
143 sc_core::next_trigger(sc_core::SC_ZERO_TIME);
146#ifdef DEBUG_MT_SCHDULING
147 SCCTRACEALL(__PRETTY_FUNCTION__) <<
"advancing SC time lockstepped to " << time_to_next_evt;
149 sc_core::next_trigger(time_to_next_evt);
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;
160 sc_core::next_trigger(time_to_next_evt);
165void sc_time_syncronizer::stage_callback(
const sc_core::sc_stage& 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());
172#ifdef DEBUG_MT_SCHDULING
173 SCCTRACEALL(SCMOD) <<
"advancing SystemC kernel time to " << next_time <<
", get_min_time()=" << get_min_time();
176 case sc_core::SC_POST_END_OF_ELABORATION: