1#ifndef __SCC_TLM_QUANTUMKEEPER_H__
2#define __SCC_TLM_QUANTUMKEEPER_H__
4#include "rigtorp/SPSCQueue.h"
5#include "sysc/kernel/sc_wait.h"
7#include <condition_variable>
14#include <nonstd/optional.hpp>
15#include <scc/async_thread.h>
17#include <scc/report.h>
18#include <scc/utilities.h>
20#include <sysc/kernel/sc_object.h>
21#include <sysc/kernel/sc_process.h>
22#include <sysc/kernel/sc_simcontext.h>
23#include <sysc/kernel/sc_spawn.h>
24#include <sysc/kernel/sc_spawn_options.h>
25#include <sysc/kernel/sc_time.h>
29#include <tlm_utils/tlm_quantumkeeper.h>
31#if SC_VERSION_MAJOR < 3
32#error "Multithreaded quantum keeper is only supported with SystemC 3.0 and newer"
38#ifdef __cpp_lib_hardware_interference_size
39static constexpr size_t kCacheLineSize = std::hardware_destructive_interference_size;
41static constexpr size_t kCacheLineSize = 64;
46struct quantumkeeper :
public tlm_utils::tlm_quantumkeeper {
47 using base = tlm_utils::tlm_quantumkeeper;
51 virtual ~quantumkeeper() {}
53 inline void check_and_sync(sc_core::sc_time core_inc) {
55 m_local_time += core_inc;
56 if((sc_core::sc_time_stamp() + m_local_time) >= m_next_sync_point) {
58 ::sc_core::wait(m_local_time);
59 m_local_time = sc_core::SC_ZERO_TIME;
60 m_next_sync_point = sc_core::sc_time_stamp() + compute_local_quantum();
63 sc_core::sc_time get_local_absolute_time()
const {
return base::get_current_time(); }
69 inline void execute_on_sysc(std::function<
void(
void)> fct) { execute_on_sysc(fct, sc_core::sc_time_stamp()); }
76 inline void execute_on_sysc(std::function<
void(
void)>& fct, sc_core::sc_time when) {
77 if(when > sc_core::SC_ZERO_TIME)
85using callback_fct = sc_core::sc_time(
void);
86using callback_task = std::packaged_task<callback_fct>;
100struct thread_comms_channel {
106 thread_comms_channel(uint64_t my_id)
108 , client2time_keeper(7)
109 , time_keeper2client(0) {}
115 thread_comms_channel(thread_comms_channel
const& o)
117 , client2time_keeper(o.client2time_keeper.capacity())
118 , time_keeper2client(0)
119 , thread_local_time(o.thread_local_time) {
120 assert(o.client2time_keeper.size() == 0);
123 rigtorp::SPSCQueue<comms_entry> client2time_keeper;
124 std::atomic<uint64_t> time_keeper2client;
126 const uint64_t my_id;
127 uint64_t thread_local_time;
132struct sc_time_syncronizer;
141struct global_time_keeper {
142 friend class sc_time_syncronizer;
147 const sc_core::sc_time sc_time_step = 1_ms;
153 static global_time_keeper& get() {
154 static global_time_keeper keeper;
162 inline uint64_t get_min_time_ticks() {
return window_min_time; }
169 inline uint64_t get_max_time_ticks(
size_t idx) {
return client_coms_channels[idx].time_keeper2client.load(); }
176 inline void update_time_ticks(
size_t idx, uint64_t tick) {
177 client_coms_channels[idx].client2time_keeper.push(std::move(comms_entry{tick, std::move(callback_task())}));
178 update_it.store(
true);
179 std::unique_lock<std::mutex> lk(upd_mtx);
189 inline void schedule_task(
size_t idx, std::packaged_task<sc_core::sc_time(
void)>&& task, uint64_t when) {
190 client_coms_channels[idx].client2time_keeper.push(std::move(comms_entry{when, std::move(task)}));
191 update_it.store(
true);
192 std::unique_lock<std::mutex> lk(upd_mtx);
200 inline uint64_t get_max_sc_time_ticks() {
return sc_coms_channel.time_keeper2client.load(); }
206 inline void update_sc_time_ticks(uint64_t tick) {
207 sc_coms_channel.client2time_keeper.push(std::move(comms_entry{tick, std::move(callback_task())}));
208#ifdef DEBUG_MT_SCHDULING
209 SCCTRACEALL(
"global_time_keeper::update_sc_time_ticks") <<
"sc_time=" << sc_core::sc_time::from_value(tick);
211 update_it.store(
true);
212 std::unique_lock<std::mutex> lk(upd_mtx);
217 global_time_keeper();
219 global_time_keeper(global_time_keeper
const&) =
delete;
221 global_time_keeper(global_time_keeper&&) =
delete;
223 ~global_time_keeper();
227 size_t get_channel_index();
229 void sync_local_times();
231 std::atomic<bool> stop_it{
false};
232 std::atomic<bool> update_it{
false};
233 std::atomic_bool all_threads_blocked{
true};
234 std::atomic_uint64_t window_min_time;
236 std::condition_variable update;
237 thread_comms_channel sc_coms_channel{0};
238 std::deque<thread_comms_channel> client_coms_channels;
239 rigtorp::SPSCQueue<std::tuple<size_t, uint64_t, callback_task>> pending_tasks{1024};
240 bool started =
false;
248struct sc_time_syncronizer : sc_core::sc_object, sc_core::sc_stage_callback_if, sc_core::sc_process_host {
249 ~sc_time_syncronizer() =
default;
255 static sc_time_syncronizer& get() {
256 static sc_time_syncronizer keeper(global_time_keeper::get());
266 size_t get_channel_index() {
267 auto res = gtk.get_channel_index();
268 sc_task_que.emplace_back();
269 blocked_channels.emplace_back(
true);
270 auto& sctq = sc_task_que[sc_task_que.size() - 1];
273 wait(sc_core::SC_ZERO_TIME);
278 sc_core::sc_gen_unique_name(
"peq_cb",
false));
287 inline sc_core::sc_time get_sc_kernel_time() {
return sc_core::sc_time::from_value(sc_max_time.load(std::memory_order_seq_cst)); }
289 inline void notify_channel_blocked(
size_t idx,
bool is_blocked) {
290 assert(idx < blocked_channels.size());
291 blocked_channels[idx] = is_blocked;
292 sc_is_free_running = !std::any_of(std::begin(blocked_channels), std::end(blocked_channels), [](
bool b) {
return !b; });
296 sc_time_syncronizer(global_time_keeper& gtk);
297 void method_callback();
298 void stage_callback(
const sc_core::sc_stage& stage)
override;
299 sc_core::sc_time get_min_time() {
return sc_core::sc_time::from_value(gtk.get_min_time_ticks()); }
300 global_time_keeper& gtk;
301 sc_core::sc_vector<::scc::peq<callback_task>> sc_task_que;
302 std::atomic_int64_t sc_max_time{0};
303 sc_core::sc_process_handle method_handle;
304 std::vector<bool> blocked_channels;
305 bool sc_is_free_running{
true};
313struct quantumkeeper_mt {
319 : gtk_idx(sc_time_syncronizer::get().get_channel_index()) {
320 sc_core::sc_spawn_options opt;
322 opt.set_sensitivity(&keep_alive);
328 virtual ~quantumkeeper_mt() =
default;
330 void run_thread(std::function<sc_core::sc_time()> f) {
331 sc_time_syncronizer::get().notify_channel_blocked(gtk_idx,
false);
332 thread_executor.start(f);
333 wait(thread_executor.thread_finish_event());
340 inline void inc(sc_core::sc_time
const& core_inc) {
341 local_absolute_time += core_inc;
342 auto res = global_time_keeper::get().get_max_time_ticks(gtk_idx);
343 if(res != std::numeric_limits<uint64_t>::max())
344 local_time_ticks_limit = res;
351 inline void check_and_sync(sc_core::sc_time
const& core_inc) {
353 global_time_keeper::get().update_time_ticks(gtk_idx, local_absolute_time.value());
355 while(local_absolute_time.value() > local_time_ticks_limit) {
356 std::this_thread::yield();
357 auto res = global_time_keeper::get().get_max_time_ticks(gtk_idx);
358 if(res != std::numeric_limits<uint64_t>::max())
359 local_time_ticks_limit = res;
367 inline void execute_on_sysc(std::function<
void(
void)> fct) { execute_on_sysc(fct, local_absolute_time); }
374 inline void execute_on_sysc(std::function<
void(
void)>& fct, sc_core::sc_time when) {
375 callback_task task([
this, &fct]() {
376 sc_time_syncronizer::get().notify_channel_blocked(gtk_idx,
true);
377 auto t0 = sc_core::sc_time_stamp();
379 sc_time_syncronizer::get().notify_channel_blocked(gtk_idx,
false);
380 return t0 - sc_core::sc_time_stamp();
382 auto ret = task.get_future();
383 global_time_keeper::get().schedule_task(gtk_idx, std::move(task), when.value());
384 auto duration = ret.get();
385 check_and_sync(duration);
392 sc_core::sc_time get_local_time()
const {
return local_absolute_time - get_current_sc_time(); }
398 sc_core::sc_time get_local_absolute_time()
const {
return local_absolute_time; }
404 sc_core::sc_time get_current_sc_time()
const {
return sc_time_syncronizer::get().get_sc_kernel_time(); }
409 void reset() { reset(get_current_sc_time()); }
415 void reset(sc_core::sc_time abs_time) { local_absolute_time = sc_core::sc_time_stamp(); }
417 void unblock_thread() { global_time_keeper::get().update_time_ticks(gtk_idx, local_absolute_time.value()); }
421 ::scc::async_thread thread_executor;
422 sc_core::sc_event keep_alive;
423 sc_core::sc_time local_absolute_time;
424 uint64_t local_time_ticks_limit{0};