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 sprintf_test_notification_source_.reset(new sparta::SprintfNotificationSource(
456 this->getRoot()->getSearchScope()->getChild("top.cpu.core0.rob"),
457 "sprintf_notif_channel",
458 "Notification channel for testing the 'sprintf-notif' report trigger",
459 "sprintf_notif_channel"));
460
461 toggle_trigger_notification_source_.reset(new sparta::NotificationSource<uint64_t>(
462 getRoot()->getSearchScope()->getChild("top.cpu.core0.rob"),
463 "stats_profiler",
464 "Notification channel for testing report toggling on/off (statistics profiling)",
465 "stats_profiler"));
466
467 legacy_warmup_report_starter_.reset(new sparta::NotificationSource<uint64_t>(
468 getRoot(),
469 "all_threads_warmup_instruction_count_retired_re4",
470 "Legacy notificiation channel for testing purposes only",
471 "all_threads_warmup_instruction_count_retired_re4"));
472
473 getRoot()->REGISTER_FOR_NOTIFICATION(
474 onTriggered_, std::string, "sparta_expression_trigger_fired");
475 on_triggered_notifier_registered_ = true;
476}
477
478void ExampleSimulator::bindTree_()
479{
480 // In TREE_FINALIZED phase
481 // Tree is finalized. Taps placed. No new nodes at this point
482 // Bind appropriate ports
483
484 //Tell the factory to bind all units
485 auto cpu_factory = getCPUFactory_();
486 cpu_factory->bindTree(getRoot());
487
488 sparta::SpartaHandler cb = sparta::SpartaHandler::from_member<
489 ExampleSimulator, &ExampleSimulator::postRandomNumber_>(
490 this, "ExampleSimulator::postRandomNumber_");
491
492 random_number_trigger_.reset(new sparta::trigger::ExpressionCounterTrigger(
493 "RandomNumber", cb, "cpu.core0.rob.stats.total_number_retired 7500", false, this->getRoot()));
494
495 toggle_notif_trigger_.reset(new sparta::trigger::ExpressionTimeTrigger(
496 "ToggleNotif",
497 CREATE_SPARTA_HANDLER(ExampleSimulator, postToToggleTrigger_),
498 "1 ns",
499 getRoot()));
500
501 static const uint32_t warmup_multiplier = 1000;
502 auto gen_expression = [](const uint32_t core_idx) {
503 std::ostringstream oss;
504 oss << "cpu.core" << core_idx << ".rob.stats.total_number_retired >= "
505 << ((core_idx+1) * warmup_multiplier);
506 return oss.str();
507 };
508
509 num_cores_still_warming_up_ = num_cores_;
510 core_warmup_listeners_.reserve(num_cores_);
511
512 for (uint32_t core_idx = 0; core_idx < num_cores_; ++core_idx) {
513 core_warmup_listeners_.emplace_back(
514 new sparta::trigger::ExpressionTrigger(
515 "LegacyWarmupNotifications",
516 CREATE_SPARTA_HANDLER(ExampleSimulator, onLegacyWarmupNotification_),
517 gen_expression(core_idx),
518 getRoot(),
519 nullptr));
520 }
521}
522
523void ExampleSimulator::onLegacyWarmupNotification_()
524{
525 sparta_assert(num_cores_still_warming_up_ > 0);
526 --num_cores_still_warming_up_;
527 if (num_cores_still_warming_up_ == 0) {
528 legacy_warmup_report_starter_->postNotification(1);
529 }
530}
531
532const sparta::CounterBase* ExampleSimulator::findSemanticCounter_(CounterSemantic sem) const {
533 switch(sem){
535 return getRoot()->getChildAs<const sparta::CounterBase>("cpu.core0.rob.stats.total_number_retired");
536 break;
537 default:
538 return nullptr;
539 }
540}
541
542void ExampleSimulator::postRandomNumber_()
543{
544 const size_t random = rand() % 25;
545 testing_notification_source_->postNotification(random);
546
547 if (sprintf_test_notification_source_->hasFormatString() && random % 10 == 0) {
548 sprintf_test_notification_source_->postNotification("random_stat", static_cast<int>(random));
549 }
550
551 random_number_trigger_->reschedule();
552}
553
554void ExampleSimulator::postToToggleTrigger_()
555{
556 typedef std::pair<uint64_t,uint64_t> ValueCount;
557 static std::queue<ValueCount> values;
558
559 if (values.empty()) {
560 values.push({0,15});
561 values.push({1,25});
562 values.push({0,15});
563 values.push({1,25});
564 values.push({0,15});
565
566 ValueCount tmp = values.front();
567 values.push(tmp);
568 }
569
570 if (values.front().second == 0) {
571 values.pop();
572 ValueCount tmp = values.front();
573 values.push(tmp);
574 } else {
575 --values.front().second;
576 }
577
578 const ValueCount & current_value = values.front();
579 const uint64_t value_to_post = current_value.first;
580 toggle_trigger_notification_source_->postNotification(value_to_post);
581 toggle_notif_trigger_->reschedule();
582}
583
584void ExampleSimulator::onTriggered_(const std::string & msg)
585{
586 std::cout << " [trigger] " << msg << std::endl;
587}
588
589template <typename ParamT>
590sparta::Parameter<ParamT>* ExampleSimulator::getExtensionParameter_(
591 sparta::TreeNode* node,
592 const std::string& param_name,
593 const std::string& ext_name)
594{
595 if (!node) {
596 return nullptr;
597 }
598
599 sparta::TreeNode::ExtensionsBase * ext = ext_name.empty() ?
600 node->createExtension() :
601 node->getExtension(ext_name, true);
602
603 if (!ext) {
604 return nullptr;
605 }
606
607 sparta::ParameterSet * params = ext->getParameters();
608 if (!params) {
609 return nullptr;
610 }
611
612 if (!params->hasParameter(param_name)) {
613 return nullptr;
614 }
615
616 auto param = &params->getParameterAs<ParamT>(param_name);
617
618 // The only reason this method exists is to get the parameter
619 // and then call addDependentValidationCallback() on it. The
620 // validation callbacks do not trigger an increment on the
621 // parameter's read count. Read the value now so we can avoid
622 // the "unread unbound parameter" exceptions.
623 param->getValue();
624
625 return param;
626}
627
628template <typename ExtensionT>
629ExtensionT* ExampleSimulator::getExtension_(
630 sparta::TreeNode* node,
631 const std::string& ext_name)
632{
633 static_assert(std::is_base_of<sparta::TreeNode::ExtensionsBase, ExtensionT>::value,
634 "ExtensionT must be derived from sparta::TreeNode::ExtensionsBase");
635
636 if (!node) {
637 return nullptr;
638 }
639
640 return ext_name.empty() ?
641 dynamic_cast<ExtensionT*>(node->getExtension()) :
642 dynamic_cast<ExtensionT*>(node->getExtension(ext_name));
643}
644
645void ExampleSimulator::validateTreeNodeExtensions_()
646{
647 // cat.name_
648 if (auto prm = getExtensionParameter_<std::string>(
649 getRoot()->getChild("cpu.core0.lsu"), "name_", "cat"))
650 {
651 prm->addDependentValidationCallback(
652 [](std::string & val, const sparta::TreeNode*) -> bool {
653 return val == "Tom";
654 }, "Parameter 'name_' should be 'Tom'");
655 }
656
657 // cat.language_
658 if (auto prm = getExtensionParameter_<std::string>(
659 getRoot()->getChild("cpu.core0.lsu"), "language_", "cat"))
660 {
661 prm->addDependentValidationCallback(
662 [](std::string & val, const sparta::TreeNode*) -> bool {
663 return val == "meow" || val == "grrr";
664 }, "Parameter 'language_' should be 'meow' or 'grrr'");
665 }
666
667 // mouse.name_
668 if (auto prm = getExtensionParameter_<std::string>(
669 getRoot()->getChild("cpu.core0.lsu"), "name_", "mouse"))
670 {
671 prm->addDependentValidationCallback(
672 [](std::string & val, const sparta::TreeNode*) -> bool {
673 return val == "Jerry";
674 }, "Parameter 'name_' should be 'Jerry'");
675 }
676
677 // mouse.language_
678 if (auto prm = getExtensionParameter_<std::string>(
679 getRoot()->getChild("cpu.core0.lsu"), "language_", "mouse"))
680 {
681 prm->addDependentValidationCallback(
682 [](std::string & val, const sparta::TreeNode*) -> bool {
683 return val == "squeak";
684 }, "Parameter 'language_' should be 'squeak'");
685 }
686
687 // circle.color_
688 if (auto prm = getExtensionParameter_<std::string>(
689 getRoot()->getChild("cpu.core0.fpu"), "color_", "circle"))
690 {
691 prm->addDependentValidationCallback(
692 [](std::string & val, const sparta::TreeNode*) -> bool {
693 return val == "green";
694 }, "Parameter 'color_' should be 'green'");
695 }
696
697 // circle.shape_
698 if (auto prm = getExtensionParameter_<std::string>(
699 getRoot()->getChild("cpu.core0.fpu"), "shape_", "circle"))
700 {
701 prm->addDependentValidationCallback(
702 [](std::string & val, const sparta::TreeNode*) -> bool {
703 return val == "round";
704 }, "Parameter 'shape_' should be 'round'");
705 }
706
707 // circle.degrees_
708 if (auto prm = getExtensionParameter_<double>(
709 getRoot()->getChild("cpu.core0.fpu"), "degrees_", "circle"))
710 {
711 prm->addDependentValidationCallback(
712 [](double & val, const sparta::TreeNode*) -> bool {
713 return val == 360.0;
714 }, "Parameter 'degrees_' should be 360.0");
715 }
716
717 // User-specified extension class
718 getExtension_<CircleExtensions>(getRoot()->getChild("cpu.core0.fpu"), "circle")->doSomethingElse();
719
720 // apple.color_
721 if (auto prm = getExtensionParameter_<std::string>(
722 getRoot(), "color_", "apple"))
723 {
724 prm->addDependentValidationCallback(
725 [](std::string & val, const sparta::TreeNode*) -> bool {
726 return val == "red";
727 }, "Parameter 'color_' should be 'red'");
728 }
729
730 // The 'core0.lsu' node has two named extensions, so asking that node for
731 // unqualified extensions (no name specified) should throw.
732 //
733 // Note that we still have to check if core0.lsu has multiple extensions,
734 // since it will have zero in most example simulations unless --extension-file
735 // was used.
736 auto core0_lsu = getRoot()->getChild("cpu.core0.lsu");
737 if (core0_lsu->getNumExtensions() > 1) {
738 bool threw = false;
739 try {
740 getExtension_<>(core0_lsu);
741 } catch (...) {
742 threw = true;
743 }
744
745 if (!threw) {
746 throw sparta::SpartaException("Expected an exception to be thrown for unqualified "
747 "call to TreeNode::getExtension()");
748 }
749 }
750
751 // <unnamed>.color_
752 if (auto prm = getExtensionParameter_<std::string>(
753 getRoot()->getChild("cpu.core0.fpu"), "color_"))
754 {
755 prm->addDependentValidationCallback(
756 [](std::string & val, const sparta::TreeNode*) -> bool {
757 return val == "green";
758 }, "Parameter 'color_' should be 'green'");
759 }
760
761 // <unnamed>.shape_
762 if (auto prm = getExtensionParameter_<std::string>(
763 getRoot()->getChild("cpu.core0.fpu"), "shape_"))
764 {
765 prm->addDependentValidationCallback(
766 [](std::string & val, const sparta::TreeNode*) -> bool {
767 return val == "round";
768 }, "Parameter 'shape_' should be 'round'");
769 }
770
771 // <unnamed>.degrees_
772 if (auto prm = getExtensionParameter_<double>(
773 getRoot()->getChild("cpu.core0.fpu"), "degrees_"))
774 {
775 prm->addDependentValidationCallback(
776 [](double & val, const sparta::TreeNode*) -> bool {
777 return val == 360.0;
778 }, "Parameter 'degrees_' should be 360.0");
779 }
780
781 // <unnamed>.edges_
782 if (auto prm = getExtensionParameter_<std::string>(
783 getRoot()->getChild("cpu.core0.fpu"), "edges_"))
784 {
785 prm->addDependentValidationCallback(
786 [](std::string & val, const sparta::TreeNode*) -> bool {
787 return val == "0";
788 }, "Parameter 'edges_' should be '0'");
789 }
790
791 // baz_ext.ticket_
792 if (auto prm = getExtensionParameter_<std::string>(
793 getRoot()->getChild("cpu.core0.dispatch.baz_node", false), "ticket_", "baz_ext"))
794 {
795 prm->addDependentValidationCallback(
796 [](std::string & val, const sparta::TreeNode*) -> bool {
797 return val == "663";
798 }, "Parameter 'ticket_' should be '663'");
799 }
800
801 if (auto ext = getRoot()->getExtension("testing", true /*no_factory_ok*/)) {
802 auto expected_exts_file = ext->getParameterValueAs<std::string>("expected_extensions");
803 sparta_assert(!expected_exts_file.empty());
804
805 sparta::ParameterTree expected_exts_ptree;
806 sparta::app::NodeConfigFileApplicator applicator("", expected_exts_file, {"./"});
807 applicator.applyUnbound(expected_exts_ptree);
808
809 std::vector<const sparta::ParameterTree::Node*> ext_root_nodes;
810 expected_exts_ptree.getRoot()->recursFindPTreeNodesNamed("extension", ext_root_nodes);
811
812 for (auto ext_root_node : ext_root_nodes) {
813 auto tn_pattern = ext_root_node->getParent()->getPath();
814 std::vector<sparta::TreeNode*> matching_tns;
815 getRoot()->getSearchScope()->findChildren(tn_pattern, matching_tns);
816
817 for (auto tn : matching_tns) {
818 for (auto ext_node : ext_root_node->getChildren()) {
819 auto ext_name = ext_node->getName();
820 ext = tn->getExtension(ext_name); // no factory NOT okay: we expect a Foobar
821 sparta_assert(dynamic_cast<Foobar*>(ext) != nullptr);
822
823 for (auto param_node : ext_node->getChildren()) {
824 auto actual_val = ext->getParameterValueAs<std::string>(param_node->getName());
825 auto expected_val = param_node->peekValue();
826 sparta_assert(actual_val == expected_val);
827 }
828 }
829 }
830 }
831 }
832}
833
834ExampleSimulator::ExampleController::ExampleController(
835 const sparta::app::Simulation * sim) :
836 sparta::app::Simulation::SimulationController(sim)
837{
838 sparta::app::Simulation::SimulationController::addNamedCallback_(
839 "eat", CREATE_SPARTA_HANDLER(ExampleController, customEatCallback_));
840
841 sparta::app::Simulation::SimulationController::addNamedCallback_(
842 "sleep", CREATE_SPARTA_HANDLER(ExampleController, customSleepCallback_));
843}
844
845void ExampleSimulator::ExampleController::pause_(const sparta::app::Simulation * sim)
846{
847 std::cout << " [control] Controller PAUSE method has been called for simulation '"
848 << sim->getSimName() << "'" << std::endl;
849}
850
851void ExampleSimulator::ExampleController::resume_(const sparta::app::Simulation * sim)
852{
853 std::cout << " [control] Controller RESUME method has been called for simulation '"
854 << sim->getSimName() << "'" << std::endl;
855}
856
857void ExampleSimulator::ExampleController::terminate_(const sparta::app::Simulation * sim)
858{
859 std::cout << " [control] Controller TERMINATE method has been called for simulation '"
860 << sim->getSimName() << "'" << std::endl;
861 const_cast<sparta::Scheduler*>(sim->getScheduler())->stopRunning();
862}
863
864void ExampleSimulator::ExampleController::customEatCallback_()
865{
866 std::cout << " [control] Controller CUSTOM method has been called ('eat')" << std::endl;
867}
868
869void ExampleSimulator::ExampleController::customSleepCallback_()
870{
871 std::cout << " [control] Controller CUSTOM method has been called ('sleep')" << std::endl;
872}
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.
Accepts a sprintf-style format string like "%s_%i.json" and uses the arguments given to postNotificat...
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:204
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,...
static constexpr 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
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.
Class that is the "top level" or starting point for simulation.
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.