The Sparta Modeling Framework
Loading...
Searching...
No Matches
PipelineCollector.hpp
Go to the documentation of this file.
1// <PipelineCollector.hpp> -*- C++ -*-
2
9#pragma once
10
11#include <set>
12#include <map>
13
21#include "sparta/events/GlobalOrderingPoint.hpp"
23
24#include "sparta/pipeViewer/Outputter.hpp"
25#include "sparta/pipeViewer/ClockFileWriter.hpp"
26#include "sparta/pipeViewer/LocationFileWriter.hpp"
27#include "sparta/simulation/TreeNodePrivateAttorney.hpp"
28
29namespace sparta{
30namespace collection
31{
66 {
67 class CollectablesByClock
68 {
69 public:
70 CollectablesByClock(const Clock * clk,
71 SchedulingPhase collection_phase) :
72 ev_set_(nullptr)
73 {
74 switch(collection_phase)
75 {
76 case SchedulingPhase::Trigger:
78 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_trigger",
79 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
80 break;
83 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_update",
84 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
85 break;
88 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_portupdate",
89 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
90 break;
93 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_flush",
94 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
95 break;
98 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_collection",
99 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
100 break;
103 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_tick",
104 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
105 break;
108 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_posttick",
109 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
110 break;
111 case SchedulingPhase::__last_scheduling_phase:
112 sparta_assert(!"Should not have gotten here");
113 break;
114 // NO DEFAULT! Allows for compiler errors if the enum
115 // class is updated.
116 }
117 ev_collect_->setScheduleableClock(clk);
118 ev_collect_->setScheduler(clk->getScheduler());
119 ev_collect_->setContinuing(false);
120 }
121
122 void enable(CollectableTreeNode * ctn) {
123 enabled_ctns_.insert(ctn);
124 // Schedule collect event in the next cycle in case
125 // this is called in an unavailable pphase.
126 ev_collect_->schedule(sparta::Clock::Cycle(1));
127 }
128
129 void disable(CollectableTreeNode * ctn) {
130 enabled_ctns_.erase(ctn);
131 }
132
133 bool anyCollected() const {
134 return !enabled_ctns_.empty();
135 }
136
137 void performCollection() {
138 // std::for_each(enabled_ctns_.begin(),
139 // enabled_ctns_.end(), [&](CollectableTreeNode * it) {
140 // std::cout << it->getName() << " " << ev_collect_.getClock()->currentCycle() << std::endl;
141 // });
142
143 for(auto & ctn : enabled_ctns_) {
144 if(ctn->isCollected()) {
145 ctn->collect();
146 }
147 }
148 if(!enabled_ctns_.empty()) {
149 ev_collect_->schedule();
150 }
151 }
152
153 void print() {
154 for(auto ctn : enabled_ctns_) {
155 std::cout << '\t' << ctn->getName() << std::endl;
156 }
157 }
158 private:
159 EventSet ev_set_;
160
161 std::unique_ptr<sparta::Scheduleable> ev_collect_;
162 std::set<CollectableTreeNode*> enabled_ctns_;
163 };
164
165 // A map of the clock pointer and the structures that
166 // represent collectables on that clock.
167 std::map<const sparta::Clock *,
168 std::array<std::unique_ptr<CollectablesByClock>,
169 sparta::NUM_SCHEDULING_PHASES>> clock_ctn_map_;
170
171 // Registered collectables
172 std::set<CollectableTreeNode*> registered_collectables_;
173
174 public:
175
203 PipelineCollector(const std::string& filepath, Scheduler::Tick heartbeat_interval,
204 const sparta::Clock* root_clk, const sparta::TreeNode* root,
205 Scheduler* scheduler=nullptr) :
206 Collector("PipelineCollector"),
207 scheduler_(scheduler != nullptr ? scheduler : root_clk->getScheduler()),
208 collector_events_(nullptr),
209 ev_heartbeat_(&collector_events_, Collector::getName() + "_heartbeat_event",
210 CREATE_SPARTA_HANDLER(PipelineCollector, performHeartBeat_), 0)
211 {
212 // Sanity check - pipeline collection cannot occur without a scheduler
213 sparta_assert(scheduler_);
214
215 ev_heartbeat_.setScheduleableClock(root_clk);
216 ev_heartbeat_.setScheduler(scheduler_);
217
218 // We need to reverse the dependency order for the PostTick GOP and ev_heartbeat_ so that every other
219 // event happens *before* ev_heartbeat_.
220 // Doing this ensures that we don't accidentally mark a 1-cycle transaction as continued.
221 DAG* dag = scheduler_->getDAG(); // Get a handle to the DAG
222 sparta_assert(dag);
223 Vertex* post_tick_gop = dag->getGOPoint("PostTick"); // Get a handle to the PostTick GOP
224 sparta_assert(post_tick_gop);
225
226 dag->unlink(ev_heartbeat_.getVertex(), post_tick_gop); // Undo the ev_heartbeat_ >> heartbeat_gop link
227 post_tick_gop->precedes(ev_heartbeat_); // Set post_tick_gop >> heartbeat_gop
228
229 sparta_assert(root != nullptr, "Pipeline Collection will not be able to create location file because it was passed a nullptr root treenode.");
230 sparta_assert(root->isFinalized(), "Pipeline collection cannot be constructed until the sparta tree has been finalized.");
231 // Assert that we got valid pointers necessary for pipeline collection.
232 sparta_assert(root_clk != nullptr, "Cannot construct PipelineCollector because root clock is a nullptr");
233 sparta_assert(scheduler_->isFinalized() == false, "Pipeline Collection cannot be instantiated after scheduler finalization -- it creates events");
234
235 // Initialize the clock/collectable map and find the fastest clock
236 const sparta::Clock* fastest_clk = nullptr;
237 std::function<void (const sparta::Clock*)> addClks;
238 addClks = [&addClks, this, &fastest_clk] (const sparta::Clock* clk)
239 {
240 if(clk != nullptr){
241 auto & u_p = clock_ctn_map_[clk];
242 for(uint32_t i = 0; i < NUM_SCHEDULING_PHASES; ++i) {
243 u_p[i].reset(new CollectablesByClock(clk, static_cast<SchedulingPhase>(i)));
244 }
246 const sparta::Clock* child_clk = dynamic_cast<const sparta::Clock*>(child);
247 if(child_clk){
248 auto clk_period = child_clk->getPeriod();
249 // If this clock has a non-1 period (i.e. not the root clock)
250 // AND there is either
251 // (A) no fastest clock yet
252 // or
253 // (B) this clock is a higher frequency than the fastest clock.
254 // then choose this as the fastest
255 if(clk_period != 1 && (!fastest_clk || (clk_period < fastest_clk->getPeriod()))) {
256 fastest_clk = child_clk;
257 }
258 addClks(child_clk);
259 }
260 }
261 }
262 };
263 addClks(root_clk);
264 if(fastest_clk == nullptr){
265 fastest_clk = root_clk;
266 }
267
268 // A multiple to multiply against the fastest clock when no heartbeat was set.
271 static const uint32_t heartbeat_multiplier = 200;
272 // round heartbeat using a multiple of the fastest clock if one was not set.
273 if (heartbeat_interval == 0)
274 {
275 sparta_assert(fastest_clk->getPeriod() != 0);
276 heartbeat_interval = fastest_clk->getPeriod() * heartbeat_multiplier; //round up to the nearest multiple of 100
277 heartbeat_interval = heartbeat_interval + (100 - (heartbeat_interval % 100));
278 // pipeViewer requires that intervals be a multiple of 100.
279 sparta_assert(heartbeat_interval % 100 == 0)
280 }
281
282 // We are gonna subtract one from the heartbeat_interval
283 // later.. Better be greater than one.
284 sparta_assert(heartbeat_interval > 1);
285 // Initialize some values.
286 filepath_ = filepath;
287 heartbeat_interval_ = heartbeat_interval;
288 closing_time_ = heartbeat_interval;
289 root_clk_ = root_clk;
290
291 ev_heartbeat_.setContinuing(false); // This event does not keep simulation going
292 }
293
295 // XXX This is a little goofy looking. Should we make sparta_abort that takes conditional
296 // be sparta_abort_unless()?
297 sparta_abort(collection_active_ != true,
298 "The PipelineCollector was not torn down properly. Before "
299 "tearing down the simulation tree, you must call "
300 "destroy() on the collector");
301 }
302
309 void destroy()
310 {
311 if(collection_active_) {
312 sparta_assert(writer_ != nullptr, "Somehow collection is active, but we have a null writer");
313 for(auto & ctn : registered_collectables_) {
314 if(ctn->isCollected()) {
315 ctn->closeRecord(true); // set true for simulation termination
316 }
317 }
318 }
319 registered_collectables_.clear();
320 writer_.reset();
321 collection_active_ = false;
322 }
323
324 void reactivate(const std::string& filepath)
325 {
326 sparta_assert(!collection_active_,
327 "You can only reactivate the PipelineCollector after you destroy it");
328 filepath_ = filepath;
329 }
330
344 {
345 if(collection_active_ == false)
346 {
347 // Create the outputter used for writing transactions to disk.
348 writer_.reset(new pipeViewer::Outputter(filepath_, heartbeat_interval_));
349
350 // We need to write an index on the start BEFORE any transactions have been written.
351 writer_->writeIndex();
352
353 // Write the clock information out
354 writeClockFile_();
355
356 // Open the locations file
357 location_writer_.reset(new pipeViewer::LocationFileWriter(filepath_));
358
359 // The reader needs heartbeat indexes up to the current
360 // collection point. This can happen on delayed pipeline
361 // collection. Start the heartbeats at the first interval
362 // as the Outputter class handles hb 0
363 last_heartbeat_ = 0;
364 const uint64_t num_hb = scheduler_->getCurrentTick()/heartbeat_interval_;
365 uint64_t cnt = 0;
366 while(cnt != num_hb) {
367 // write an index
368 writer_->writeIndex();
369 last_heartbeat_ += heartbeat_interval_;
370 ++cnt;
371 }
372
373 // Schedule a heartbeat at the next interval, offset from
374 // the current tick. For example, if the scheduler is at
375 // 6,600,456, then we want to schedule at 7,000,000 if the
376 // interval is 1M and the num_hb is 6
377 ev_heartbeat_.scheduleRelativeTick(((num_hb + 1) * heartbeat_interval_) -
378 scheduler_->getCurrentTick(), scheduler_);
379
380 collection_active_ = true;
381 }
382
383 *(location_writer_.get()) << (*starting_node);
384
385 // Recursively collect the start node and children
386 std::function<void (sparta::TreeNode* starting_node)> recursiveCollect;
387 recursiveCollect = [&recursiveCollect, this] (sparta::TreeNode* starting_node)
388 {
389 // First turn on this node if it's actually a CollectableTreeNode
390 CollectableTreeNode* c_node = dynamic_cast<CollectableTreeNode*>(starting_node);
391 if(c_node != nullptr) {
392 c_node->startCollecting(this);
393 registered_collectables_.insert(c_node);
394 }
395
396 // Recursive step. Go through the children and turn them on as well.
398 {
399 recursiveCollect(node);
400 }
401 };
402
403 recursiveCollect(starting_node);
404 }
405
412 void stopCollection(sparta::TreeNode* starting_node)
413 {
414 std::function<void (sparta::TreeNode* starting_node)> recursiveStopCollect;
415 recursiveStopCollect = [&recursiveStopCollect, this] (sparta::TreeNode* starting_node) {
416 // First turn off this node if it's actually a CollectableTreeNode
417 CollectableTreeNode* c_node = dynamic_cast<CollectableTreeNode*>(starting_node);
418 if(c_node != nullptr)
419 {
420 c_node->stopCollecting(this);
421 registered_collectables_.erase(c_node);
422 }
423
424 // Recursive step. Go through the children and turn them on as well.
426 {
427 recursiveStopCollect(node);
428 }
429 };
430 recursiveStopCollect(starting_node);
431
432 bool still_active = !registered_collectables_.empty();
433 // for(auto & cp : clock_ctn_map_) {
434 // if(cp.second->anyCollected()) {
435 // still_active = true;
436 // break;
437 // }
438 // }
439 collection_active_ = still_active;
440 }
441
448 for(auto & col : registered_collectables_) {
449 col->stopCollecting(this);
450 }
451 registered_collectables_.clear();
452 }
453
467 SchedulingPhase collection_phase = SchedulingPhase::Tick)
468 {
469 auto ccm_pair = clock_ctn_map_.find(ctn->getClock());
470 sparta_assert(ccm_pair != clock_ctn_map_.end());
471 ccm_pair->second[static_cast<uint32_t>(collection_phase)]->enable(ctn);
472 }
473
486 {
487 auto ccm_pair = clock_ctn_map_.find(ctn->getClock());
488 sparta_assert(ccm_pair != clock_ctn_map_.end());
489 for(auto & u_p : ccm_pair->second) {
490 u_p->disable(ctn);
491 }
492 }
493
498 {
499 // make sure we are not going to overflow our int,
500 // if we did overflow our id's are no longer unique!
501 sparta_assert(last_transaction_id_ < (~(uint64_t)0));
502 return ++last_transaction_id_;
503 }
504
510 template<class R_Type>
511 void writeRecord(const R_Type& dat)
512 {
513 sparta_assert(collection_active_, "The pipeline head must be running in order to write a transaction");
514
515 // Make sure it's within the heartbeat window
516 sparta_assert(dat.time_End <= (last_heartbeat_ + heartbeat_interval_));
517 sparta_assert(dat.time_Start >= last_heartbeat_);
518
519
520 // Make sure transactions are exclusive.
521 // transaction [4999-5000] should be written BEFORE the index is written for transactions
522 // starting at 5000
523 sparta_assert(closing_time_ < heartbeat_interval_ // Ignore first heartbeat
524 || dat.time_Start >= closing_time_ - heartbeat_interval_,
525 "Attempted to write a pipeout record with exclusive start =("
526 << dat.time_Start << "), less than closing of previous interval"
527 << closing_time_ - heartbeat_interval_ );
528
529 // std::cout << "writing annt. " << "loc: " << dat.location_ID << " start: "
530 // << dat.time_Start << " end: " << dat.time_End
531 // << " parent: " << dat.parent_ID << std::endl;
532
533 writer_->writeTransaction<R_Type>(dat);
534 ++transactions_written_;
535 }
536
541 uint64_t numTransactionsWritten() const
542 {
543 return transactions_written_;
544 }
545
556 bool isCollectionActive() const {
557 return collection_active_;
558 }
559
560 void printMap() {
561 //std::cout << "Printing Map Not Supported" << std::endl;
562 // for(auto & p : clock_ctn_map_) {
563 // std::cout << "\nClock : " << p.first->getName()
564 // << "\nAuto collectables: " << std::endl;
565 // p.second->print();
566 // }
567 }
568
570 const std::string & getFilePath() const {
571 return filepath_;
572 }
573
576 return scheduler_;
577 }
578
579 private:
580
585 void writeClockFile_()
586 {
587 // We only need the ClockFileWriter to exist during the writing of the clock file.
588 // there for it was created on the stack.
589 //std::cout << "Writing Pipeline Collection clock file. " << std::endl;
590 pipeViewer::ClockFileWriter clock_writer(filepath_);
591 clock_writer << (*root_clk_);
592 }
593
597 void performHeartBeat_()
598 {
599 if(collection_active_) {
600 // Close all transactions
601 for(auto & ctn : registered_collectables_) {
602 if(ctn->isCollected()) {
603 ctn->restartRecord();
604 }
605 }
606
607 // write an index
608 writer_->writeIndex();
609
610 // Remember the last time we recorded a heartbeat
611 last_heartbeat_ = scheduler_->getCurrentTick();
612
613 // Schedule another heartbeat
614 ev_heartbeat_.schedule(heartbeat_interval_);
615 }
616 }
617
620 std::unique_ptr<pipeViewer::Outputter> writer_;
621
624 std::unique_ptr<pipeViewer::LocationFileWriter> location_writer_;
625 //sparta::TreeNode* collected_treenode_ = nullptr;
626
629 const sparta::Clock * root_clk_ = nullptr;
630
632 std::string filepath_;
633
636 uint64_t last_transaction_id_ = 0;
637
639 uint64_t transactions_written_ = 0;
640
643 uint64_t heartbeat_interval_ = 0;
644
646 Scheduler::Tick last_heartbeat_ = 0;
647
649 Scheduler * scheduler_;
650
652 EventSet collector_events_;
653 UniqueEvent<SchedulingPhase::PostTick> ev_heartbeat_;
654
656 uint64_t closing_time_ = 0;
657
659 bool collection_active_ = false;
660
661 };
662
663}// namespace collection
664}// namespace sparta
File that defines the Clock class.
define a CollectableTreeNode type TreeNode.
Define a base Collector class.
File that defines the EventSet class.
A simple time-based, event precedence based scheduler.
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_abort(...)
Simple variatic assertion that will print a message to std::cerr and call std::terminate()
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define CREATE_SPARTA_HANDLER(clname, meth)
Basic Node framework in sparta device tree composite pattern.
File that defines the UniqueEvent class.
A representation of simulated time.
Definition Clock.hpp:51
Period getPeriod() const
Returns the period of this clock in scheduler ticks.
Definition Clock.hpp:156
Scheduler * getScheduler() const
Definition Clock.hpp:302
Vertex * getGOPoint(const std::string &label)
Get the named GOP point; create it if not found.
Definition DAG.hpp:177
Set of Events that a unit (or sparta::TreeNode, sparta::Resource) contains and are visible through a ...
Definition EventSet.hpp:26
virtual bool isFinalized() const
Is this node (and thus the entire tree above it) "finalized".
void scheduleRelativeTick(sparta::Scheduler::Tick rel_tick, sparta::Scheduler *scheduler) override final
Schedule at time rel_tick.
void setContinuing(bool continuing)
This event, if continuing == true, will keep the simulation running.
void setScheduler(Scheduler *sched)
Set the Scheduler of this Scheduleable, and set the local vertex_ to a new vertex from the Vertex Fac...
Vertex * getVertex()
get the internal Vertex of this scheduleable
void setScheduleableClock(const Clock *clk)
Set the clock and scheduler of this Scheduleable.
A class that lets you schedule events now and in the future.
DAG * getDAG() const
Get the internal DAG.
Tick getCurrentTick() const noexcept
The current tick the Scheduler is working on or just finished.
uint64_t Tick
Typedef for our unit of time.
bool isFinalized() const noexcept override
Is the scheduler finalized.
static const TreeNode::ChildrenVector & getAllChildren(const TreeNode &node)
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
const Clock * getClock() override
Walks up parents (starting with self) until a parent with an associated local clock is found,...
const std::string & getName() const override
Gets the name of this node.
A type of Event that uniquely schedules itself on the schedule within a single time quantum....
void schedule()
Schedule this event with its pre-set delay using the pre-set Clock.
An abstract type of TreeNode that has virtual calls to start collection on this node,...
void startCollecting(Collector *collector)
Method that tells this treenode that is now running collection.
bool isCollected() const
Determine whether or not this node has collection turned on or off.
void stopCollecting(Collector *collector)
Method that tells this treenode that is now not running collection.
A non-templated base class that all Collectors should inherit from.
Definition Collector.hpp:23
A class that facilitates all universal pipeline collection operations such as outputting finalized re...
uint64_t numTransactionsWritten() const
Return the number of transactions that this singleton has passed to it's output. This is useful for t...
uint64_t getUniqueTransactionId()
Return a unique transaction id using a dummy counter.
void startCollection(sparta::TreeNode *starting_node)
Turn on collection for everything below a TreeNode. Recursively transverse the tree and turn on child...
PipelineCollector(const std::string &filepath, Scheduler::Tick heartbeat_interval, const sparta::Clock *root_clk, const sparta::TreeNode *root, Scheduler *scheduler=nullptr)
Instantiate the collector with required parameters before pipeline collection can occur.
void writeRecord(const R_Type &dat)
Output a finized transaction to our Outputter class.
void removeFromAutoCollection(CollectableTreeNode *ctn)
Remove the given CollectableTreeNode from collection.
bool isCollectionActive() const
Return true if the collector is actively collecting.
void stopCollection()
Stop pipeline collection on only those CollectableTreeNodes that this PipelineCollector was started w...
const std::string & getFilePath() const
void destroy()
Teardown the pipeline collector.
void addToAutoCollection(CollectableTreeNode *ctn, SchedulingPhase collection_phase=SchedulingPhase::Tick)
Add the CollectableTreeNode to auto collection.
void stopCollection(sparta::TreeNode *starting_node)
Stop pipeline collection on only those CollectableTreeNodes given.
Macros for handling exponential backoff.
T * notNull(T *p)
Ensures that a pointer is not null.
Definition Utils.hpp:224
const uint32_t NUM_SCHEDULING_PHASES
The number of phases.
SchedulingPhase
The SchedulingPhases used for events (Tick, Update, PortUpdate, etc)
@ Update
Resources are updated in this phase.
@ PortUpdate
N-cycle Ports are updated in this phase.
@ Tick
Most operations (combinational logic) occurs in this phase.
@ Flush
Phase where flushing of pipelines, etc can occur.
@ PostTick
Operations such as post-tick pipeline collection occur here.
@ Collection
Pipeline collection occurs here.