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
16#include "sparta/simulation/TreeNodePrivateAttorney.hpp"
22#include "sparta/events/GlobalOrderingPoint.hpp"
24
25namespace sparta::app{
26 class Simulation;
27 extern void onPipelineCollectionShutdown(Simulation* sim);
28}
29
30namespace sparta{
31namespace collection
32{
67 {
68 class CollectablesByClock
69 {
70 public:
71 CollectablesByClock(const Clock * clk,
72 SchedulingPhase collection_phase) :
73 ev_set_(nullptr)
74 {
75 switch(collection_phase)
76 {
77 case SchedulingPhase::Trigger:
79 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_trigger",
80 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
81 break;
84 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_update",
85 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
86 break;
89 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_portupdate",
90 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
91 break;
94 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_flush",
95 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
96 break;
99 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_collection",
100 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
101 break;
104 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_tick",
105 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
106 break;
109 (&ev_set_, sparta::notNull(clk)->getName() + "_auto_collection_event_posttick",
110 CREATE_SPARTA_HANDLER(CollectablesByClock, performCollection), 1));
111 break;
112 case SchedulingPhase::__last_scheduling_phase:
113 sparta_assert(!"Should not have gotten here");
114 break;
115 // NO DEFAULT! Allows for compiler errors if the enum
116 // class is updated.
117 }
118 ev_collect_->setScheduleableClock(clk);
119 ev_collect_->setScheduler(clk->getScheduler());
120 ev_collect_->setContinuing(false);
121 }
122
123 void enable(CollectableTreeNode * ctn) {
124 enabled_ctns_.insert(ctn);
125 // Schedule collect event in the next cycle in case
126 // this is called in an unavailable pphase.
127 ev_collect_->schedule(sparta::Clock::Cycle(1));
128 }
129
130 void disable(CollectableTreeNode * ctn) {
131 enabled_ctns_.erase(ctn);
132 }
133
134 bool anyCollected() const {
135 return !enabled_ctns_.empty();
136 }
137
138 void performCollection() {
139 // std::for_each(enabled_ctns_.begin(),
140 // enabled_ctns_.end(), [&](CollectableTreeNode * it) {
141 // std::cout << it->getName() << " " << ev_collect_.getClock()->currentCycle() << std::endl;
142 // });
143
144 for(auto & ctn : enabled_ctns_) {
145 if(ctn->isCollected()) {
146 ctn->collect();
147 }
148 }
149 if(!enabled_ctns_.empty()) {
150 ev_collect_->schedule();
151 }
152 }
153
154 void print() {
155 for(auto ctn : enabled_ctns_) {
156 std::cout << '\t' << ctn->getName() << std::endl;
157 }
158 }
159 private:
160 EventSet ev_set_;
161
162 std::unique_ptr<sparta::Scheduleable> ev_collect_;
163 std::set<CollectableTreeNode*> enabled_ctns_;
164 };
165
166 // A map of the clock pointer and the structures that
167 // represent collectables on that clock.
168 std::map<const sparta::Clock *,
169 std::array<std::unique_ptr<CollectablesByClock>,
170 sparta::NUM_SCHEDULING_PHASES>> clock_ctn_map_;
171
172 // Registered collectables
173 std::set<CollectableTreeNode*> registered_collectables_;
174
175 // Simulation from the root node. This is used to ensure the SimDB
176 // pipeline is flushed on simulator exceptions, any newly-seen enum
177 // values get stringified into the DB, ensure newly-seen std::string
178 // IDs in the TinyStrings table get written to the DB, etc.
179 //
180 // Without proper teardown, the database blobs could be unparseable
181 // for the collected data that just occurred prior to the exception.
182 // Either that or there is just missing data that should be there.
183 app::Simulation* simulation_ = nullptr;
184
185 public:
186
214 PipelineCollector(const std::string& filepath, Scheduler::Tick heartbeat_interval,
215 const sparta::Clock* root_clk, const sparta::TreeNode* root,
216 Scheduler* scheduler=nullptr) :
217 Collector("PipelineCollector"),
218 scheduler_(scheduler != nullptr ? scheduler : root_clk->getScheduler())
219 {
220 // Sanity check - pipeline collection cannot occur without a scheduler
221 sparta_assert(scheduler_);
222
223 sparta_assert(root != nullptr, "Pipeline Collection will not be able to create location file because it was passed a nullptr root treenode.");
224 sparta_assert(root->isFinalized(), "Pipeline collection cannot be constructed until the sparta tree has been finalized.");
225 // Assert that we got valid pointers necessary for pipeline collection.
226 sparta_assert(root_clk != nullptr, "Cannot construct PipelineCollector because root clock is a nullptr");
227 sparta_assert(scheduler_->isFinalized() == false, "Pipeline Collection cannot be instantiated after scheduler finalization -- it creates events");
228
229 // Initialize the clock/collectable map and find the fastest clock
230 const sparta::Clock* fastest_clk = nullptr;
231 std::function<void (const sparta::Clock*)> addClks;
232 addClks = [&addClks, this, &fastest_clk] (const sparta::Clock* clk)
233 {
234 if(clk != nullptr){
235 auto & u_p = clock_ctn_map_[clk];
236 for(uint32_t i = 0; i < NUM_SCHEDULING_PHASES; ++i) {
237 u_p[i].reset(new CollectablesByClock(clk, static_cast<SchedulingPhase>(i)));
238 }
240 const sparta::Clock* child_clk = dynamic_cast<const sparta::Clock*>(child);
241 if(child_clk){
242 auto clk_period = child_clk->getPeriod();
243 // If this clock has a non-1 period (i.e. not the root clock)
244 // AND there is either
245 // (A) no fastest clock yet
246 // or
247 // (B) this clock is a higher frequency than the fastest clock.
248 // then choose this as the fastest
249 if(clk_period != 1 && (!fastest_clk || (clk_period < fastest_clk->getPeriod()))) {
250 fastest_clk = child_clk;
251 }
252 addClks(child_clk);
253 }
254 }
255 }
256 };
257 addClks(root_clk);
258 if(fastest_clk == nullptr){
259 fastest_clk = root_clk;
260 }
261
262 // A multiple to multiply against the fastest clock when no heartbeat was set.
265 static const uint32_t heartbeat_multiplier = 200;
266 // round heartbeat using a multiple of the fastest clock if one was not set.
267 if (heartbeat_interval == 0)
268 {
269 sparta_assert(fastest_clk->getPeriod() != 0);
270 heartbeat_interval = fastest_clk->getPeriod() * heartbeat_multiplier; //round up to the nearest multiple of 100
271 heartbeat_interval = heartbeat_interval + (100 - (heartbeat_interval % 100));
272 // pipeViewer requires that intervals be a multiple of 100.
273 sparta_assert(heartbeat_interval % 100 == 0)
274 }
275
276 // Initialize some values.
277 filepath_ = filepath;
278 root_clk_ = root_clk;
279 }
280
282 // XXX This is a little goofy looking. Should we make sparta_abort that takes conditional
283 // be sparta_abort_unless()?
284 sparta_abort(collection_active_ != true,
285 "The PipelineCollector was not torn down properly. Before "
286 "tearing down the simulation tree, you must call "
287 "destroy() on the collector");
288 }
289
296 void destroy()
297 {
298 if(collection_active_) {
299 for(auto & ctn : registered_collectables_) {
300 if(ctn->isCollected()) {
301 ctn->closeRecord(true); // set true for simulation termination
302 }
303 }
304 }
305 registered_collectables_.clear();
306 collection_active_ = false;
307 onPipelineCollectionShutdown(simulation_);
308 }
309
310 void reactivate(const std::string& filepath)
311 {
312 sparta_assert(!collection_active_,
313 "You can only reactivate the PipelineCollector after you destroy it");
314 filepath_ = filepath;
315 }
316
330 {
331 // Recursively collect the start node and children
332 std::function<void (sparta::TreeNode* starting_node)> recursiveCollect;
333 recursiveCollect = [&recursiveCollect, this] (sparta::TreeNode* starting_node)
334 {
335 // First turn on this node if it's actually a CollectableTreeNode
336 CollectableTreeNode* c_node = dynamic_cast<CollectableTreeNode*>(starting_node);
337 if(c_node != nullptr) {
338 c_node->startCollecting(this);
339 if(!c_node->isIterableCollectorBin()) {
340 registered_collectables_.insert(c_node);
341 }
342 }
343
344 // Recursive step. Go through the children and turn them on as well.
346 {
347 recursiveCollect(node);
348 }
349 };
350
351 recursiveCollect(starting_node);
352 }
353
360 void stopCollection(sparta::TreeNode* starting_node)
361 {
362 std::function<void (sparta::TreeNode* starting_node)> recursiveStopCollect;
363 recursiveStopCollect = [&recursiveStopCollect, this] (sparta::TreeNode* starting_node) {
364 // First turn off this node if it's actually a CollectableTreeNode
365 CollectableTreeNode* c_node = dynamic_cast<CollectableTreeNode*>(starting_node);
366 if(c_node != nullptr)
367 {
368 c_node->stopCollecting(this);
369 registered_collectables_.erase(c_node);
370 }
371
372 // Recursive step. Go through the children and turn them on as well.
374 {
375 recursiveStopCollect(node);
376 }
377 };
378 recursiveStopCollect(starting_node);
379
380 collection_active_ = !registered_collectables_.empty();
381 }
382
389 for(auto & col : registered_collectables_) {
390 col->stopCollecting(this);
391 }
392 registered_collectables_.clear();
393 onPipelineCollectionShutdown(simulation_);
394 }
395
409 SchedulingPhase collection_phase = SchedulingPhase::Tick)
410 {
411 auto ccm_pair = clock_ctn_map_.find(ctn->getClock());
412 sparta_assert(ccm_pair != clock_ctn_map_.end());
413 ccm_pair->second[static_cast<uint32_t>(collection_phase)]->enable(ctn);
414 }
415
428 {
429 auto ccm_pair = clock_ctn_map_.find(ctn->getClock());
430 sparta_assert(ccm_pair != clock_ctn_map_.end());
431 for(auto & u_p : ccm_pair->second) {
432 u_p->disable(ctn);
433 }
434 }
435
446 bool isCollectionActive() const {
447 return collection_active_;
448 }
449
451 const std::string & getFilePath() const {
452 return filepath_;
453 }
454
457 return scheduler_;
458 }
459
460 private:
461
464 const sparta::Clock * root_clk_ = nullptr;
465
467 std::string filepath_;
468
470 Scheduler * scheduler_;
471
473 bool collection_active_ = false;
474 };
475
476}// namespace collection
477}// 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:44
Period getPeriod() const
Returns the period of this clock in scheduler ticks.
Definition Clock.hpp:149
Scheduler * getScheduler() const
Definition Clock.hpp:289
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".
A class that lets you schedule events now and in the future.
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:204
const std::string & getName() const override
Gets the name of this node.
const Clock * getClock() override
Walks up parents (starting with self) until a parent with an associated local clock is found,...
A type of Event that uniquely schedules itself on the schedule within a single time quantum....
Simulator which builds a sparta DeviceTree.
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.
bool isIterableCollectorBin() const
Check if this CollectableTreeNode is a Collectable inside an IterableCollector.
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...
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 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.
Sparta Application framework.
Macros for handling exponential backoff.
T * notNull(T *p)
Ensures that a pointer is not null.
Definition Utils.hpp:235
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.