The Sparta Modeling Framework
Loading...
Searching...
No Matches
SysCSpartaSchedulerAdapter.hpp
Go to the documentation of this file.
1// <SysCSpartaSchedulerAdapter> -*- C++ -*-
2
3
11#pragma once
12
13#include <climits>
14
17
18// Ignore -Wconversion issues with SysC
19#pragma GCC diagnostic push
20#pragma GCC diagnostic ignored "-Wconversion"
21 #include "sysc/kernel/sc_event.h"
22 #include "sysc/kernel/sc_time.h"
23 #include "sysc/kernel/sc_module.h"
24 #include "sysc/communication/sc_clock.h"
25#pragma GCC diagnostic pop
26
27namespace sparta
28{
29
30 namespace sparta_sysc_utils {
56 inline Clock::Cycle calculateSpartaOffset(const sparta::Clock * sparta_clk,
57 sc_core::sc_time::value_type sysc_offset)
58 {
59
60 //
61 // This is a transaction coming from SysC that is on SysC's
62 // clock, not Sparta's. Need to find the same tick cycle on
63 // the Sparta clock and align the time for the transaction.
64 // The Sparta's scheduler starts at tick 0 like SysC.
65 //
66 // Another item to note is how the sparta::Scheduler sees
67 // time. If the Scheduler is running, currentTick
68 // represents the time events are being fired. If the
69 // Scheduler _is not running_ currentTick represents the
70 // tick _after_ the last successful run. In the case the
71 // Scheduler is not running, use elapsedCycles to align
72 // the schedulers.
73 //
74 // For example,
75 // - The Sparta's clock is at 7 ticks
76 // - The SysC clock is at 10 ticks
77 // - The transaction's delay is 1 tick (to be fired at tick 11)
78 //
79 // sysc_clock - sparta_clock + sysc_offset = 4 cycles on sparta clock (11)
80 //
81
82 // Send to memory with the given delay - NS -> clock cycles.
83 // The Clock is on the same freq as the memory block
84 const auto current_sc_time = sc_core::sc_time_stamp().value();
85 const auto sparta_sched = sparta_clk->getScheduler();
86 const auto current_tick = sparta_sched->isRunning() ?
87 sparta_sched->getCurrentTick() : sparta_sched->getElapsedTicks();
88 sparta_assert(current_sc_time >= current_tick);
89 const auto final_relative_tick = current_sc_time - current_tick + sysc_offset;
90 return final_relative_tick;
91 }
92 }
93
96#define SC_SPARTA_SCHEDULER_NAME "SysCSpartaSchedulerAdapter"
97
100#define SC_SPARTA_STOP_EVENT_NAME "sc_ev_stop_simulation"
101
130class SysCSpartaSchedulerAdapter : public sc_core::sc_module
131{
132
133 // Called by the Scheduler when a new event is scheduled
134 void wakeupAdapter_(const Scheduler::Tick&) {
135 sc_wake_sparta_.notify();
136 sparta_scheduler_->
137 deregisterForNotification<Scheduler::Tick,
139 &SysCSpartaSchedulerAdapter::wakeupAdapter_>(this, "item_scheduled");
140 }
141
142public:
143
146
149 sc_module(sc_core::sc_module_name(SC_SPARTA_SCHEDULER_NAME)),
150 sparta_scheduler_(scheduler),
151 sc_ev_stop_simulation_(SC_SPARTA_STOP_EVENT_NAME),
152 sc_wake_sparta_("sc_ev_wake_sparta")
153 {
154 SC_THREAD(runScheduler_);
155 SC_METHOD(setSystemCSimulationDone);
156 dont_initialize();
157 sensitive << sc_ev_stop_simulation_;
158
159 switch(PS_PER_SECOND/sparta_scheduler_->getFrequency())
160 {
161 case 1:
162 sparta_sc_time_ = sc_core::SC_PS;
163 break;
164 case 10:
165 sparta_sc_time_ = sc_core::SC_NS;
166 break;
167 case 100:
168 sparta_sc_time_ = sc_core::SC_US;
169 break;
170 default:
171 throw sparta::SpartaException("Frequency not supported");
172 break;
173 }
174 }
175
186 {
187 double convert_to_sysc_time = std::numeric_limits<double>::max();
188 if(num_ticks != Scheduler::INDEFINITE) {
189 convert_to_sysc_time = num_ticks;
190 }
191 sc_core::sc_start(sc_core::sc_time(convert_to_sysc_time, sparta_sc_time_));
192 }
193
200 // Only report this once.
201 if(!sysc_simulation_done_) {
202 std::cout << "SysCSpartaSchedulerAdapter: SystemC reports finished on tick "
203 << sc_core::sc_time_stamp().value() << std::endl;
204 sysc_simulation_done_ = true;
205 }
206 }
207
226 const Scheduler::Tick interval)
227 {
228 sysc_query_event_ = sysc_query_event;
229 sysc_query_event_interval_ = interval;
230 next_sysc_event_fire_tick_ = sparta_scheduler_->getCurrentTick() + interval;
231 sparta_assert(sysc_query_event_->isContinuing() == false,
232 "This event should be non-continuing");
233 }
234
238 bool wasScStopCalled() const {
239 return sc_stop_called_;
240 }
241
242private:
243
245 void runScheduler_()
246 {
247 // Start simulation -- align the schedulers
248 sparta_assert(sparta_scheduler_->nextEventTick() > 0);
249
250 // Align the schedulers. Sparta starts at tick 1
251 wait(sc_core::sc_time(double(1), sparta_sc_time_));
252
253 do {
254 // If the Sparta Scheduler has nothing to do, put it to sleep
255 if(sparta_scheduler_->nextEventTick() == Scheduler::INDEFINITE) {
256 sparta_scheduler_->registerForNotification<Scheduler::Tick,
258 &SysCSpartaSchedulerAdapter::wakeupAdapter_>(this, "item_scheduled");
259 wait(sc_wake_sparta_);
260 }
261
262 //
263 // Wait for SysC to get to the next event time on the
264 // Sparta Scheduler, then advance Sparta
265 //
266 const sc_core::sc_time sysc_time = sc_core::sc_time_stamp();
267 if(sparta_scheduler_->nextEventTick() >= sysc_time.value()) {
268 // Wait until the sysc scheduled catches up with Sparta
269 wait(sc_core::sc_time(double(sparta_scheduler_->nextEventTick() - sysc_time.value()),
270 sparta_sc_time_));
271 }
272
273 // Align to the posedge events in systemc
274 wait(sc_core::SC_ZERO_TIME);
275
276 // If given, schedule the user's SystemC query event to
277 // allow the Sparta user to check to see if the SystemC side
278 // of simulation is complete. This query can be as simple
279 // as asking the SystemC kernel if there are any events
280 // (sc_core::sc_pending_activity()) or asking the SystemC
281 // simulator if it's done.
282 if(sysc_query_event_ && sparta_scheduler_->getCurrentTick() >= next_sysc_event_fire_tick_)
283 {
284 next_sysc_event_fire_tick_ = sparta_scheduler_->getCurrentTick() + sysc_query_event_interval_;
285 sysc_query_event_->scheduleRelativeTick(1, sparta_scheduler_);
286 }
287
288 advanceSpartaScheduler_();
289
290 } while(!sparta_scheduler_->isFinished() || !sysc_simulation_done_);
291
292 // Stop simulation
293 sc_core::sc_stop();
294 sc_stop_called_ = true;
295 }
296
297 void advanceSpartaScheduler_()
298 {
299 const sc_core::sc_time sysc_time = sc_core::sc_time_stamp();
300
301 // The SystemC scheduler will always be exactly at the same
302 // tick as Sparta when this function is called. Following
303 // this rule allows safe assumptions in scheduling
304 // synchronization.
305 sparta_assert(sysc_time.value() == sparta_scheduler_->nextEventTick());
306 sparta_assert(sparta_scheduler_->nextEventTick() >= sparta_scheduler_->getCurrentTick());
307
308 constexpr bool exacting_run = true;
309 constexpr bool measure_scheduler_time = false; // no need to do this
310 // Run to the next event scheduled and then run that cycle
311 sparta_scheduler_->run(sparta_scheduler_->nextEventTick() - sparta_scheduler_->getCurrentTick() + 1,
312 exacting_run, measure_scheduler_time);
313
314 // Sparta should now be finished with the cycle at `sysc_time`
315 // and be exactly one cycle ahead of it.
316 sparta_assert(sysc_time.value() + 1 == sparta_scheduler_->getCurrentTick());
317 }
318
319 // Local copy of the sparta scheduler
320 sparta::Scheduler * sparta_scheduler_ = nullptr;
321
322 // Time unit Sparta runs in
323 sc_core::sc_time_unit sparta_sc_time_ = sc_core::SC_PS;
324
325 // Boolean that tells our runScheduler_ thread to exit -- the rest
326 // of the SystemC scheduling is complete
327 bool sysc_simulation_done_ = false;
328
329 // SystemC event that stops simulation
330 sc_core::sc_event sc_ev_stop_simulation_;
331 bool sc_stop_called_ = false;
332
333 // SystemC event that wakes the Sparta Scheduler
334 sc_core::sc_event sc_wake_sparta_;
335
336 // Sparta Event (optional) that will be automatically scheduled if
337 // the Sparta scheduler is finished and the driver needs to query systemc
338 sparta::Scheduleable * sysc_query_event_ = nullptr;
339 Scheduler::Tick sysc_query_event_interval_ = 10000;
340 Scheduler::Tick next_sysc_event_fire_tick_ = 0;
341};
342
343}
File that defines the Event class.
A simple time-based, event precedence based scheduler.
#define PS_PER_SECOND
Picoseconds per second constant.
Definition Scheduler.hpp:52
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SC_SPARTA_STOP_EVENT_NAME
#define SC_SPARTA_SCHEDULER_NAME
Clock::Cycle calculateSpartaOffset(const sparta::Clock *sparta_clk, sc_core::sc_time::value_type sysc_offset)
A representation of simulated time.
Definition Clock.hpp:51
Scheduler * getScheduler() const
Definition Clock.hpp:302
A class that defines the basic scheduling interface to the Scheduler. Not intended to be used by mode...
virtual void scheduleRelativeTick(const Scheduler::Tick rel_tick, Scheduler *const scheduler)
Schedule this event on a relative scheduler tick.
bool isContinuing() const
Is this Event continuing?
A class that lets you schedule events now and in the future.
bool isFinished() const
Returns true if there are no more pending non-continuing events.
Tick getCurrentTick() const noexcept
The current tick the Scheduler is working on or just finished.
bool isRunning() const noexcept
Query if the scheduler is running.
Tick getFrequency() const
Returns the frequency (in ticks per simulated second) of this Scheduler.
Tick nextEventTick() const
Returns the next tick an event is pending.
uint64_t Tick
Typedef for our unit of time.
void run(Tick num_ticks=INDEFINITE, const bool exacting_run=false, const bool measure_run_time=true)
Enter running state and runs the scheduler until running is stopped (e.g. through a stop event) or th...
static const Tick INDEFINITE
Constant for infinite tick count.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Class that "connects" Sparta to SystemC.
bool wasScStopCalled() const
Return whether the schedule called sc_stop()
void setSystemCSimulationDone()
Set simulation complete on the SystemC side via the SC_Sparta_STOP_EVENT_NAME sc_event....
void run(Scheduler::Tick num_ticks=Scheduler::INDEFINITE)
Run simulation – all of it including SystemC.
SysCSpartaSchedulerAdapter(Scheduler *scheduler)
Initialized the sc_module this adapter is part of.
SC_HAS_PROCESS(SysCSpartaSchedulerAdapter)
Register the process for SystemC.
void registerSysCFinishQueryEvent(sparta::Scheduleable *sysc_query_event, const Scheduler::Tick interval)
Register a sparta::Event that is used to determine if the SystemC components are finished.
void registerForNotification(T *obj, const std::string &name, bool ensure_possible=true)
Registers a callback method to listen for all notifications having the specified data type DataT and ...
Macros for handling exponential backoff.