The Sparta Modeling Framework
Loading...
Searching...
No Matches
Clock.hpp
Go to the documentation of this file.
1// <Clock> -*- C++ -*-
2
3
4#pragma once
5
12#include <inttypes.h>
13#include <cstdint>
14#include <string>
15#include <iostream>
16#include <sstream>
17#include <memory>
18#include <list>
19#include <map>
20
23#include "sparta/statistics/Counter.hpp"
24#include "sparta/statistics/ReadOnlyCounter.hpp"
25#include "sparta/utils/Rational.hpp"
26#include "sparta/utils/MathUtils.hpp"
27#include "simdb_fwd.hpp"
28#include "simdb/schema/DatabaseTypedefs.hpp"
29#include "sparta/statistics/CounterBase.hpp"
33
34namespace simdb {
35 class TableRef;
36 class ObjectManager;
37} // namespace simdb
38
39namespace sparta
40{
50 class Clock : public TreeNode
51 {
52 public:
53 typedef std::shared_ptr<Clock> Handle;
54 typedef uint32_t Period;
55 typedef uint64_t Cycle;
56 typedef double Frequency;
57
58 private:
59 static bool normalized_;
60 typedef std::list<Clock*> RefList;
61
62 public:
63
69 Clock(const std::string& name, Scheduler *scheduler);
70
81 Clock(RootTreeNode* parent_root, const std::string& name, Scheduler *scheduler);
82
94 Clock(const std::string& name, const Handle& parent_clk,
95 const uint32_t& p_rat = 1, const uint32_t& c_rat = 1);
96
101 Clock(const std::string& name, const Handle& parent, double frequency_mhz);
102
105
107 void associate(const Handle& parent);
108
112 void setRatio(const uint32_t& p_rat = 1, const uint32_t& c_rat = 1);
113
115 double getFrequencyMhz() const {
116 return frequency_mhz_;
117 }
118
121 {
122 return parent_ratio_;
123 }
124
126 uint32_t calcNorm(uint32_t partial_norm = 1)
127 {
128 if (parent_ != nullptr) {
129 root_ratio_ = parent_ratio_.inv() * parent_->root_ratio_;
130 }
131
132 partial_norm = utils::lcm(partial_norm, root_ratio_.getDenominator());
133 for (auto i = children_.begin(); i != children_.end(); ++i) {
134 partial_norm = utils::lcm(partial_norm, (*i)->calcNorm(partial_norm));
135 }
136 return partial_norm;
137 }
138
145 void setPeriod(uint32_t norm)
146 {
148 "Should not be setting period on a sparta::Clock after device tree finalization");
149 period_ = uint32_t(root_ratio_ * norm);
150 }
151
156 Period getPeriod() const
157 {
158 return period_;
159 }
160
166 Cycle getCycle(const Scheduler::Tick& tick) const
167 {
168 return (tick / period_);
169 }
170
176 Cycle currentCycle() const
177 {
178 return getCycle(scheduler_->getCurrentTick());
179 }
180
186 {
187 return scheduler_->getCurrentTick();
188 }
189
195 void updateElapsedCycles(const Scheduler::Tick elapsed_ticks)
196 {
197 elapsed_cycles_ = getCycle(elapsed_ticks);
198 }
199
204 Cycle elapsedCycles() const
205 {
206 return elapsed_cycles_;
207 }
208
214 Scheduler::Tick getTick(const Cycle& cycle) const
215 {
216 return cycle * period_;
217 }
218
224 Scheduler::Tick getTick(const double& cycle) const
225 {
226 return static_cast<Scheduler::Tick>(cycle * static_cast<double>(period_));
227 }
228
234 Scheduler::Tick getAbsoluteTick(const Cycle& abs_cycle) const
235 {
236 return ((abs_cycle - 1) * period_);
237 }
238
247 bool isPosedge() const
248 {
249 return ((scheduler_->getCurrentTick() % period_) == 0);
250 }
251
253 explicit operator std::string() const
254 {
255 std::stringstream ss;
256 ss << "<Clock " << getName()
257 << " period=" << period_;
258 if (frequency_mhz_ != 0.0) {
259 ss << " freq=" << frequency_mhz_;
260 }
261 if (parent_ != nullptr) {
262 ss << " (" << std::string(parent_ratio_)
263 << " to " << parent_->getName() << ")";
264 }
265 ss << ">";
266 return ss.str();
267 }
268
270 void print(std::ostream& os) const
271 {
272 os << "Clock(" << getName() << "):" << std::endl;
273 if (parent_ != nullptr) {
274 os << "\tRatio to Clock(" << parent_->getName() << "): "
275 << parent_ratio_ << std::endl;
276 } else {
277 os << "\tROOT Clock" << std::endl;
278 }
279 os << "\tRatio to ROOT: " << root_ratio_ << std::endl
280 << "\tPeriod: " << period_ << std::endl;
281 if (frequency_mhz_ != 0.0) {
282 os << "\tFrequency: " << frequency_mhz_ << std::endl;
283 }
284 os << std::endl;
285 }
286
291 simdb::DatabaseID serializeTo(const simdb::ObjectManager & sim_db) const;
292
293 // Overload of TreeNode::stringize
294 virtual std::string stringize(bool pretty=false) const override {
295 (void) pretty;
296 return (std::string)*this;
297 }
298
303 {
304 sparta_assert(scheduler_); // should be guaranteed by constructor
305 return scheduler_;
306 }
307
311
316 return cycles_roctr_;
317 }
318
323 return cycles_roctr_;
324 }
325
328
329 private:
330 Handle parent_;
331 Scheduler * scheduler_ = nullptr;
332 RefList children_;
333 utils::Rational<uint32_t> parent_ratio_ = 1;
334 utils::Rational<uint32_t> root_ratio_ = 1;
335 Period period_ = 1;
336 StatisticSet sset_ = {this};
337 const double frequency_mhz_ = 0.0;
338 Cycle elapsed_cycles_ = 0;
339
340 class CurrentCycleCounter : public ReadOnlyCounter {
341 Clock& clk_;
342 public:
343 CurrentCycleCounter(Clock& clk, StatisticSet* parent) :
344 ReadOnlyCounter(parent,
345 "cycles",
346 "Cycle Count of this Clock",
347 Counter::COUNT_NORMAL),
348 clk_(clk)
349 {
350 // Needed for time calculation down the road in
351 // StatisticInstance
352 this->setClock(&clk_);
353 }
354
355 counter_type get() const override {
356 return clk_.currentCycle();
357 }
358 } cycles_roctr_ = {*this, &sset_};
359
360 // Persist clock hierarchy in the provided database,
361 // recursing down through child nodes as necessary.
362 // Output argument 'db_ids' tracks database record
363 // ID's for each sparta::Clock that was written to
364 // the clock hierarchy table. The map keys are
365 // sparta::Clock 'this' pointers.
366 void recursSerializeToTable_(
367 simdb::TableRef & clock_tbl,
368 const simdb::DatabaseID parent_clk_id,
369 std::map<const Clock*, simdb::DatabaseID> & db_ids) const;
370 };
371
372 inline std::ostream& operator<<(std::ostream& os, const sparta::Clock& clk)
373 {
374 clk.print(os);
375 return os;
376 }
377} // namespace sparta
378
379
380namespace sparta
381{
382
383/*
384 * Return the delay, in ticks, incurred when crossing a clock boundary
385 *
386 * \param src_delay The sender's delay (in ticks) to schedule from "now"
387 * \param src_clk The sender's clock
388 * \param dst_delay The receiver's delay (in ticks) to schedule from "now"
389 * \param dst_clk The receiver's clock
390 * \note both clocks must be on the same scheduler. See
391 * sparta::Clock::getScheduler
392 *
393 * \return Relative scheduler tick that the crossing would incur
394 */
395inline Scheduler::Tick calculateClockCrossingDelay(Scheduler::Tick src_delay, const Clock * src_clk,
396 Scheduler::Tick dst_delay, const Clock * dst_clk)
397{
398 sparta_assert(src_clk, "calculateClockCrossingDelay requires a non-null src_clk");
399 sparta_assert(dst_clk, "calculateClockCrossingDelay requires a non-null dst_clk");
400 sparta_assert(src_clk->getScheduler() == dst_clk->getScheduler(),
401 "calculateClockCrossingDelay requires src_clk and dst_clk to operate on "
402 "the same scheduler. src = " << src_clk->getScheduler() << " and dst = "
403 << dst_clk->getScheduler());
404 auto scheduler = src_clk->getScheduler();
405 sparta_assert(scheduler,
406 "calculateClockCrossingDelay requires src_clk (" << *src_clk << ") to "
407 "have a non-null scheduler");
408
409 sparta::Scheduler::Tick current_tick = scheduler->getCurrentTick();
410 sparta::Scheduler::Tick num_delay_ticks = 0;
411
412 // 1. Snap to source positive edge (assert match)
413 // 2. Add source delay in source clock
414 // 3. Add destination delay in source clock
415 // 4. Snap to destination positive edge
416
417 // Note: A snap to destination positive edge between source delay and
418 // destination delay is not required. If dst_delay is integer, it has
419 // no effect. If dst_delay is float, it acts as implicit round up.
420 // Similarly, a snap to source positive edge would have the same effect
421 // of being benign or rounding up the src_delay.
422
423 sparta::Scheduler::Tick last_src_clk_posedge_tick =
424 current_tick / src_clk->getPeriod() * src_clk->getPeriod();
425 sparta_assert(last_src_clk_posedge_tick == current_tick);
426
427 num_delay_ticks += src_delay + dst_delay;
428
429 sparta::Scheduler::Tick raw_event_arrival_tick = (current_tick + num_delay_ticks);
430 sparta::Scheduler::Tick raw_dst_clk_posedge_tick =
431 raw_event_arrival_tick / dst_clk->getPeriod() * dst_clk->getPeriod();
432 if (raw_event_arrival_tick != raw_dst_clk_posedge_tick) {
433 sparta_assert(raw_event_arrival_tick > raw_dst_clk_posedge_tick);
434 num_delay_ticks += dst_clk->getPeriod() - (raw_event_arrival_tick - raw_dst_clk_posedge_tick);
435 }
436
437 return num_delay_ticks;
438}
439
440/*
441 * Return the delay, in ticks, incurred when crossing a clock boundary in
442 * the reverse direction
443 *
444 * \param dst_arrival_tick The tick in the future on which the receiver receives the data
445 * \param src_delay The sender's delay (in ticks) to schedule from "now"
446 * \param src_clk The sender's clock
447 * \param dst_delay The receiver's delay (in ticks) to schedule from "now"
448 * \param dst_clk The receiver's clock
449 * \note both clocks must be on the same scheduler. See
450 * sparta::Clock::getScheduler
451 *
452 * \return Relative scheduler tick that the reverse crossing would incur
453 */
454inline Scheduler::Tick calculateReverseClockCrossingDelay(Scheduler::Tick dst_arrival_tick,
455 Scheduler::Tick src_delay, const Clock * src_clk,
456 Scheduler::Tick dst_delay, const Clock * dst_clk)
457{
458 sparta_assert(src_clk, "calculateReverseClockCrossingDelay requires a non-null src_clk");
459 sparta_assert(dst_clk, "calculateReverseClockCrossingDelay requires a non-null dst_clk");
460 sparta_assert(src_clk->getScheduler() == dst_clk->getScheduler(),
461 "calculateReverseClockCrossingDelay requires src_clk and dst_clk to operate on "
462 "the same scheduler. src = " << src_clk->getScheduler() << " and dst = "
463 << dst_clk->getScheduler());
464 auto scheduler = src_clk->getScheduler();
465 sparta_assert(scheduler,
466 "calculateReverseClockCrossingDelay requires src_clk (" << *src_clk << ") to "
467 "have a non-null scheduler");
468
469 sparta::Scheduler::Tick relative_ticks_before_arrival = 0;
470
471 // 1. Snap to destination positive edge (assert match)
472 sparta::Scheduler::Tick raw_dst_clk_posedge_tick =
473 dst_arrival_tick / dst_clk->getPeriod() * dst_clk->getPeriod();
474 sparta_assert(dst_arrival_tick == raw_dst_clk_posedge_tick);
475
476 // 2. Subtract source delay in source clock
477 // 3. Subtract destination delay in source clock
478 relative_ticks_before_arrival += src_delay + dst_delay;
479 sparta::Scheduler::Tick raw_event_sent_tick = (dst_arrival_tick - relative_ticks_before_arrival);
480
481 // 4. Snap to previous source positive edge
482 sparta::Scheduler::Tick raw_src_clk_posedge_tick =
483 raw_event_sent_tick / src_clk->getPeriod() * src_clk->getPeriod();
484
485 if (raw_event_sent_tick != raw_src_clk_posedge_tick) {
486 sparta_assert(raw_event_sent_tick > raw_src_clk_posedge_tick);
487 relative_ticks_before_arrival += (raw_event_sent_tick - raw_src_clk_posedge_tick);
488 }
489
490 return relative_ticks_before_arrival;
491}
492
493
494} // namespace sparta
495
496
497#define SPARTA_CLOCK_BODY \
498 namespace sparta { \
499 bool Clock::normalized_ = false; \
500 }
A simple time-based, event precedence based scheduler.
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
Exception class for all of Sparta.
File that defines the StatisticSet class.
Basic Node framework in sparta device tree composite pattern.
A representation of simulated time.
Definition Clock.hpp:51
Cycle currentCycle() const
Get the current cycle (uses current tick from the Scheduler)
Definition Clock.hpp:176
utils::Rational< uint32_t > getRatio() const
Get the clock ratio.
Definition Clock.hpp:120
Scheduler::Tick currentTick() const
Get the current scheduler tick.
Definition Clock.hpp:185
virtual std::string stringize(bool pretty=false) const override
Create a string representation of this node.
Definition Clock.hpp:294
void associate(const Handle &parent)
Associate this clock with another clock.
Clock(const std::string &name, const Handle &parent_clk, const uint32_t &p_rat=1, const uint32_t &c_rat=1)
Construct a named clock with a clock parent and a clock relative to that parent.
Cycle elapsedCycles() const
Return the total elapsed cycles from this Clocks POV.
Definition Clock.hpp:204
void setPeriod(uint32_t norm)
Set the period of this clock.
Definition Clock.hpp:145
Clock(const std::string &name, const Handle &parent, double frequency_mhz)
Construct with a frequency.
Scheduler::Tick getTick(const double &cycle) const
Return the tick corresponding to the given cycle.
Definition Clock.hpp:224
Clock(const std::string &name, Scheduler *scheduler)
Construct a clock.
Clock(RootTreeNode *parent_root, const std::string &name, Scheduler *scheduler)
Construct a named clock with a RootTreeNode as its parent. This will effeclivelly allow this tree (an...
simdb::DatabaseID serializeTo(const simdb::ObjectManager &sim_db) const
void updateElapsedCycles(const Scheduler::Tick elapsed_ticks)
Update the elapsed_cycles internal value given the number of ticks.
Definition Clock.hpp:195
void print(std::ostream &os) const
Used for printing the clock information.
Definition Clock.hpp:270
const ReadOnlyCounter & getCyclesROCounter() const
Returns a counter holding the cycle count of this clock.
Definition Clock.hpp:322
Period getPeriod() const
Returns the period of this clock in scheduler ticks.
Definition Clock.hpp:156
ReadOnlyCounter & getCyclesROCounter()
Returns a counter holding the cycle count of this clock.
Definition Clock.hpp:315
double getFrequencyMhz() const
Get the clock frequency.
Definition Clock.hpp:115
Scheduler * getScheduler() const
Definition Clock.hpp:302
void setRatio(const uint32_t &p_rat=1, const uint32_t &c_rat=1)
Set the ratio of the clock.
Scheduler::Tick getAbsoluteTick(const Cycle &abs_cycle) const
Convert the given absolute cycle number into the corresponding absolute tick number.
Definition Clock.hpp:234
Scheduler::Tick getTick(const Cycle &cycle) const
Return the tick corresponding to the given cycle.
Definition Clock.hpp:214
~Clock()
Destroy this Clock (deregisters from the Scheduler)
uint32_t calcNorm(uint32_t partial_norm=1)
Calculate the norm.
Definition Clock.hpp:126
bool isPosedge() const
Return true if the current tick aligns with a positive edge of this Clock.
Definition Clock.hpp:247
Cycle getCycle(const Scheduler::Tick &tick) const
Given the tick, convert to a Clock::Cycle.
Definition Clock.hpp:166
uint64_t counter_type
Counter value type.
@ COUNT_NORMAL
Counter counts the number of times something happens like one would expect. This is a weakly monotoni...
virtual bool isFinalized() const
Is this node (and thus the entire tree above it) "finalized".
Represents a non-writable and non-observable counter with a very similar interface to sparta::Counter...
TreeNode which represents the root ("top") of a device tree.
A class that lets you schedule events now and in the future.
Tick getCurrentTick() const noexcept
The current tick the Scheduler is working on or just finished.
uint64_t Tick
Typedef for our unit of time.
Set of StatisticDef and CounterBase-derived objects for visiblility through a sparta Tree.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
virtual void setClock(const Clock *clk)
Assigns a clock to this node. This clock will then be accessed by any descendant which has no assigne...
const std::string & getName() const override
Gets the name of this node.
Class to represent a Rational number.
Definition Rational.hpp:21
Macros for handling exponential backoff.
std::ostream & operator<<(std::ostream &o, const SimulationInfo &info)
ostream insertion operator for SimulationInfo