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 "sparta/statistics/CounterBase.hpp"
31
32namespace sparta
33{
43 class Clock : public TreeNode
44 {
45 public:
46 typedef std::shared_ptr<Clock> Handle;
47 typedef uint32_t Period;
48 typedef uint64_t Cycle;
49 typedef double Frequency;
50
51 private:
52 static bool normalized_;
53 typedef std::list<Clock*> RefList;
54
55 public:
56
62 Clock(const std::string& name, Scheduler *scheduler);
63
74 Clock(RootTreeNode* parent_root, const std::string& name, Scheduler *scheduler);
75
87 Clock(const std::string& name, const Handle& parent_clk,
88 const uint32_t& p_rat = 1, const uint32_t& c_rat = 1);
89
94 Clock(const std::string& name, const Handle& parent, double frequency_mhz);
95
98
100 void associate(const Handle& parent);
101
105 void setRatio(const uint32_t& p_rat = 1, const uint32_t& c_rat = 1);
106
108 double getFrequencyMhz() const {
109 return frequency_mhz_;
110 }
111
114 {
115 return parent_ratio_;
116 }
117
119 uint32_t calcNorm(uint32_t partial_norm = 1)
120 {
121 if (parent_ != nullptr) {
122 root_ratio_ = parent_ratio_.inv() * parent_->root_ratio_;
123 }
124
125 partial_norm = utils::lcm(partial_norm, root_ratio_.getDenominator());
126 for (auto i = children_.begin(); i != children_.end(); ++i) {
127 partial_norm = utils::lcm(partial_norm, (*i)->calcNorm(partial_norm));
128 }
129 return partial_norm;
130 }
131
138 void setPeriod(uint32_t norm)
139 {
141 "Should not be setting period on a sparta::Clock after device tree finalization");
142 period_ = uint32_t(root_ratio_ * norm);
143 }
144
149 Period getPeriod() const
150 {
151 return period_;
152 }
153
159 Cycle getCycle(const Scheduler::Tick& tick) const
160 {
161 return (tick / period_);
162 }
163
169 Cycle currentCycle() const
170 {
171 return getCycle(scheduler_->getCurrentTick());
172 }
173
179 {
180 return scheduler_->getCurrentTick();
181 }
182
188 void updateElapsedCycles(const Scheduler::Tick elapsed_ticks)
189 {
190 elapsed_cycles_ = getCycle(elapsed_ticks);
191 }
192
197 Cycle elapsedCycles() const
198 {
199 return elapsed_cycles_;
200 }
201
207 Scheduler::Tick getTick(const Cycle& cycle) const
208 {
209 return cycle * period_;
210 }
211
217 Scheduler::Tick getTick(const double& cycle) const
218 {
219 return static_cast<Scheduler::Tick>(cycle * static_cast<double>(period_));
220 }
221
227 Scheduler::Tick getAbsoluteTick(const Cycle& abs_cycle) const
228 {
229 return ((abs_cycle - 1) * period_);
230 }
231
240 bool isPosedge() const
241 {
242 return ((scheduler_->getCurrentTick() % period_) == 0);
243 }
244
246 explicit operator std::string() const
247 {
248 std::stringstream ss;
249 ss << "<Clock " << getName()
250 << " period=" << period_;
251 if (frequency_mhz_ != 0.0) {
252 ss << " freq=" << frequency_mhz_;
253 }
254 if (parent_ != nullptr) {
255 ss << " (" << std::string(parent_ratio_)
256 << " to " << parent_->getName() << ")";
257 }
258 ss << ">";
259 return ss.str();
260 }
261
263 void print(std::ostream& os) const
264 {
265 os << "Clock(" << getName() << "):" << std::endl;
266 if (parent_ != nullptr) {
267 os << "\tRatio to Clock(" << parent_->getName() << "): "
268 << parent_ratio_ << std::endl;
269 } else {
270 os << "\tROOT Clock" << std::endl;
271 }
272 os << "\tRatio to ROOT: " << root_ratio_ << std::endl
273 << "\tPeriod: " << period_ << std::endl;
274 if (frequency_mhz_ != 0.0) {
275 os << "\tFrequency: " << frequency_mhz_ << std::endl;
276 }
277 os << std::endl;
278 }
279
280 // Overload of TreeNode::stringize
281 virtual std::string stringize(bool pretty=false) const override {
282 (void) pretty;
283 return (std::string)*this;
284 }
285
290 {
291 sparta_assert(scheduler_); // should be guaranteed by constructor
292 return scheduler_;
293 }
294
298
303 return cycles_roctr_;
304 }
305
310 return cycles_roctr_;
311 }
312
315
316 private:
317 Handle parent_;
318 Scheduler * scheduler_ = nullptr;
319 RefList children_;
320 utils::Rational<uint32_t> parent_ratio_ = 1;
321 utils::Rational<uint32_t> root_ratio_ = 1;
322 Period period_ = 1;
323 StatisticSet sset_ = {this};
324 const double frequency_mhz_ = 0.0;
325 Cycle elapsed_cycles_ = 0;
326
327 class CurrentCycleCounter : public ReadOnlyCounter {
328 Clock& clk_;
329 public:
330 CurrentCycleCounter(Clock& clk, StatisticSet* parent) :
331 ReadOnlyCounter(parent,
332 "cycles",
333 "Cycle Count of this Clock",
334 Counter::COUNT_NORMAL),
335 clk_(clk)
336 {
337 // Needed for time calculation down the road in
338 // StatisticInstance
339 this->setClock(&clk_);
340 }
341
342 counter_type get() const override {
343 return clk_.currentCycle();
344 }
345 } cycles_roctr_ = {*this, &sset_};
346 };
347
348 inline std::ostream& operator<<(std::ostream& os, const sparta::Clock& clk)
349 {
350 clk.print(os);
351 return os;
352 }
353} // namespace sparta
354
355
356namespace sparta
357{
358
359/*
360 * Return the delay, in ticks, incurred when crossing a clock boundary
361 *
362 * \param src_delay The sender's delay (in ticks) to schedule from "now"
363 * \param src_clk The sender's clock
364 * \param dst_delay The receiver's delay (in ticks) to schedule from "now"
365 * \param dst_clk The receiver's clock
366 * \note both clocks must be on the same scheduler. See
367 * sparta::Clock::getScheduler
368 *
369 * \return Relative scheduler tick that the crossing would incur
370 */
371inline Scheduler::Tick calculateClockCrossingDelay(Scheduler::Tick src_delay, const Clock * src_clk,
372 Scheduler::Tick dst_delay, const Clock * dst_clk)
373{
374 sparta_assert(src_clk, "calculateClockCrossingDelay requires a non-null src_clk");
375 sparta_assert(dst_clk, "calculateClockCrossingDelay requires a non-null dst_clk");
376 sparta_assert(src_clk->getScheduler() == dst_clk->getScheduler(),
377 "calculateClockCrossingDelay requires src_clk and dst_clk to operate on "
378 "the same scheduler. src = " << src_clk->getScheduler() << " and dst = "
379 << dst_clk->getScheduler());
380 auto scheduler = src_clk->getScheduler();
381 sparta_assert(scheduler,
382 "calculateClockCrossingDelay requires src_clk (" << *src_clk << ") to "
383 "have a non-null scheduler");
384
385 sparta::Scheduler::Tick current_tick = scheduler->getCurrentTick();
386 sparta::Scheduler::Tick num_delay_ticks = 0;
387
388 // 1. Snap to source positive edge (assert match)
389 // 2. Add source delay in source clock
390 // 3. Add destination delay in source clock
391 // 4. Snap to destination positive edge
392
393 // Note: A snap to destination positive edge between source delay and
394 // destination delay is not required. If dst_delay is integer, it has
395 // no effect. If dst_delay is float, it acts as implicit round up.
396 // Similarly, a snap to source positive edge would have the same effect
397 // of being benign or rounding up the src_delay.
398
399 sparta::Scheduler::Tick last_src_clk_posedge_tick =
400 current_tick / src_clk->getPeriod() * src_clk->getPeriod();
401 sparta_assert(last_src_clk_posedge_tick == current_tick);
402
403 num_delay_ticks += src_delay + dst_delay;
404
405 sparta::Scheduler::Tick raw_event_arrival_tick = (current_tick + num_delay_ticks);
406 sparta::Scheduler::Tick raw_dst_clk_posedge_tick =
407 raw_event_arrival_tick / dst_clk->getPeriod() * dst_clk->getPeriod();
408 if (raw_event_arrival_tick != raw_dst_clk_posedge_tick) {
409 sparta_assert(raw_event_arrival_tick > raw_dst_clk_posedge_tick);
410 num_delay_ticks += dst_clk->getPeriod() - (raw_event_arrival_tick - raw_dst_clk_posedge_tick);
411 }
412
413 return num_delay_ticks;
414}
415
416/*
417 * Return the delay, in ticks, incurred when crossing a clock boundary in
418 * the reverse direction
419 *
420 * \param dst_arrival_tick The tick in the future on which the receiver receives the data
421 * \param src_delay The sender's delay (in ticks) to schedule from "now"
422 * \param src_clk The sender's clock
423 * \param dst_delay The receiver's delay (in ticks) to schedule from "now"
424 * \param dst_clk The receiver's clock
425 * \note both clocks must be on the same scheduler. See
426 * sparta::Clock::getScheduler
427 *
428 * \return Relative scheduler tick that the reverse crossing would incur
429 */
430inline Scheduler::Tick calculateReverseClockCrossingDelay(Scheduler::Tick dst_arrival_tick,
431 Scheduler::Tick src_delay, const Clock * src_clk,
432 Scheduler::Tick dst_delay, const Clock * dst_clk)
433{
434 sparta_assert(src_clk, "calculateReverseClockCrossingDelay requires a non-null src_clk");
435 sparta_assert(dst_clk, "calculateReverseClockCrossingDelay requires a non-null dst_clk");
436 sparta_assert(src_clk->getScheduler() == dst_clk->getScheduler(),
437 "calculateReverseClockCrossingDelay requires src_clk and dst_clk to operate on "
438 "the same scheduler. src = " << src_clk->getScheduler() << " and dst = "
439 << dst_clk->getScheduler());
440 auto scheduler = src_clk->getScheduler();
441 sparta_assert(scheduler,
442 "calculateReverseClockCrossingDelay requires src_clk (" << *src_clk << ") to "
443 "have a non-null scheduler");
444
445 sparta::Scheduler::Tick relative_ticks_before_arrival = 0;
446
447 // 1. Snap to destination positive edge (assert match)
448 sparta::Scheduler::Tick raw_dst_clk_posedge_tick =
449 dst_arrival_tick / dst_clk->getPeriod() * dst_clk->getPeriod();
450 sparta_assert(dst_arrival_tick == raw_dst_clk_posedge_tick);
451
452 // 2. Subtract source delay in source clock
453 // 3. Subtract destination delay in source clock
454 relative_ticks_before_arrival += src_delay + dst_delay;
455 sparta::Scheduler::Tick raw_event_sent_tick = (dst_arrival_tick - relative_ticks_before_arrival);
456
457 // 4. Snap to previous source positive edge
458 sparta::Scheduler::Tick raw_src_clk_posedge_tick =
459 raw_event_sent_tick / src_clk->getPeriod() * src_clk->getPeriod();
460
461 if (raw_event_sent_tick != raw_src_clk_posedge_tick) {
462 sparta_assert(raw_event_sent_tick > raw_src_clk_posedge_tick);
463 relative_ticks_before_arrival += (raw_event_sent_tick - raw_src_clk_posedge_tick);
464 }
465
466 return relative_ticks_before_arrival;
467}
468
469
470} // namespace sparta
471
472
473#define SPARTA_CLOCK_BODY \
474 namespace sparta { \
475 bool Clock::normalized_ = false; \
476 }
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:44
Cycle currentCycle() const
Get the current cycle (uses current tick from the Scheduler)
Definition Clock.hpp:169
utils::Rational< uint32_t > getRatio() const
Get the clock ratio.
Definition Clock.hpp:113
Scheduler::Tick currentTick() const
Get the current scheduler tick.
Definition Clock.hpp:178
virtual std::string stringize(bool pretty=false) const override
Create a string representation of this node.
Definition Clock.hpp:281
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:197
void setPeriod(uint32_t norm)
Set the period of this clock.
Definition Clock.hpp:138
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:217
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...
void updateElapsedCycles(const Scheduler::Tick elapsed_ticks)
Update the elapsed_cycles internal value given the number of ticks.
Definition Clock.hpp:188
void print(std::ostream &os) const
Used for printing the clock information.
Definition Clock.hpp:263
const ReadOnlyCounter & getCyclesROCounter() const
Returns a counter holding the cycle count of this clock.
Definition Clock.hpp:309
Period getPeriod() const
Returns the period of this clock in scheduler ticks.
Definition Clock.hpp:149
ReadOnlyCounter & getCyclesROCounter()
Returns a counter holding the cycle count of this clock.
Definition Clock.hpp:302
double getFrequencyMhz() const
Get the clock frequency.
Definition Clock.hpp:108
Scheduler * getScheduler() const
Definition Clock.hpp:289
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:227
Scheduler::Tick getTick(const Cycle &cycle) const
Return the tick corresponding to the given cycle.
Definition Clock.hpp:207
~Clock()
Destroy this Clock (deregisters from the Scheduler)
uint32_t calcNorm(uint32_t partial_norm=1)
Calculate the norm.
Definition Clock.hpp:119
bool isPosedge() const
Return true if the current tick aligns with a positive edge of this Clock.
Definition Clock.hpp:240
Cycle getCycle(const Scheduler::Tick &tick) const
Given the tick, convert to a Clock::Cycle.
Definition Clock.hpp:159
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...
ReadOnlyCounter(TreeNode *parent, const std::string &name, const std::string &group, TreeNode::group_idx_type group_idx, const std::string &desc, CounterBehavior behave, const counter_type *ref, visibility_t visibility)
ReadOnlyCounter constructor.
TreeNode which represents the root ("top") of a device tree.
A class that lets you schedule events now and in the future.
uint64_t Tick
Typedef for our unit of time.
Tick getCurrentTick() const noexcept
The current tick the Scheduler is working on or just finished.
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