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"
15#include "sparta/trigger/ContextCounterTrigger.hpp"
21#include "sparta/report/DatabaseInterface.hpp"
22#include "simdb/schema/Schema.hpp"
23#include "simdb/TableProxy.hpp"
24#include "simdb/async/AsyncTaskEval.hpp"
25#include "simdb/impl/sqlite/SQLiteConnProxy.hpp"
26#include "simdb/impl/hdf5/HDF5ConnProxy.hpp"
27#include "simdb/utils/uuids.hpp"
28#include "simdb/utils/ObjectQuery.hpp"
29
30#include "Fetch.hpp"
31#include "Decode.hpp"
32#include "Rename.hpp"
33#include "Dispatch.hpp"
34#include "Execute.hpp"
35#include "LSU.hpp"
36#include "ROB.hpp"
37#include "FlushManager.hpp"
38#include "Preloader.hpp"
39#include "CustomHistogramStats.hpp"
40
41// UPDATE
42#include "BIU.hpp"
43#include "MSS.hpp"
44
45namespace {
46
47 // Struct for writing and verifying SQLite records.
48 // See buildSchemaA() below.
49 struct TestSQLiteSchemaA {
50 struct Numbers {
51 double First;
52 double Second;
53 };
54 Numbers numbers;
55
56 struct Metadata {
57 std::string Name;
58 double Value;
59 };
60 Metadata metadata;
61
62 static TestSQLiteSchemaA createRandom() {
63 TestSQLiteSchemaA s;
64 s.numbers.First = rand() / 1000 * 3.14;
65 s.numbers.Second = rand() / 1000 * 3.14;
66 s.metadata.Name = simdb::generateUUID();
67 s.metadata.Value = rand() / 1000 * 3.14;
68 return s;
69 }
70 };
71
72 // Another struct for writing and verifying SQLite
73 // records. See buildSchemaB() below.
74 struct TestSQLiteSchemaB {
75 struct Strings {
76 std::string First;
77 std::string Second;
78 };
79 Strings strings;
80
81 struct Metadata {
82 std::string Name;
83 std::string Value;
84 };
85 Metadata metadata;
86
87 static TestSQLiteSchemaB createRandom() {
88 TestSQLiteSchemaB s;
89 s.strings.First = simdb::generateUUID();
90 s.strings.Second = simdb::generateUUID();
91 s.metadata.Name = simdb::generateUUID();
92 s.metadata.Value = simdb::generateUUID();
93 return s;
94 }
95 };
96
97 // Struct for writing and verifying HDF5 records
98 struct TestHDF5SchemaC {
99 double x;
100 double y;
101 uint16_t z;
102
103 static TestHDF5SchemaC createRandom() {
104 TestHDF5SchemaC s;
105 s.x = rand() / 1000 * 3.14;
106 s.y = rand() / 1000 * 3.14;
107 s.z = rand();
108 return s;
109 }
110 };
111}
112
113namespace sparta_simdb {
114
115 // Helper class which creates random SQLite / HDF5
116 // structs for SimDB writes, and stores the structs
117 // in memory too. The data will be read back from
118 // the database at the end of simulation, and the
119 // values retrieved from file will be compared with
120 // the values that were stored in memory.
122 public:
123 DatabaseTester() = default;
124 ~DatabaseTester() = default;
125
126 const TestSQLiteSchemaA & createAndStoreRecordForSQLiteSchemaA() {
127 if (records_schemaA_.size() < 100) {
128 indices_schemaA_.emplace_back(records_schemaA_.size());
129 records_schemaA_.emplace_back(TestSQLiteSchemaA::createRandom());
130 return records_schemaA_.back();
131 } else {
132 indices_schemaA_.emplace_back(rand() % records_schemaA_.size());
133 return records_schemaA_[indices_schemaA_.back()];
134 }
135 }
136
137 const TestSQLiteSchemaB & createAndStoreRecordForSQLiteSchemaB() {
138 if (records_schemaB_.size() < 100) {
139 indices_schemaB_.emplace_back(records_schemaB_.size());
140 records_schemaB_.emplace_back(TestSQLiteSchemaB::createRandom());
141 return records_schemaB_.back();
142 } else {
143 indices_schemaB_.emplace_back(rand() % records_schemaB_.size());
144 return records_schemaB_[indices_schemaB_.back()];
145 }
146 }
147
148 const TestHDF5SchemaC & createAndStoreRecordForHDF5SchemaC() {
149 records_schemaC_.emplace_back(TestHDF5SchemaC::createRandom());
150 return records_schemaC_.back();
151 }
152
153 const std::vector<TestSQLiteSchemaA> & getWrittenRecordsForSchemaA() const {
154 return records_schemaA_;
155 }
156
157 const std::vector<TestSQLiteSchemaB> & getWrittenRecordsForSchemaB() const {
158 return records_schemaB_;
159 }
160
161 const std::vector<TestHDF5SchemaC> & getWrittenRecordsForSchemaC() const {
162 return records_schemaC_;
163 }
164
165 void verifyRecords(const std::string & db_file) const {
166 simdb::ObjectManager obj_mgr(".");
167 if (!obj_mgr.connectToExistingDatabase(db_file)) {
168 return;
169 }
170
171 auto numeric_db = GET_DB_FROM_CURRENT_SIMULATION(NumericMeta);
172 if (numeric_db) {
173 auto values_query =
174 numeric_db->createObjectQueryForTable("Numbers");
175
176 if (values_query) {
177 double first = 0, second = 0;
178 values_query->writeResultIterationsTo(
179 "First", &first, "Second", &second);
180
181 if (values_query->countMatches() != indices_schemaA_.size()) {
182 throw sparta::SpartaException("Could not verify SimDB records");
183 }
184
185 auto result_iter = values_query->executeQuery();
186 size_t record_idx = 0;
187 while (result_iter->getNext()) {
188 const auto & expected = records_schemaA_[indices_schemaA_[record_idx]];
189 if (first != expected.numbers.First) {
190 throw sparta::SpartaException("Could not verify SimDB records");
191 }
192 if (second != expected.numbers.Second) {
193 throw sparta::SpartaException("Could not verify SimDB records");
194 }
195 ++record_idx;
196 }
197 }
198
199 auto meta_query =
200 numeric_db->createObjectQueryForTable("Metadata");
201 if (meta_query) {
202 std::string name;
203 double value = 0;
204 meta_query->writeResultIterationsTo("Name", &name, "Value", &value);
205
206 if (meta_query->countMatches() != indices_schemaA_.size()) {
207 throw sparta::SpartaException("Could not verify SimDB records");
208 }
209
210 auto result_iter = meta_query->executeQuery();
211 size_t record_idx = 0;
212 while (result_iter->getNext()) {
213 const auto & expected = records_schemaA_[indices_schemaA_[record_idx]];
214 if (name != expected.metadata.Name) {
215 throw sparta::SpartaException("Could not verify SimDB records");
216 }
217 if (value != expected.metadata.Value) {
218 throw sparta::SpartaException("Could not verify SimDB records");
219 }
220 ++record_idx;
221 }
222 }
223 }
224 }
225
226 private:
227 std::vector<TestSQLiteSchemaA> records_schemaA_;
228 std::vector<TestSQLiteSchemaB> records_schemaB_;
229 std::vector<TestHDF5SchemaC> records_schemaC_;
230 std::vector<uint16_t> indices_schemaA_;
231 std::vector<uint16_t> indices_schemaB_;
232 std::vector<uint16_t> indices_schemaC_;
233 };
234}
235
236namespace {
237
238 // Schema builder to test two simdb::ObjectManager's
239 // bound to the same database file, separated in that
240 // same file by their respective application name.
241 // A third schema builder is for another ObjectManager,
242 // though it will be used to write records to an HDF5
243 // database, and therefore will be in its own file.
244 // SimDB's worker thread should be able to keep them
245 // separated into two groups: one group for the two
246 // SQLite database connections, and one group only
247 // serving the one HDF5 connection.
248 //
249 // Note that the two schema builders below have some
250 // overlap in their table definitions: schemaA and
251 // schemaB have some of the same table names, but
252 // these tables have different column configurations.
253 // This should not be a problem for ObjectManager
254 // since it will use its unique application name
255 // with the table names we give it to create a
256 // unique schema inside the shared file, separated
257 // from other applications tied to the same file.
258 // The specific way in which the schemas are kept
259 // separate in the file is not our concern; the
260 // DbConnProxy subclasses take care of those
261 // specifics.
262 void buildSchemaA(simdb::Schema & schema)
263 {
264 using dt = simdb::ColumnDataType;
265
266 schema.addTable("Numbers")
267 .addColumn("First", dt::double_t)
268 .addColumn("Second", dt::double_t);
269
270 schema.addTable("Metadata")
271 .addColumn("Name", dt::string_t)
272 .addColumn("Value", dt::double_t);
273 }
274
275 void buildSchemaB(simdb::Schema & schema)
276 {
277 using dt = simdb::ColumnDataType;
278
279 schema.addTable("Strings")
280 .addColumn("First", dt::string_t)
281 .addColumn("Second", dt::string_t);
282
283 schema.addTable("Metadata")
284 .addColumn("Name", dt::string_t)
285 .addColumn("Value", dt::string_t);
286 }
287
288 void buildSchemaC(simdb::Schema & schema)
289 {
290 using dt = simdb::ColumnDataType;
291
292 schema.addTable("Numbers")
293 .addField("x", dt::double_t, FOFFSET(TestHDF5SchemaC,x))
294 .addField("y", dt::double_t, FOFFSET(TestHDF5SchemaC,y))
295 .addField("z", dt::uint16_t, FOFFSET(TestHDF5SchemaC,z));
296 }
297
298 simdb::DbConnProxy * createSQLiteProxy()
299 {
300 return new simdb::SQLiteConnProxy;
301 }
302
303 simdb::DbConnProxy * createHDF5Proxy()
304 {
305 return new simdb::HDF5ConnProxy;
306 }
307}
308
309namespace sparta {
310
311 // Example parameter set used to reproduce write-final-config
313 public:
314 IntParameterSet(TreeNode * parent) :
315 ParameterSet(parent),
316 int_param_(new Parameter<uint32_t>(
317 "baz", 0, "Example parameter set to reproduce bug"))
318 {
319 addParameter_(int_param_.get());
320 }
321
322 uint32_t read() const {
323 return int_param_->getValue();
324 }
325
326 private:
327 std::unique_ptr<Parameter<uint32_t>> int_param_;
328 };
329
330 // Dummy node class used together with IntParameterSet to
331 // reproduce write-final-config bug
332 class Baz : public TreeNode {
333 public:
334 Baz(TreeNode* parent,
335 const std::string & desc) :
336 TreeNode(parent, "baz_node", "BazGroup", 0, desc)
337 {
338 baz_.reset(new IntParameterSet(this));
339 checkDbAccess();
340 }
341
342 void checkDbAccess(const bool stop_checking = false) {
343 if (stop_checking_db_access_) {
344 return;
345 }
346 if (auto dbconn = GET_DB_FOR_COMPONENT(Stats, this)) {
347 //Run a simple query against the database just to verify
348 //the connection is open and accepting requests
349 (void) dbconn->findObject("ObjectManagersInDatabase", 1);
350 stop_checking_db_access_ = stop_checking;
351 }
352 }
353
354 void readParams() {
355 std::cout << " Node '" << getLocation()
356 << "' has parameter 'baz' with a value set to "
357 << baz_->read() << std::endl;
358 auto ext = getExtension("baz_ext");
359 if(ext) {
360 std::cout << "That's the ticket: "
361 << ext->getParameters()->getParameterValueAs<std::string>("ticket_") << std::endl;
362 }
363 }
364
365 private:
366 std::unique_ptr<IntParameterSet> baz_;
367 bool stop_checking_db_access_ = false;
368 };
369
370}
371
372template <typename DataT>
373void validateParameter(const sparta::ParameterSet & params,
374 const std::string & param_name,
375 const DataT & expected_value)
376{
377 if (!params.hasParameter(param_name)) {
378 return;
379 }
380 const DataT actual_value = params.getParameterValueAs<DataT>(param_name);
381 if (actual_value != expected_value) {
382 throw sparta::SpartaException("Invalid extension parameter encountered:\n")
383 << "\tParameter name: " << param_name
384 << "\nParameter value (actual): " << actual_value
385 << "\nParameter value (expected): " << expected_value;
386 }
387}
388
389template <typename DataT>
390void validateParameter(const sparta::ParameterSet & params,
391 const std::string & param_name,
392 const std::set<DataT> & expected_values)
393{
394 bool found = false;
395 for (const auto & expected : expected_values) {
396 try {
397 found = false;
398 validateParameter<DataT>(params, param_name, expected);
399 found = true;
400 break;
401 } catch (...) {
402 }
403 }
404
405 if (!found) {
406 throw sparta::SpartaException("Invalid extension parameter "
407 "encountered for '") << param_name << "'";
408 }
409}
410
412{
413public:
415 virtual ~CircleExtensions() {}
416
417 void doSomethingElse() const {
418 std::cout << "Invoking a method that is unknown to the sparta::TreeNode object, "
419 "even though 'this' object was created by, and currently owned by, "
420 "a specific tree node.";
421 }
422
423private:
424
425 // Note: this parameter is NOT in the yaml config file,
426 // but subclasses can provide any parameter type supported
427 // by sparta::Parameter<T> which may be too complicated to
428 // clearly describe using simple yaml syntax
429 std::unique_ptr<sparta::Parameter<double>> degrees_;
430
431 // The base class will clobber together whatever parameter values it
432 // found in the yaml file, and give us a chance to add custom parameters
433 // to the same set
434 virtual void postCreate() override {
435 sparta::ParameterSet * ps = getParameters();
436 degrees_.reset(new sparta::Parameter<double>(
437 "degrees_", 360.0, "Number of degrees in a circle", ps));
438 }
439};
440
441double calculateAverageOfInternalCounters(
442 const std::vector<const sparta::CounterBase*> & counters)
443{
444 double agg = 0;
445 for (const auto & ctr : counters) {
446 agg += ctr->get();
447 }
448 return agg / counters.size();
449}
450
451void tryAccessSimDB()
452{
453 if (auto dbconn = GET_DB_FROM_CURRENT_SIMULATION(Stats)) {
454 //Run a simple query against the database just to verify
455 //the connection is open and accepting requests
456 (void) dbconn->findObject("ObjectManagersInDatabase", 1);
457 }
458}
459
460ExampleSimulator::ExampleSimulator(const std::string& topology,
461 sparta::Scheduler & scheduler,
462 uint32_t num_cores,
463 uint64_t instruction_limit,
464 bool show_factories) :
465 sparta::app::Simulation("sparta_core_example", &scheduler),
466 cpu_topology_(topology),
467 num_cores_(num_cores),
468 instruction_limit_(instruction_limit),
469 show_factories_(show_factories),
470 simdb_tester_(std::make_shared<sparta_simdb::DatabaseTester>())
471{
472 // Set up the CPU Resource Factory to be available through ResourceTreeNode
474
475 // Set up all node extension factories to be available during the simulation
476 // - This is only needed for parameter sets that also want to add some methods
477 // to their tree node extension, and/or for those that want to extend node
478 // parameter sets with more complicated sparta::Parameter<T> data types
479 addTreeNodeExtensionFactory_("circle", [](){return new CircleExtensions;});
480
481 // Initialize example simulation controller
482 controller_.reset(new ExampleSimulator::ExampleController(this));
484
485 // Register a custom calculation method for 'combining' a context counter's
486 // internal counters into one number. In this example simulator, let's just
487 // use an averaging function called "avg" which we can then invoke from report
488 // definition YAML files.
489 sparta::trigger::ContextCounterTrigger::registerContextCounterCalcFunction(
490 "avg", &calculateAverageOfInternalCounters);
491
492 //SQLite namespaces: NumericMeta & StringMeta
493 REGISTER_SIMDB_NAMESPACE(NumericMeta, SQLite);
494 REGISTER_SIMDB_SCHEMA_BUILDER(NumericMeta, buildSchemaA);
495
496 REGISTER_SIMDB_NAMESPACE(StringMeta, SQLite);
497 REGISTER_SIMDB_SCHEMA_BUILDER(StringMeta, buildSchemaB);
498
499 //HDF5 namespace: NumericVals
500 REGISTER_SIMDB_NAMESPACE(NumericVals, HDF5);
501 REGISTER_SIMDB_SCHEMA_BUILDER(NumericVals, buildSchemaC);
502
503 //Proxy factory registration
504 REGISTER_SIMDB_PROXY_CREATE_FUNCTION(HDF5, createHDF5Proxy);
505}
506
507void ExampleSimulator::registerStatCalculationFcns_()
508{
509 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, stdev_x3);
510 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_greaterThan2StdDev);
511 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_p_StdDev_mean_p_2StdDev);
512 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_mean_p_StdDev);
513 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_m_StdDev_mean);
514 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_mean_m_2StdDev_mean_m_StdDev);
515 REGISTER_HISTOGRAM_STAT_CALC_FCN(CycleHistogramTreeNode, fraction_coverage_lesserThan2StdDev);
516 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, stdev_x3_h);
517 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_greaterThan2StdDev_h);
518 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_p_StdDev_mean_p_2StdDev_h);
519 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_mean_p_StdDev_h);
520 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_m_StdDev_mean_h);
521 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_mean_m_2StdDev_mean_m_StdDev_h);
522 REGISTER_HISTOGRAM_STAT_CALC_FCN(HistogramTreeNode, fraction_coverage_lesserThan2StdDev_h);
523}
524
525ExampleSimulator::~ExampleSimulator()
526{
527 getRoot()->enterTeardown(); // Allow deletion of nodes without error now
528 if (on_triggered_notifier_registered_) {
529 getRoot()->DEREGISTER_FOR_NOTIFICATION(
530 onTriggered_, std::string, "sparta_expression_trigger_fired");
531 }
532
533 if (simdb_perf_async_ctrl_enabled_) {
534 std::set<std::string> simdb_files;
535 if (auto dbconn = GET_DB_FOR_COMPONENT(NumericMeta, this)) {
536 simdb_files.insert(dbconn->getDatabaseFile());
537 }
538
539 for (const auto & db_file : simdb_files) {
540 simdb_tester_->verifyRecords(db_file);
541 }
542 }
543}
544
546auto ExampleSimulator::getCPUFactory_() -> core_example::CPUFactory*{
547 auto sparta_res_factory = getResourceSet()->getResourceFactory("cpu");
548 auto cpu_factory = dynamic_cast<core_example::CPUFactory*>(sparta_res_factory);
549 return cpu_factory;
550}
551
552void ExampleSimulator::buildTree_()
553{
554 // TREE_BUILDING Phase. See sparta::PhasedObject::TreePhase
555 // Register all the custom stat calculation functions with (cycle)histogram nodes
556 registerStatCalculationFcns_();
557
558 auto cpu_factory = getCPUFactory_();
559
560 // Set the cpu topology that will be built
561 cpu_factory->setTopology(cpu_topology_, num_cores_);
562
563 // Create a single CPU
565 "cpu",
568 "CPU Node",
569 cpu_factory);
570 to_delete_.emplace_back(cpu_tn);
571
572 // Tell the factory to build the resources now
573 cpu_factory->buildTree(getRoot());
574
575 // Print the registered factories
576 if(show_factories_){
577 std::cout << "Registered factories: \n";
578 for(const auto& f : getCPUFactory_()->getResourceNames()){
579 std::cout << "\t" << f << std::endl;
580 }
581 }
582
583 // Validate tree node extensions during tree building
584 for(uint32_t i = 0; i < num_cores_; ++i){
585 sparta::TreeNode * dispatch = getRoot()->getChild("cpu.core0.dispatch", false);
586 if (dispatch) {
587 sparta::TreeNode::ExtensionsBase * extensions = dispatch->getExtension("user_data");
588
589 // If present, validate the parameter values as given in the extension / configuration file
590 if (extensions != nullptr) {
591 const sparta::ParameterSet * dispatch_prms = extensions->getParameters();
592 sparta_assert(dispatch_prms != nullptr);
593 validateParameter<std::string>(*dispatch_prms, "when_", "buildTree_");
594 validateParameter<std::string>(*dispatch_prms, "why_", "checkAvailability");
595 }
596
597 // There might be an extension given in --extension-file that is not found
598 // at all in any --config-file given at the command prompt. Verify that if
599 // present, the value is as expected.
600 extensions = dispatch->getExtension("square");
601 if (extensions != nullptr) {
602 const sparta::ParameterSet * dispatch_prms = extensions->getParameters();
603 sparta_assert(dispatch_prms != nullptr);
604 validateParameter<std::string>(*dispatch_prms, "edges_", "4");
605 }
606 }
607
608 // See if there are any extensions for the alu0/alu1 nodes
609 sparta::TreeNode * alu0 = getRoot()->getChild("cpu.core0.alu0");
610 sparta::TreeNode * alu1 = getRoot()->getChild("cpu.core0.alu1");
611 if (alu0) {
612 sparta::TreeNode::ExtensionsBase * extensions = alu0->getExtension("difficulty");
613 if (extensions != nullptr) {
614 const sparta::ParameterSet * alu0_prms = extensions->getParameters();
615 sparta_assert(alu0_prms != nullptr);
616
617 validateParameter<std::string>(*alu0_prms, "color_", "black");
618 validateParameter<std::string>(*alu0_prms, "shape_", "diamond");
619 }
620 }
621 if (alu1) {
622 sparta::TreeNode::ExtensionsBase * extensions = alu1->getExtension("difficulty");
623 if (extensions != nullptr) {
624 const sparta::ParameterSet * alu1_prms = extensions->getParameters();
625 sparta_assert(alu1_prms != nullptr);
626
627 validateParameter<std::string>(*alu1_prms, "color_", "green");
628 validateParameter<std::string>(*alu1_prms, "shape_", "circle");
629 }
630 }
631
632 // Once again, ask for a named extension for a tree node that was just created.
633 // The difference here is that the 'circle' extension also has a factory associated
634 // with it.
635 sparta::TreeNode * fpu = getRoot()->getChild("cpu.core0.fpu", false);
636 if (fpu) {
637 sparta::TreeNode::ExtensionsBase * extensions = fpu->getExtension("circle");
638
639 // If present, validate the parameter values as given in the extension / configuration file
640 if (extensions != nullptr) {
641 const sparta::ParameterSet * fpu_prms = extensions->getParameters();
642 sparta_assert(fpu_prms != nullptr);
643
644 validateParameter<std::string>(*fpu_prms, "color_", "green");
645 validateParameter<std::string>(*fpu_prms, "shape_", "round");
646 validateParameter<double> (*fpu_prms, "degrees_", 360.0);
647
648 // While most of the 'circle' extensions are given in --config-file options,
649 // there might be more parameters added in with --extension-file, so let's check
650 validateParameter<std::string>(*fpu_prms, "edges_", "0");
651
652 // We know the subclass type, so we should be able to safely dynamic cast
653 // to that type and call methods on it
654 const CircleExtensions * circle_subclass = dynamic_cast<const CircleExtensions*>(extensions);
655 circle_subclass->doSomethingElse();
656 }
657 }
658 }
659
660 // Attach two tree nodes to get the following:
661 // top
662 // core0
663 // dispatch
664 // baz_node
665 // params
666 // baz
667 // fpu
668 // baz_node
669 // params
670 // baz
671 //
672 // This is needed to reproduce a write-final-config bug where an arch file
673 // specifies 'top.core0.*.baz_node.params.baz: 300' and the ConfigEmitterYAML
674 // ends up throwing an exception due to the '*' which tripped up the tree node
675 // extensions code.
676 auto dispatch = getRoot()->getChild("cpu.core0.dispatch");
677 auto fpu = getRoot()->getChild("cpu.core0.fpu");
678
679 dispatch_baz_.reset(new sparta::Baz(
680 dispatch, "Dummy node under top.cpu.core0.dispatch (to reproduce a SPARTA bug)"));
681
682 fpu_baz_.reset(new sparta::Baz(
683 fpu, "Dummy node under top.cpu.core0.fpu (to reproduce a SPARTA bug)"));
684}
685
686void ExampleSimulator::configureTree_()
687{
688 //Context-aware SimDB access
689 std::pair<std::string, std::string> sqlite_db_files;
690 if (auto dbconn = GET_DB_FOR_COMPONENT(NumericMeta, this)) {
691 const TestSQLiteSchemaA data = simdb_tester_->
692 createAndStoreRecordForSQLiteSchemaA();
693
694 const double first = data.numbers.First;
695 const double second = data.numbers.Second;
696 dbconn->getTable("Numbers")->createObjectWithArgs(
697 "First", first, "Second", second);
698
699 const std::string meta_name = data.metadata.Name;
700 const double meta_value = data.metadata.Value;
701 dbconn->getTable("Metadata")->createObjectWithArgs(
702 "Name", meta_name, "Value", meta_value);
703
704 sqlite_db_files.first = dbconn->getDatabaseFile();
705
706 //Verification of the two records we just made above
707 //will occur at the end of the simulation.
708 }
709
710 if (auto dbconn = GET_DB_FOR_COMPONENT(StringMeta, this)) {
711 const TestSQLiteSchemaB data = simdb_tester_->
712 createAndStoreRecordForSQLiteSchemaB();
713
714 const std::string first = data.strings.First;
715 const std::string second = data.strings.Second;
716 dbconn->getTable("Strings")->createObjectWithArgs(
717 "First", first, "Second", second);
718
719 const std::string meta_name = data.metadata.Name;
720 const std::string meta_value = data.metadata.Value;
721 dbconn->getTable("Metadata")->createObjectWithArgs(
722 "Name", meta_name, "Value", meta_value);
723
724 sqlite_db_files.second = dbconn->getDatabaseFile();
725
726 //Verification of the two records we just made above
727 //will occur at the end of the simulation.
728 }
729
730 //Both of the ObjectManager's used above should have put the
731 //created records into the same file.
732 sparta_assert(sqlite_db_files.first == sqlite_db_files.second);
733
734 //Context-unaware SimDB access
735 tryAccessSimDB();
736
737 validateTreeNodeExtensions_();
738
739 // In TREE_CONFIGURING phase
740 // Configuration from command line is already applied
741
742 // Read these parameter values to avoid 'unread unbound parameter' exceptions:
743 // top.cpu.core0.dispatch.baz_node.params.baz
744 // top.cpu.core0.fpu.baz_node.params.baz
745 dispatch_baz_->readParams();
746 fpu_baz_->readParams();
747
748 sparta::ParameterBase* max_instrs =
749 getRoot()->getChildAs<sparta::ParameterBase>("cpu.core0.rob.params.num_insts_to_retire");
750
751 // Safely assign as string for now in case parameter type changes.
752 // Direct integer assignment without knowing parameter type is not yet available through C++ API
753 if(instruction_limit_ != 0){
754 max_instrs->setValueFromString(sparta::utils::uint64_to_str(instruction_limit_));
755 }
756
757 testing_notification_source_.reset(new sparta::NotificationSource<uint64_t>(
758 this->getRoot()->getSearchScope()->getChild("top.cpu.core0.rob"),
759 "testing_notif_channel",
760 "Notification channel for testing purposes only",
761 "testing_notif_channel"));
762
763 toggle_trigger_notification_source_.reset(new sparta::NotificationSource<uint64_t>(
764 getRoot()->getSearchScope()->getChild("top.cpu.core0.rob"),
765 "stats_profiler",
766 "Notification channel for testing report toggling on/off (statistics profiling)",
767 "stats_profiler"));
768
769 legacy_warmup_report_starter_.reset(new sparta::NotificationSource<uint64_t>(
770 getRoot(),
771 "all_threads_warmup_instruction_count_retired_re4",
772 "Legacy notificiation channel for testing purposes only",
773 "all_threads_warmup_instruction_count_retired_re4"));
774
775 getRoot()->REGISTER_FOR_NOTIFICATION(
776 onTriggered_, std::string, "sparta_expression_trigger_fired");
777 on_triggered_notifier_registered_ = true;
778
779 simdb_perf_async_ctrl_enabled_ = sparta::IsFeatureValueEnabled(
780 getFeatureConfiguration(), "simdb-perf-async-ctrl") > 0;
781}
782
783void ExampleSimulator::bindTree_()
784{
785 // In TREE_FINALIZED phase
786 // Tree is finalized. Taps placed. No new nodes at this point
787 // Bind appropriate ports
788
789 //Tell the factory to bind all units
790 auto cpu_factory = getCPUFactory_();
791 cpu_factory->bindTree(getRoot());
792
793 sparta::SpartaHandler cb = sparta::SpartaHandler::from_member<
794 ExampleSimulator, &ExampleSimulator::postRandomNumber_>(
795 this, "ExampleSimulator::postRandomNumber_");
796
797 random_number_trigger_.reset(new sparta::trigger::ExpressionCounterTrigger(
798 "RandomNumber", cb, "cpu.core0.rob.stats.total_number_retired 7500", false, this->getRoot()));
799
800 toggle_notif_trigger_.reset(new sparta::trigger::ExpressionTimeTrigger(
801 "ToggleNotif",
802 CREATE_SPARTA_HANDLER(ExampleSimulator, postToToggleTrigger_),
803 "1 ns",
804 getRoot()));
805
806 lazy_table_create_trigger_.reset(new sparta::trigger::ExpressionTrigger(
807 "DelayedTableCreate",
808 CREATE_SPARTA_HANDLER(ExampleSimulator, addToStatsSchema_),
809 "top.cpu.core0.rob.stats.total_number_retired >= 12000",
810 getRoot()->getSearchScope(),
811 nullptr));
812
813 if (auto db_root = GET_DB_FROM_CURRENT_SIMULATION(Stats)) {
814 lazy_table_proxy_ = db_root->getConditionalTable("Lazy");
815 sparta_assert(lazy_table_proxy_ != nullptr);
816 sparta_assert(lazy_table_proxy_->getTable() == nullptr);
817 }
818
819 static const uint32_t warmup_multiplier = 1000;
820 auto gen_expression = [](const uint32_t core_idx) {
821 std::ostringstream oss;
822 oss << "cpu.core" << core_idx << ".rob.stats.total_number_retired >= "
823 << ((core_idx+1) * warmup_multiplier);
824 return oss.str();
825 };
826
827 num_cores_still_warming_up_ = num_cores_;
828 core_warmup_listeners_.reserve(num_cores_);
829
830 for (uint32_t core_idx = 0; core_idx < num_cores_; ++core_idx) {
831 core_warmup_listeners_.emplace_back(
832 new sparta::trigger::ExpressionTrigger(
833 "LegacyWarmupNotifications",
834 CREATE_SPARTA_HANDLER(ExampleSimulator, onLegacyWarmupNotification_),
835 gen_expression(core_idx),
836 getRoot(),
837 nullptr));
838 }
839}
840
841void ExampleSimulator::onLegacyWarmupNotification_()
842{
843 sparta_assert(num_cores_still_warming_up_ > 0);
844 --num_cores_still_warming_up_;
845 if (num_cores_still_warming_up_ == 0) {
846 legacy_warmup_report_starter_->postNotification(1);
847 }
848}
849
850const sparta::CounterBase* ExampleSimulator::findSemanticCounter_(CounterSemantic sem) const {
851 switch(sem){
853 return getRoot()->getChildAs<const sparta::CounterBase>("cpu.core0.rob.stats.total_number_retired");
854 break;
855 default:
856 return nullptr;
857 }
858}
859
860void ExampleSimulator::postRandomNumber_()
861{
862 const size_t random = rand() % 25;
863 testing_notification_source_->postNotification(random);
864 random_number_trigger_->reschedule();
865
866 if (dispatch_baz_) {
867 dispatch_baz_->checkDbAccess(true);
868 }
869
870 if (!simdb_perf_async_ctrl_enabled_) {
871 return;
872 }
873
874 using ObjectDatabase = simdb::ObjectManager::ObjectDatabase;
875
876 // In the SimDB-related code below, note that GET_DB_FOR_COMPONENT is
877 // returning a unique_ptr<ObjectDatabase>, not a shared_ptr.
878 //
879 // The ability to request database connections and get unique_ptr's
880 // back is important because it demonstrates that different parts
881 // of the simulator can write data into the same database, into their
882 // own namespace's schema, sharing the same worker thread (which is
883 // just implementation detail, but it's important for performance and
884 // scalability) with no coordination required between the simulator
885 // components / call sites.
886 //
887 // Also note that we have a mixture of DB writes going on here. There
888 // are two separate physical database files: one is SQLite, and the
889 // other is HDF5. The SQLite file has two namespaces in it, named
890 // NumericMeta and StringMeta; the HDF5 file just has one namespace
891 // in it called NumericVals. These namespaces, their database formats,
892 // and the namespace schema definition was registered with SimDB from
893 // the ExampleSimulator's constructor earlier on.
894
895 if (auto obj_db = GET_DB_FOR_COMPONENT(NumericMeta, this)) {
896 // Helper class which writes a data record on the worker thread
897 class TestWriter : public simdb::WorkerTask
898 {
899 public:
900 TestWriter(ObjectDatabase * obj_db,
901 sparta_simdb::DatabaseTester * db_tester) :
902 obj_db_(obj_db),
903 simdb_tester_(db_tester)
904 {}
905
906 void completeTask() override {
907 const TestSQLiteSchemaA data = simdb_tester_->
908 createAndStoreRecordForSQLiteSchemaA();
909
910 obj_db_->getTable("Numbers")->createObjectWithArgs(
911 "First", data.numbers.First,
912 "Second", data.numbers.Second);
913
914 obj_db_->getTable("Metadata")->createObjectWithArgs(
915 "Name", data.metadata.Name,
916 "Value", data.metadata.Value);
917 }
918
919 private:
920 ObjectDatabase * obj_db_ = nullptr;
921 sparta_simdb::DatabaseTester * simdb_tester_ = nullptr;
922 };
923
924 std::unique_ptr<simdb::WorkerTask> task(new TestWriter(
925 obj_db, simdb_tester_.get()));
926 obj_db->getTaskQueue()->addWorkerTask(std::move(task));
927 }
928
929 if (auto obj_db = GET_DB_FOR_COMPONENT(StringMeta, this)) {
930 // Helper class which writes a data record on the worker thread
931 class TestWriter : public simdb::WorkerTask
932 {
933 public:
934 TestWriter(ObjectDatabase * obj_db,
935 sparta_simdb::DatabaseTester * db_tester) :
936 obj_db_(obj_db),
937 simdb_tester_(db_tester)
938 {}
939
940 void completeTask() override {
941 const TestSQLiteSchemaB data = simdb_tester_->
942 createAndStoreRecordForSQLiteSchemaB();
943
944 obj_db_->getTable("Strings")->createObjectWithArgs(
945 "First", data.strings.First,
946 "Second", data.strings.Second);
947
948 obj_db_->getTable("Metadata")->createObjectWithArgs(
949 "Name", data.metadata.Name,
950 "Value", data.metadata.Value);
951 }
952
953 private:
954 ObjectDatabase * obj_db_ = nullptr;
955 sparta_simdb::DatabaseTester * simdb_tester_ = nullptr;
956 };
957
958 std::unique_ptr<simdb::WorkerTask> task(new TestWriter(
959 obj_db, simdb_tester_.get()));
960 obj_db->getTaskQueue()->addWorkerTask(std::move(task));
961 }
962
963 if (auto obj_db = GET_DB_FOR_COMPONENT(NumericVals, this)) {
964 // Helper class which writes a data record on the worker thread
965 class TestWriter : public simdb::WorkerTask
966 {
967 public:
968 TestWriter(ObjectDatabase * obj_db,
969 sparta_simdb::DatabaseTester * db_tester) :
970 obj_db_(obj_db),
971 simdb_tester_(db_tester)
972 {}
973
974 void completeTask() override {
975 const TestHDF5SchemaC data = simdb_tester_->
976 createAndStoreRecordForHDF5SchemaC();
977
978 obj_db_->getTable("Numbers")->createObjectWithVals(
979 data.x, data.y, data.z);
980 }
981
982 private:
983 ObjectDatabase * obj_db_ = nullptr;
984 sparta_simdb::DatabaseTester * simdb_tester_ = nullptr;
985 };
986
987 std::unique_ptr<simdb::WorkerTask> task(new TestWriter(
988 obj_db, simdb_tester_.get()));
989 obj_db->getTaskQueue()->addWorkerTask(std::move(task));
990 }
991}
992
993void ExampleSimulator::postToToggleTrigger_()
994{
995 typedef std::pair<uint64_t,uint64_t> ValueCount;
996 static std::queue<ValueCount> values;
997
998 if (values.empty()) {
999 values.push({0,15});
1000 values.push({1,25});
1001 values.push({0,15});
1002 values.push({1,25});
1003 values.push({0,15});
1004
1005 ValueCount tmp = values.front();
1006 values.push(tmp);
1007 }
1008
1009 if (values.front().second == 0) {
1010 values.pop();
1011 ValueCount tmp = values.front();
1012 values.push(tmp);
1013 } else {
1014 --values.front().second;
1015 }
1016
1017 const ValueCount & current_value = values.front();
1018 const uint64_t value_to_post = current_value.first;
1019 toggle_trigger_notification_source_->postNotification(value_to_post);
1020 toggle_notif_trigger_->reschedule();
1021}
1022
1023void ExampleSimulator::addToStatsSchema_()
1024{
1025 if (auto db_root = getDatabaseRoot()) {
1026 if (auto db_namespace = db_root->getNamespace("Stats")) {
1027 db_namespace->addToSchema([&](simdb::Schema & schema) {
1028 using dt = simdb::ColumnDataType;
1029
1030 schema.addTable("Lazy")
1031 .addColumn("Foo", dt::string_t)
1032 .addColumn("Bar", dt::int32_t);
1033 });
1034
1035 lazy_table_create_trigger_.reset(new sparta::trigger::ExpressionTrigger(
1036 "DelayedTableCreate",
1037 CREATE_SPARTA_HANDLER(ExampleSimulator, addToLazySchemaTable_),
1038 "top.cpu.core0.rob.stats.total_number_retired >= 40000",
1039 getRoot()->getSearchScope(),
1040 nullptr));
1041 }
1042 }
1043}
1044
1045void ExampleSimulator::addToLazySchemaTable_()
1046{
1047 if (lazy_table_proxy_->isWritable()) {
1048 const std::string foo = "hello_world";
1049 const int bar = 45;
1050
1051 auto recordA = lazy_table_proxy_->getTable()->createObjectWithArgs(
1052 "Foo", foo, "Bar", bar);
1053
1054 auto db_root = GET_DB_FROM_CURRENT_SIMULATION(Stats);
1055 sparta_assert(db_root != nullptr);
1056
1057 auto recordB = db_root->getTable("Lazy")->createObjectWithArgs(
1058 "Foo", foo, "Bar", bar);
1059
1060 sparta_assert(recordA->getPropertyString("Foo") ==
1061 recordB->getPropertyString("Foo"));
1062
1063 sparta_assert(recordA->getPropertyInt32("Bar") ==
1064 recordB->getPropertyInt32("Bar"));
1065 }
1066}
1067
1068void ExampleSimulator::onTriggered_(const std::string & msg)
1069{
1070 std::cout << " [trigger] " << msg << std::endl;
1071}
1072
1073void ExampleSimulator::validateTreeNodeExtensions_()
1074{
1075 // From the yaml file, the 'cat' extension had parameters 'name_' and 'language_'
1076 sparta::TreeNode * core_tn = getRoot()->getChild("cpu.core0.lsu");
1077 if (core_tn == nullptr) {
1078 return;
1079 }
1080 sparta::TreeNode::ExtensionsBase * cat_base = core_tn->getExtension("cat");
1081 if (cat_base == nullptr) {
1082 return;
1083 }
1084 sparta::ParameterSet * cat_prms = cat_base->getParameters();
1085
1086 validateParameter<std::string>(*cat_prms, "name_", "Tom");
1087
1088 // The expected "meow" parameter value, given in a --config-file, may have
1089 // been overridden in a provided --extension-file
1090 validateParameter<std::string>(*cat_prms, "language_", {"meow", "grrr"});
1091
1092 // Same goes for the 'mouse' extension...
1093 sparta::TreeNode::ExtensionsBase * mouse_base = core_tn->getExtension("mouse");
1094 if (mouse_base == nullptr) {
1095 return;
1096 }
1097 sparta::ParameterSet * mouse_prms = mouse_base->getParameters();
1098
1099 validateParameter<std::string>(*mouse_prms, "name_", "Jerry");
1100 validateParameter<std::string>(*mouse_prms, "language_", "squeak");
1101
1102 // Another extension called 'circle' was put on a different tree node...
1103 sparta::TreeNode * fpu_tn = getRoot()->getChild("cpu.core0.fpu");
1104 if (fpu_tn == nullptr) {
1105 return;
1106 }
1107 sparta::TreeNode::ExtensionsBase * circle_base = fpu_tn->getExtension("circle");
1108 if (circle_base == nullptr) {
1109 return;
1110 }
1111 sparta::ParameterSet * circle_prms = circle_base->getParameters();
1112
1113 // The 'circle' extension had 'color_' and 'shape_' parameters given in the yaml file:
1114 validateParameter<std::string>(*circle_prms, "color_", "green");
1115 validateParameter<std::string>(*circle_prms, "shape_", "round");
1116
1117 // That subclass also gave a parameter value not found in the yaml file at all:
1118 validateParameter<double>(*circle_prms, "degrees_", 360.0);
1119
1120 // Further, the 'circle' extension gave a subclass factory for the CircleExtensions class...
1121 // so we should be able to dynamic_cast to the known type:
1122 const CircleExtensions * circle_subclass = dynamic_cast<const CircleExtensions*>(circle_base);
1123 circle_subclass->doSomethingElse();
1124
1125 // Lastly, verify that there are no issues with putting extensions on the 'top' node
1126 sparta::TreeNode * top_node = getRoot();
1127 if (top_node == nullptr) {
1128 return;
1129 }
1130 sparta::TreeNode::ExtensionsBase * top_extensions = top_node->getExtension("apple");
1131 if (top_extensions == nullptr) {
1132 return;
1133 }
1134 sparta::ParameterSet *top_prms = top_extensions->getParameters();
1135 validateParameter<std::string>(*top_prms, "color_", "red");
1136
1137 // The 'core0.lsu' node has two named extensions, so asking that node for
1138 // unqualified extensions (no name specified) should throw
1139 try {
1140 core_tn->getExtension();
1141 throw sparta::SpartaException("Expected an exception to be thrown for unqualified "
1142 "call to TreeNode::getExtension()");
1143 } catch (...) {
1144 }
1145
1146 // While the 'core0.fpu' node only had one extension, so we should be able to
1147 // access it without giving any particular name
1148 sparta::TreeNode::ExtensionsBase * circle_base_by_default = fpu_tn->getExtension();
1149 circle_prms = circle_base_by_default->getParameters();
1150
1151 validateParameter<std::string>(*circle_prms, "color_", "green");
1152 validateParameter<std::string>(*circle_prms, "shape_", "round");
1153 validateParameter<double>(*circle_prms, "degrees_", 360.0);
1154
1155 // Check to see if additional parameters were added to this tree node's extension
1156 // (--config-file and --extension-file options can be given at the same time, and
1157 // we should have access to the merged result of both ParameterTree's)
1158 if (circle_prms->getNumParameters() > 3) {
1159 validateParameter<std::string>(*circle_prms, "edges_", "0");
1160 }
1161
1162 // Verify that we can work with extensions on 'top.core0.dispatch.baz_node', which
1163 // was added to this example simulator to reproduce bug
1164 sparta::TreeNode * baz_node = getRoot()->getChild("cpu.core0.dispatch.baz_node", false);
1165 if (baz_node) {
1166 sparta::TreeNode::ExtensionsBase * extensions = baz_node->getExtension("baz_ext");
1167 if (extensions) {
1168 const sparta::ParameterSet * baz_prms = extensions->getParameters();
1169 sparta_assert(baz_prms != nullptr);
1170 validateParameter<std::string>(*baz_prms, "ticket_", "663");
1171 }
1172 }
1173}
1174
1175ExampleSimulator::ExampleController::ExampleController(
1176 const sparta::app::Simulation * sim) :
1177 sparta::app::Simulation::SimulationController(sim)
1178{
1179 sparta::app::Simulation::SimulationController::addNamedCallback_(
1180 "eat", CREATE_SPARTA_HANDLER(ExampleController, customEatCallback_));
1181
1182 sparta::app::Simulation::SimulationController::addNamedCallback_(
1183 "sleep", CREATE_SPARTA_HANDLER(ExampleController, customSleepCallback_));
1184}
1185
1186void ExampleSimulator::ExampleController::pause_(const sparta::app::Simulation * sim)
1187{
1188 std::cout << " [control] Controller PAUSE method has been called for simulation '"
1189 << sim->getSimName() << "'" << std::endl;
1190}
1191
1192void ExampleSimulator::ExampleController::resume_(const sparta::app::Simulation * sim)
1193{
1194 std::cout << " [control] Controller RESUME method has been called for simulation '"
1195 << sim->getSimName() << "'" << std::endl;
1196}
1197
1198void ExampleSimulator::ExampleController::terminate_(const sparta::app::Simulation * sim)
1199{
1200 std::cout << " [control] Controller TERMINATE method has been called for simulation '"
1201 << sim->getSimName() << "'" << std::endl;
1202 const_cast<sparta::Scheduler*>(sim->getScheduler())->stopRunning();
1203}
1204
1205void ExampleSimulator::ExampleController::customEatCallback_()
1206{
1207 std::cout << " [control] Controller CUSTOM method has been called ('eat')" << std::endl;
1208}
1209
1210void ExampleSimulator::ExampleController::customSleepCallback_()
1211{
1212 std::cout << " [control] Controller CUSTOM method has been called ('sleep')" << std::endl;
1213}
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.
Helper class used to trivially extend TreeNode parameter sets (but not any additional functionality b...
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.
const T getParameterValueAs(const std::string &name) const
Finds a parameter and gets its value as the templated type.
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.
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 ...
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.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Base class used to extend TreeNode parameter sets.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
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:303
std::string getLocation() const override final
static constexpr char GROUP_NAME_NONE[]
Group name indicating that a node belongs to no group.
Definition TreeNode.hpp:314
TreeNode()=delete
Not default-constructable.
ExtensionsBase * getExtension()
Get an extension without needing to specify any particular type string. If no extensions exist,...
ExtensionsBase * getExtension(const std::string &extension_name)
Get an extension object by type string. Returns nullptr if not found (unrecognized).
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.
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...
@ CSEM_INSTRUCTIONS
Instruction count semantic (usually core0)
std::shared_ptr< SimulationController > controller_
Custom controller to handle various simulation events.
simdb::DatabaseRoot * getDatabaseRoot() const
Get the database root for this simulation.
sparta::RootTreeNode * getRoot() noexcept
Returns the tree root.
const FeatureConfiguration * getFeatureConfiguration() const
Returns this simulator's feature configuration.
void addTreeNodeExtensionFactory_(const std::string &extension_name, std::function< TreeNode::ExtensionsBase *()> creator)
Include an extension factory for this simulation's device tree nodes. They will be given to specific ...
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.
std::enable_if< std::is_same< T, app::FeatureConfiguration >::value, bool >::type IsFeatureValueEnabled(const T &cfg, const std::string &feature_name)