The Sparta Modeling Framework
Loading...
Searching...
No Matches
ExampleSimulation.cpp
1// <Simulation.cpp> -*- C++ -*-
2
3
4#include <iostream>
5
6
7#include "ExampleSimulation.hpp"
8#include "Core.hpp"
9#include "CPUFactory.hpp"
10
12#include "sparta/utils/TimeManager.hpp"
14#include "sparta/simulation/TreeNodeExtensions.hpp"
15#include "sparta/trigger/ContextCounterTrigger.hpp"
21
22#include "Fetch.hpp"
23#include "Decode.hpp"
24#include "Rename.hpp"
25#include "Dispatch.hpp"
26#include "Execute.hpp"
27#include "LSU.hpp"
28#include "ROB.hpp"
29#include "FlushManager.hpp"
30#include "Preloader.hpp"
31#include "CustomHistogramStats.hpp"
32
33// UPDATE
34#include "BIU.hpp"
35#include "MSS.hpp"
36
37namespace sparta {
38
39 // Example parameter set used to reproduce write-final-config
41 public:
42 IntParameterSet(TreeNode * parent) :
43 ParameterSet(parent),
44 int_param_(new Parameter<uint32_t>(
45 "baz", 0, "Example parameter set to reproduce bug"))
46 {
47 addParameter_(int_param_.get());
48 }
49
50 uint32_t read() const {
51 return int_param_->getValue();
52 }
53
54 private:
55 std::unique_ptr<Parameter<uint32_t>> int_param_;
56 };
57
58 // Dummy node class used together with IntParameterSet to
59 // reproduce write-final-config bug
60 class Baz : public TreeNode {
61 public:
62 Baz(TreeNode* parent,
63 const std::string & desc) :
64 TreeNode(parent, "baz_node", "BazGroup", 0, desc)
65 {
66 baz_.reset(new IntParameterSet(this));
67 }
68
69 void readParams() {
70 std::cout << " Node '" << getLocation()
71 << "' has parameter 'baz' with a value set to "
72 << baz_->read() << std::endl;
73
74 auto ext = getExtension("baz_ext", true);
75 if(ext) {
76 std::cout << "That's the ticket: "
77 << ext->getParameters()->getParameterValueAs<std::string>("ticket_") << std::endl;
78 }
79 }
80
81 private:
82 std::unique_ptr<IntParameterSet> baz_;
83 };
84
85}
86
87class CircleExtensions : public sparta::ExtensionsParamsOnly
88{
89public:
90 void doSomethingElse() const {
91 std::cout << "Invoking a method that is unknown to the sparta::TreeNode object, "
92 "even though 'this' object was created by, and currently owned by, "
93 "a specific tree node.";
94 }
95
96private:
97 // Note: this parameter is NOT in the yaml config file,
98 // but subclasses can provide any parameter type supported
99 // by sparta::Parameter<T> which may be too complicated to
100 // clearly describe using simple yaml syntax
101 std::unique_ptr<sparta::Parameter<double>> degrees_;
102
103 // The base class will clobber together whatever parameter values it
104 // found in the yaml file, and give us a chance to add custom parameters
105 // to the same set
106 void postCreate() override {
107 sparta::ParameterSet * ps = getParameters();
108 degrees_.reset(new sparta::Parameter<double>(
109 "degrees_", 360.0, "Number of degrees in a circle", ps));
110 }
111};
112
113class NeverExplicitlyInstantiated : public sparta::ExtensionsParamsOnly
114{
115private:
116 void postCreate() override {
117 sparta::ParameterSet * ps = getParameters();
118 value_.reset(new sparta::Parameter<int>(
119 "value_", 555, "Dummy value", ps));
120 }
121
122 std::unique_ptr<sparta::Parameter<int>> value_;
123};
124
125class Foobar : public sparta::ExtensionsParamsOnly
126{
127private:
128 void postCreate() override {
129 sparta::ParameterSet * ps = getParameters();
130 extra_.reset(new sparta::Parameter<int>(
131 "extra", 404, "Extra postCreate parameter", ps));
132 }
133
134 std::unique_ptr<sparta::Parameter<int>> extra_;
135};
136
137class CoreExtensions : public sparta::ExtensionsParamsOnly
138{
139public:
140 using EnabledUnits = std::vector<std::vector<std::vector<std::string>>>;
141
142private:
143 void postCreate() override {
144 auto ps = getParameters();
145 enabled_units_.reset(new sparta::Parameter<std::vector<std::vector<std::vector<std::string>>>>(
146 "enabled_units", {}, "Enabled units to test nested vectors", ps));
147 fetch_type_.reset(new sparta::Parameter<std::string>("fetch_type", "dummy", "blah", ps));
148 }
149
150 std::unique_ptr<sparta::Parameter<EnabledUnits>> enabled_units_;
151 std::unique_ptr<sparta::Parameter<std::string>> fetch_type_;
152};
153
154double calculateAverageOfInternalCounters(
155 const std::vector<const sparta::CounterBase*> & counters)
156{
157 double agg = 0;
158 for (const auto & ctr : counters) {
159 agg += ctr->get();
160 }
161 return agg / counters.size();
162}
163
164ExampleSimulator::ExampleSimulator(const std::string& topology,
165 sparta::Scheduler & scheduler,
166 uint32_t num_cores,
167 uint64_t instruction_limit,
168 bool show_factories) :
169 sparta::app::Simulation("sparta_core_example", &scheduler),
170 cpu_topology_(topology),
171 num_cores_(num_cores),
172 instruction_limit_(instruction_limit),
173 show_factories_(show_factories)
174{
175 // Set up the CPU Resource Factory to be available through ResourceTreeNode
177
178 // Set up all node extension factories to be available during the simulation
179 // - This is only needed for parameter sets that also want to add some methods
180 // to their tree node extension, and/or for those that want to extend node
181 // parameter sets with more complicated sparta::Parameter<T> data types
182 addTreeNodeExtensionFactory_("circle", [](){return new CircleExtensions;});
183 addTreeNodeExtensionFactory_("never_explicitly_instantiated", [](){return new NeverExplicitlyInstantiated;});
184 addTreeNodeExtensionFactory_("foobar", [](){return new Foobar;});
185 addTreeNodeExtensionFactory_("core_extensions", [](){return new CoreExtensions;});
186
187 // Initialize example simulation controller
188 controller_.reset(new ExampleSimulator::ExampleController(this));
190
191 // Register a custom calculation method for 'combining' a context counter's
192 // internal counters into one number. In this example simulator, let's just
193 // use an averaging function called "avg" which we can then invoke from report
194 // definition YAML files.
195 sparta::trigger::ContextCounterTrigger::registerContextCounterCalcFunction(
196 "avg", &calculateAverageOfInternalCounters);
197}
198
199void ExampleSimulator::registerStatCalculationFcns_()
200{
201 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, stdev_x3);
202 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_greaterThan2StdDev);
203 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_p_StdDev_mean_p_2StdDev);
204 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_mean_p_StdDev);
205 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_m_StdDev_mean);
206 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_m_2StdDev_mean_m_StdDev);
207 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_lesserThan2StdDev);
208 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, stdev_x3_h);
209 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_greaterThan2StdDev_h);
210 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_p_StdDev_mean_p_2StdDev_h);
211 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_mean_p_StdDev_h);
212 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_m_StdDev_mean_h);
213 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_m_2StdDev_mean_m_StdDev_h);
214 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_lesserThan2StdDev_h);
215}
216
217ExampleSimulator::~ExampleSimulator()
218{
219 getRoot()->enterTeardown(); // Allow deletion of nodes without error now
220 if (on_triggered_notifier_registered_) {
221 getRoot()->DEREGISTER_FOR_NOTIFICATION(
222 onTriggered_, std::string, "sparta_expression_trigger_fired");
223 }
224}
225
227auto ExampleSimulator::getCPUFactory_() -> core_example::CPUFactory*{
228 auto sparta_res_factory = getResourceSet()->getResourceFactory("cpu");
229 auto cpu_factory = dynamic_cast<core_example::CPUFactory*>(sparta_res_factory);
230 return cpu_factory;
231}
232
233void ExampleSimulator::buildTree_()
234{
235 // TREE_BUILDING Phase. See sparta::PhasedObject::TreePhase
236 // Register all the custom stat calculation functions with (cycle)histogram nodes
237 registerStatCalculationFcns_();
238
239 auto cpu_factory = getCPUFactory_();
240
241 // Set the cpu topology that will be built
242 cpu_factory->setTopology(cpu_topology_, num_cores_);
243
244 // Create a single CPU
246 "cpu",
249 "CPU Node",
250 cpu_factory);
251 to_delete_.emplace_back(cpu_tn);
252
253 // Tell the factory to build the resources now
254 cpu_factory->buildTree(getRoot());
255
256 // This is here to verify that we can access an extension during buildTree(),
257 // read its value (increment the read count), and have that read count seen
258 // as "yes this has been read" during finalizeTree().
259 if(auto ext = cpu_tn->getChild("core0")->getExtension("core_extensions", true)){
260 auto ps = ext->getParameters();
261 auto & p = ps->getParameterAs<std::string>("fetch_type");
262 p.getValue();
263 }
264
265 // Print the registered factories
266 if(show_factories_){
267 std::cout << "Registered factories: \n";
268 for(const auto& f : getCPUFactory_()->getResourceNames()){
269 std::cout << "\t" << f << std::endl;
270 }
271 }
272
273 // Validate tree node extensions during tree building
274 for(uint32_t i = 0; i < num_cores_; ++i){
275 const std::string core_loc = "cpu.core" + std::to_string(i);
276 const std::string dispatch_loc = core_loc + ".dispatch";
277 const std::string alu0_loc = core_loc + ".alu0";
278 const std::string alu1_loc = core_loc + ".alu1";
279 const std::string fpu_loc = core_loc + ".fpu";
280
281 // user_data.when_ (dispatch)
282 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(dispatch_loc), "when_", "user_data")) {
283 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
284 return val == "buildTree_";
285 }, "Parameter 'when_' should be 'buildTree_'");
286 }
287
288 // user_data.why_ (dispatch)
289 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(dispatch_loc), "why_", "user_data")) {
290 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
291 return val == "checkAvailability";
292 }, "Parameter 'why_' should be 'checkAvailability'");
293 }
294
295 // square.edges_ (dispatch)
296 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(dispatch_loc), "edges_", "square")) {
297 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
298 return val == "4";
299 }, "Parameter 'edges_' should be '4'");
300 }
301
302 // difficulty.color_ (alu0)
303 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(alu0_loc), "color_", "difficulty")) {
304 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
305 return val == "black";
306 }, "Parameter 'color_' should be 'black'");
307 }
308
309 // difficulty.shape_ (alu0)
310 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(alu0_loc), "shape_", "difficulty")) {
311 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
312 return val == "diamond";
313 }, "Parameter 'shape_' should be 'diamond'");
314 }
315
316 // difficulty.color_ (alu1)
317 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(alu1_loc), "color_", "difficulty")) {
318 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
319 return val == "green";
320 }, "Parameter 'color_' should be 'green'");
321 }
322
323 // difficulty.shape_ (alu1)
324 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(alu1_loc), "shape_", "difficulty")) {
325 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
326 return val == "circle";
327 }, "Parameter 'shape_' should be 'circle'");
328 }
329
330 // circle.color_ (fpu)
331 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(fpu_loc), "color_", "circle")) {
332 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
333 return val == "green";
334 }, "Parameter 'color_' should be 'green'");
335 }
336
337 // circle.shape_ (fpu)
338 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(fpu_loc), "shape_", "circle")) {
339 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
340 return val == "round";
341 }, "Parameter 'shape_' should be 'round'");
342 }
343
344 // circle.degrees_ (fpu)
345 if (auto prm = getExtensionParameter_<double>(getRoot()->getChild(fpu_loc), "degrees_", "circle")) {
346 prm->addDependentValidationCallback([](double & val, const sparta::TreeNode*) -> bool {
347 return val == 360.0;
348 }, "Parameter 'degrees_' should be 360.0");
349 }
350
351 // circle.edges_ (fpu)
352 if (auto prm = getExtensionParameter_<std::string>(getRoot()->getChild(fpu_loc), "edges_", "circle")) {
353 prm->addDependentValidationCallback([](std::string & val, const sparta::TreeNode*) -> bool {
354 return val == "0";
355 }, "Parameter 'edges_' should be '0'");
356 }
357
358 // core_extensions.enabled_units_ (core0)
359 auto core_tn = getRoot()->getChild(core_loc);
360 using NestedVector = typename CoreExtensions::EnabledUnits;
361 if (auto prm = getExtensionParameter_<NestedVector>(core_tn, "enabled_units", "core_extensions")) {
362 prm->addDependentValidationCallback([](NestedVector & actual_vecs, const sparta::TreeNode* node) -> bool {
363 if (actual_vecs.empty()) {
364 return true;
365 }
366
367 // Note that we should read the extension to verify that it matches the incoming
368 // actual_vecs, but also to silence "unread unbound parameter" exceptions.
369 auto ext = node->getExtension("core_extensions");
370 if (!ext) {
371 return false;
372 }
373
374 auto ps = ext->getParameters();
375 if (!ps->hasParameter("enabled_units")) {
376 return false;
377 }
378
379 auto& p = ps->getParameterAs<NestedVector>("enabled_units");
380 if (p.getValue() != actual_vecs) {
381 return false;
382 }
383
384 static NestedVector expected_vecs = {
385 {
386 {"int"},
387 {"int", "div"},
388 {"int", "mul"},
389 {"int", "mul", "i2f", "cmov"}
390 }
391 };
392
393 return p.getValue() == expected_vecs;
394 }, "Invalid enabled units in 'core_extensions' (nested vector param)");
395 }
396
397 // User-specified extension class
398 getExtension_<CircleExtensions>(getRoot()->getChild(fpu_loc), "circle")->doSomethingElse();
399 }
400
401 // Attach two tree nodes to get the following:
402 // top
403 // core0
404 // dispatch
405 // baz_node
406 // params
407 // baz
408 // fpu
409 // baz_node
410 // params
411 // baz
412 //
413 // This is needed to reproduce a write-final-config bug where an arch file
414 // specifies 'top.core0.*.baz_node.params.baz: 300' and the ConfigEmitterYAML
415 // ends up throwing an exception due to the '*' which tripped up the tree node
416 // extensions code.
417 auto dispatch = getRoot()->getChild("cpu.core0.dispatch");
418 auto fpu = getRoot()->getChild("cpu.core0.fpu");
419
420 dispatch_baz_.reset(new sparta::Baz(
421 dispatch, "Dummy node under top.cpu.core0.dispatch (to reproduce a SPARTA bug)"));
422
423 fpu_baz_.reset(new sparta::Baz(
424 fpu, "Dummy node under top.cpu.core0.fpu (to reproduce a SPARTA bug)"));
425}
426
427void ExampleSimulator::configureTree_()
428{
429 validateTreeNodeExtensions_();
430
431 // In TREE_CONFIGURING phase
432 // Configuration from command line is already applied
433
434 // Read these parameter values to avoid 'unread unbound parameter' exceptions:
435 // top.cpu.core0.dispatch.baz_node.params.baz
436 // top.cpu.core0.fpu.baz_node.params.baz
437 dispatch_baz_->readParams();
438 fpu_baz_->readParams();
439
440 sparta::ParameterBase* max_instrs =
441 getRoot()->getChildAs<sparta::ParameterBase>("cpu.core0.rob.params.num_insts_to_retire");
442
443 // Safely assign as string for now in case parameter type changes.
444 // Direct integer assignment without knowing parameter type is not yet available through C++ API
445 if(instruction_limit_ != 0){
446 max_instrs->setValueFromString(sparta::utils::uint64_to_str(instruction_limit_));
447 }
448
449 testing_notification_source_.reset(new sparta::NotificationSource<uint64_t>(
450 this->getRoot()->getSearchScope()->getChild("top.cpu.core0.rob"),
451 "testing_notif_channel",
452 "Notification channel for testing purposes only",
453 "testing_notif_channel"));
454
455 toggle_trigger_notification_source_.reset(new sparta::NotificationSource<uint64_t>(
456 getRoot()->getSearchScope()->getChild("top.cpu.core0.rob"),
457 "stats_profiler",
458 "Notification channel for testing report toggling on/off (statistics profiling)",
459 "stats_profiler"));
460
461 legacy_warmup_report_starter_.reset(new sparta::NotificationSource<uint64_t>(
462 getRoot(),
463 "all_threads_warmup_instruction_count_retired_re4",
464 "Legacy notificiation channel for testing purposes only",
465 "all_threads_warmup_instruction_count_retired_re4"));
466
467 getRoot()->REGISTER_FOR_NOTIFICATION(
468 onTriggered_, std::string, "sparta_expression_trigger_fired");
469 on_triggered_notifier_registered_ = true;
470}
471
472void ExampleSimulator::bindTree_()
473{
474 // In TREE_FINALIZED phase
475 // Tree is finalized. Taps placed. No new nodes at this point
476 // Bind appropriate ports
477
478 //Tell the factory to bind all units
479 auto cpu_factory = getCPUFactory_();
480 cpu_factory->bindTree(getRoot());
481
482 sparta::SpartaHandler cb = sparta::SpartaHandler::from_member<
483 ExampleSimulator, &ExampleSimulator::postRandomNumber_>(
484 this, "ExampleSimulator::postRandomNumber_");
485
486 random_number_trigger_.reset(new sparta::trigger::ExpressionCounterTrigger(
487 "RandomNumber", cb, "cpu.core0.rob.stats.total_number_retired 7500", false, this->getRoot()));
488
489 toggle_notif_trigger_.reset(new sparta::trigger::ExpressionTimeTrigger(
490 "ToggleNotif",
491 CREATE_SPARTA_HANDLER(ExampleSimulator, postToToggleTrigger_),
492 "1 ns",
493 getRoot()));
494
495 static const uint32_t warmup_multiplier = 1000;
496 auto gen_expression = [](const uint32_t core_idx) {
497 std::ostringstream oss;
498 oss << "cpu.core" << core_idx << ".rob.stats.total_number_retired >= "
499 << ((core_idx+1) * warmup_multiplier);
500 return oss.str();
501 };
502
503 num_cores_still_warming_up_ = num_cores_;
504 core_warmup_listeners_.reserve(num_cores_);
505
506 for (uint32_t core_idx = 0; core_idx < num_cores_; ++core_idx) {
507 core_warmup_listeners_.emplace_back(
508 new sparta::trigger::ExpressionTrigger(
509 "LegacyWarmupNotifications",
510 CREATE_SPARTA_HANDLER(ExampleSimulator, onLegacyWarmupNotification_),
511 gen_expression(core_idx),
512 getRoot(),
513 nullptr));
514 }
515}
516
517void ExampleSimulator::onLegacyWarmupNotification_()
518{
519 sparta_assert(num_cores_still_warming_up_ > 0);
520 --num_cores_still_warming_up_;
521 if (num_cores_still_warming_up_ == 0) {
522 legacy_warmup_report_starter_->postNotification(1);
523 }
524}
525
526const sparta::CounterBase* ExampleSimulator::findSemanticCounter_(CounterSemantic sem) const {
527 switch(sem){
529 return getRoot()->getChildAs<const sparta::CounterBase>("cpu.core0.rob.stats.total_number_retired");
530 break;
531 default:
532 return nullptr;
533 }
534}
535
536void ExampleSimulator::postRandomNumber_()
537{
538 const size_t random = rand() % 25;
539 testing_notification_source_->postNotification(random);
540 random_number_trigger_->reschedule();
541}
542
543void ExampleSimulator::postToToggleTrigger_()
544{
545 typedef std::pair<uint64_t,uint64_t> ValueCount;
546 static std::queue<ValueCount> values;
547
548 if (values.empty()) {
549 values.push({0,15});
550 values.push({1,25});
551 values.push({0,15});
552 values.push({1,25});
553 values.push({0,15});
554
555 ValueCount tmp = values.front();
556 values.push(tmp);
557 }
558
559 if (values.front().second == 0) {
560 values.pop();
561 ValueCount tmp = values.front();
562 values.push(tmp);
563 } else {
564 --values.front().second;
565 }
566
567 const ValueCount & current_value = values.front();
568 const uint64_t value_to_post = current_value.first;
569 toggle_trigger_notification_source_->postNotification(value_to_post);
570 toggle_notif_trigger_->reschedule();
571}
572
573void ExampleSimulator::onTriggered_(const std::string & msg)
574{
575 std::cout << " [trigger] " << msg << std::endl;
576}
577
578template <typename ParamT>
579sparta::Parameter<ParamT>* ExampleSimulator::getExtensionParameter_(
580 sparta::TreeNode* node,
581 const std::string& param_name,
582 const std::string& ext_name)
583{
584 if (!node) {
585 return nullptr;
586 }
587
588 sparta::TreeNode::ExtensionsBase * ext = ext_name.empty() ?
589 node->createExtension() :
590 node->getExtension(ext_name, true);
591
592 if (!ext) {
593 return nullptr;
594 }
595
596 sparta::ParameterSet * params = ext->getParameters();
597 if (!params) {
598 return nullptr;
599 }
600
601 if (!params->hasParameter(param_name)) {
602 return nullptr;
603 }
604
605 auto param = &params->getParameterAs<ParamT>(param_name);
606
607 // The only reason this method exists is to get the parameter
608 // and then call addDependentValidationCallback() on it. The
609 // validation callbacks do not trigger an increment on the
610 // parameter's read count. Read the value now so we can avoid
611 // the "unread unbound parameter" exceptions.
612 param->getValue();
613
614 return param;
615}
616
617template <typename ExtensionT>
618ExtensionT* ExampleSimulator::getExtension_(
619 sparta::TreeNode* node,
620 const std::string& ext_name)
621{
622 static_assert(std::is_base_of<sparta::TreeNode::ExtensionsBase, ExtensionT>::value,
623 "ExtensionT must be derived from sparta::TreeNode::ExtensionsBase");
624
625 if (!node) {
626 return nullptr;
627 }
628
629 return ext_name.empty() ?
630 dynamic_cast<ExtensionT*>(node->getExtension()) :
631 dynamic_cast<ExtensionT*>(node->getExtension(ext_name));
632}
633
634void ExampleSimulator::validateTreeNodeExtensions_()
635{
636 // cat.name_
637 if (auto prm = getExtensionParameter_<std::string>(
638 getRoot()->getChild("cpu.core0.lsu"), "name_", "cat"))
639 {
640 prm->addDependentValidationCallback(
641 [](std::string & val, const sparta::TreeNode*) -> bool {
642 return val == "Tom";
643 }, "Parameter 'name_' should be 'Tom'");
644 }
645
646 // cat.language_
647 if (auto prm = getExtensionParameter_<std::string>(
648 getRoot()->getChild("cpu.core0.lsu"), "language_", "cat"))
649 {
650 prm->addDependentValidationCallback(
651 [](std::string & val, const sparta::TreeNode*) -> bool {
652 return val == "meow" || val == "grrr";
653 }, "Parameter 'language_' should be 'meow' or 'grrr'");
654 }
655
656 // mouse.name_
657 if (auto prm = getExtensionParameter_<std::string>(
658 getRoot()->getChild("cpu.core0.lsu"), "name_", "mouse"))
659 {
660 prm->addDependentValidationCallback(
661 [](std::string & val, const sparta::TreeNode*) -> bool {
662 return val == "Jerry";
663 }, "Parameter 'name_' should be 'Jerry'");
664 }
665
666 // mouse.language_
667 if (auto prm = getExtensionParameter_<std::string>(
668 getRoot()->getChild("cpu.core0.lsu"), "language_", "mouse"))
669 {
670 prm->addDependentValidationCallback(
671 [](std::string & val, const sparta::TreeNode*) -> bool {
672 return val == "squeak";
673 }, "Parameter 'language_' should be 'squeak'");
674 }
675
676 // circle.color_
677 if (auto prm = getExtensionParameter_<std::string>(
678 getRoot()->getChild("cpu.core0.fpu"), "color_", "circle"))
679 {
680 prm->addDependentValidationCallback(
681 [](std::string & val, const sparta::TreeNode*) -> bool {
682 return val == "green";
683 }, "Parameter 'color_' should be 'green'");
684 }
685
686 // circle.shape_
687 if (auto prm = getExtensionParameter_<std::string>(
688 getRoot()->getChild("cpu.core0.fpu"), "shape_", "circle"))
689 {
690 prm->addDependentValidationCallback(
691 [](std::string & val, const sparta::TreeNode*) -> bool {
692 return val == "round";
693 }, "Parameter 'shape_' should be 'round'");
694 }
695
696 // circle.degrees_
697 if (auto prm = getExtensionParameter_<double>(
698 getRoot()->getChild("cpu.core0.fpu"), "degrees_", "circle"))
699 {
700 prm->addDependentValidationCallback(
701 [](double & val, const sparta::TreeNode*) -> bool {
702 return val == 360.0;
703 }, "Parameter 'degrees_' should be 360.0");
704 }
705
706 // User-specified extension class
707 getExtension_<CircleExtensions>(getRoot()->getChild("cpu.core0.fpu"), "circle")->doSomethingElse();
708
709 // apple.color_
710 if (auto prm = getExtensionParameter_<std::string>(
711 getRoot(), "color_", "apple"))
712 {
713 prm->addDependentValidationCallback(
714 [](std::string & val, const sparta::TreeNode*) -> bool {
715 return val == "red";
716 }, "Parameter 'color_' should be 'red'");
717 }
718
719 // The 'core0.lsu' node has two named extensions, so asking that node for
720 // unqualified extensions (no name specified) should throw.
721 //
722 // Note that we still have to check if core0.lsu has multiple extensions,
723 // since it will have zero in most example simulations unless --extension-file
724 // was used.
725 auto core0_lsu = getRoot()->getChild("cpu.core0.lsu");
726 if (core0_lsu->getNumExtensions() > 1) {
727 bool threw = false;
728 try {
729 getExtension_<>(core0_lsu);
730 } catch (...) {
731 threw = true;
732 }
733
734 if (!threw) {
735 throw sparta::SpartaException("Expected an exception to be thrown for unqualified "
736 "call to TreeNode::getExtension()");
737 }
738 }
739
740 // <unnamed>.color_
741 if (auto prm = getExtensionParameter_<std::string>(
742 getRoot()->getChild("cpu.core0.fpu"), "color_"))
743 {
744 prm->addDependentValidationCallback(
745 [](std::string & val, const sparta::TreeNode*) -> bool {
746 return val == "green";
747 }, "Parameter 'color_' should be 'green'");
748 }
749
750 // <unnamed>.shape_
751 if (auto prm = getExtensionParameter_<std::string>(
752 getRoot()->getChild("cpu.core0.fpu"), "shape_"))
753 {
754 prm->addDependentValidationCallback(
755 [](std::string & val, const sparta::TreeNode*) -> bool {
756 return val == "round";
757 }, "Parameter 'shape_' should be 'round'");
758 }
759
760 // <unnamed>.degrees_
761 if (auto prm = getExtensionParameter_<double>(
762 getRoot()->getChild("cpu.core0.fpu"), "degrees_"))
763 {
764 prm->addDependentValidationCallback(
765 [](double & val, const sparta::TreeNode*) -> bool {
766 return val == 360.0;
767 }, "Parameter 'degrees_' should be 360.0");
768 }
769
770 // <unnamed>.edges_
771 if (auto prm = getExtensionParameter_<std::string>(
772 getRoot()->getChild("cpu.core0.fpu"), "edges_"))
773 {
774 prm->addDependentValidationCallback(
775 [](std::string & val, const sparta::TreeNode*) -> bool {
776 return val == "0";
777 }, "Parameter 'edges_' should be '0'");
778 }
779
780 // baz_ext.ticket_
781 if (auto prm = getExtensionParameter_<std::string>(
782 getRoot()->getChild("cpu.core0.dispatch.baz_node", false), "ticket_", "baz_ext"))
783 {
784 prm->addDependentValidationCallback(
785 [](std::string & val, const sparta::TreeNode*) -> bool {
786 return val == "663";
787 }, "Parameter 'ticket_' should be '663'");
788 }
789
790 if (auto ext = getRoot()->getExtension("testing", true /*no_factory_ok*/)) {
791 auto expected_exts_file = ext->getParameterValueAs<std::string>("expected_extensions");
792 sparta_assert(!expected_exts_file.empty());
793
794 sparta::ParameterTree expected_exts_ptree;
795 sparta::app::NodeConfigFileApplicator applicator("", expected_exts_file, {"./"});
796 applicator.applyUnbound(expected_exts_ptree);
797
798 std::vector<const sparta::ParameterTree::Node*> ext_root_nodes;
799 expected_exts_ptree.getRoot()->recursFindPTreeNodesNamed("extension", ext_root_nodes);
800
801 for (auto ext_root_node : ext_root_nodes) {
802 auto tn_pattern = ext_root_node->getParent()->getPath();
803 std::vector<sparta::TreeNode*> matching_tns;
804 getRoot()->getSearchScope()->findChildren(tn_pattern, matching_tns);
805
806 for (auto tn : matching_tns) {
807 for (auto ext_node : ext_root_node->getChildren()) {
808 auto ext_name = ext_node->getName();
809 ext = tn->getExtension(ext_name); // no factory NOT okay: we expect a Foobar
810 sparta_assert(dynamic_cast<Foobar*>(ext) != nullptr);
811
812 for (auto param_node : ext_node->getChildren()) {
813 auto actual_val = ext->getParameterValueAs<std::string>(param_node->getName());
814 auto expected_val = param_node->peekValue();
815 sparta_assert(actual_val == expected_val);
816 }
817 }
818 }
819 }
820 }
821}
822
823ExampleSimulator::ExampleController::ExampleController(
824 const sparta::app::Simulation * sim) :
825 sparta::app::Simulation::SimulationController(sim)
826{
827 sparta::app::Simulation::SimulationController::addNamedCallback_(
828 "eat", CREATE_SPARTA_HANDLER(ExampleController, customEatCallback_));
829
830 sparta::app::Simulation::SimulationController::addNamedCallback_(
831 "sleep", CREATE_SPARTA_HANDLER(ExampleController, customSleepCallback_));
832}
833
834void ExampleSimulator::ExampleController::pause_(const sparta::app::Simulation * sim)
835{
836 std::cout << " [control] Controller PAUSE method has been called for simulation '"
837 << sim->getSimName() << "'" << std::endl;
838}
839
840void ExampleSimulator::ExampleController::resume_(const sparta::app::Simulation * sim)
841{
842 std::cout << " [control] Controller RESUME method has been called for simulation '"
843 << sim->getSimName() << "'" << std::endl;
844}
845
846void ExampleSimulator::ExampleController::terminate_(const sparta::app::Simulation * sim)
847{
848 std::cout << " [control] Controller TERMINATE method has been called for simulation '"
849 << sim->getSimName() << "'" << std::endl;
850 const_cast<sparta::Scheduler*>(sim->getScheduler())->stopRunning();
851}
852
853void ExampleSimulator::ExampleController::customEatCallback_()
854{
855 std::cout << " [control] Controller CUSTOM method has been called ('eat')" << std::endl;
856}
857
858void ExampleSimulator::ExampleController::customSleepCallback_()
859{
860 std::cout << " [control] Controller CUSTOM method has been called ('sleep')" << std::endl;
861}
File that defines the Clock class.
CycleHistogram implementation using sparta CycleCounter.
Definition of the CoreModel Fetch unit.
#define REGISTER_HISTOGRAM_STAT_CALC_FCN(histogram_type, fcn_name)
Function Registration Macro for Histogram/CycleHistogram. This macro is called by the users in their ...
Histogram implementation using sparta Counters.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define CREATE_SPARTA_HANDLER(clname, meth)
File that defines the SpartaTester class and testing Macros.
Cool string utilities.
Basic Node framework in sparta device tree composite pattern.
ExampleSimulator which builds the model and configures it.
ExampleSimulator(const std::string &topology, sparta::Scheduler &scheduler, uint32_t num_cores=1, uint64_t instruction_limit=0, bool show_factories=false)
Construct ExampleSimulator.
The base class for all Counters.
A TreeNode that generates a specific type of notification which propagates up a tree of TreeNodes (us...
Non-templated base class for generic parameter access and iteration.
void setValueFromString(const std::string &str, bool poke=false)
Attempts to assign a value to this non-vector Parameter from a string.
Generic container of Parameters.
ParameterSet()=delete
Default constructor disabled.
void addParameter_(sparta::ParameterBase *p)
Add a parameter to the parameter set. \temp This will be removed.
bool hasParameter(const std::string &name) const
Determines whether this ParameterSet has the parameter with the given name.
const Parameter< ContentT > & getParameterAs(const std::string &name) const
Retrieves a sparta::Parameter<ContentT> reference from this parameter set.
void recursFindPTreeNodesNamed(const std::string &name, std::vector< Node * > &matching_nodes)
Recursively find all nodes that have a given name.
Virtual Parameter Tree. This represents a tree of parameters read from some source but does not neces...
Parameter instance, templated to contain only a specific type.
void addResourceFactory()
Add a resource factory by its template type.
TreeNode subclass representing a node in the device tree which contains a single ResourceFactory and ...
GlobalTreeNode * getSearchScope()
Gets the search node "parent" of this root node which is suitable for performing searches that includ...
void enterTeardown()
Places this tree into TREE_TEARDOWN phase so that nodes may be deleted without errors.
A class that lets you schedule events now and in the future.
void stopRunning()
Tell the scheduler to stop running.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:204
static const group_idx_type GROUP_IDX_NONE
GroupIndex indicating that a node has no group index because it belongs to no group.
Definition TreeNode.hpp:302
std::string getLocation() const override final
ExtensionsBase * getExtension(const std::string &extension_name, bool no_factory_ok=false)
Get an extension object by extension name.
static constexpr char GROUP_NAME_NONE[]
Group name indicating that a node belongs to no group.
Definition TreeNode.hpp:313
ExtensionsBase * createExtension(const std::string &extension_name, bool replace=false)
Create an extension on demand. This is useful if you want to add an extension to a node that was not ...
TreeNode()=delete
Not default-constructable.
ExtensionsBase * getExtension()
Get an extension without needing to specify any particular extension name. If no extensions exist,...
const ConstT getChildAs(const std::string &name, bool must_exist=true) const
Retrieves a child that is castable to T with the given dotted path.
TreeNode * getChild(const std::string &name, bool must_exist=true)
Retrieves a child with this dotted path name.
uint32_t findChildren(const std::string &pattern, std::vector< TreeNode * > &results, std::vector< std::vector< std::string > > &replacements)
Finds all children starting at this node with a given pattern relative to this node by matching names...
Applies a configuration file to a node pattern.
void applyUnbound(sparta::ParameterTree &ptree, bool verbose=false) const override
Apply the parameter contained in this object to the unbound (virtual) parameter tree ptree.
Simulator which builds a sparta DeviceTree.
std::vector< std::unique_ptr< sparta::TreeNode > > to_delete_
Vector of TreeNodes to delete automatically at destruction. Add any nodes allocated to this list to a...
void addTreeNodeExtensionFactory_(const std::string &extension_name, std::function< TreeNode::ExtensionsBase *()> factory)
Include an extension factory for this simulation's device tree nodes. They will be given to specific ...
@ CSEM_INSTRUCTIONS
Instruction count semantic (usually core0)
std::shared_ptr< SimulationController > controller_
Custom controller to handle various simulation events.
sparta::RootTreeNode * getRoot() noexcept
Returns the tree root.
void setSimulationController_(std::shared_ptr< SimulationController > controller)
Set a controller to handle custom simulation events.
sparta::ResourceSet * getResourceSet() noexcept
Returns the resource set for this Simulation.
const std::string & getSimName() const noexcept
Returns this simulator's name.
sparta::Scheduler * getScheduler()
Returns the simulation's scheduler.
Macros for handling exponential backoff.