config: Add the ability to read a config file using C++ and Python
[gem5.git] / src / sim / cxx_manager.cc
1 /*
2 * Copyright (c) 2014 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Andrew Bardsley
38 */
39
40 #include <cstdlib>
41 #include <sstream>
42
43 #include "base/str.hh"
44 #include "debug/CxxConfig.hh"
45 #include "mem/mem_object.hh"
46 #include "sim/cxx_manager.hh"
47 #include "sim/serialize.hh"
48
49 CxxConfigManager::CxxConfigManager(CxxConfigFileBase &configFile_) :
50 configFile(configFile_), flags(configFile_.getFlags()),
51 simObjectResolver(*this)
52 {
53 }
54
55 const CxxConfigDirectoryEntry &
56 CxxConfigManager::findObjectType(const std::string &object_name,
57 std::string &object_type)
58 {
59 if (!configFile.objectExists(object_name))
60 throw Exception(object_name, "Can't find sim object");
61
62 if (!configFile.getParam(object_name, "type", object_type))
63 throw Exception(object_name, "Sim object has no 'type' field");
64
65 if (cxx_config_directory.find(object_type) ==
66 cxx_config_directory.end())
67 {
68 throw Exception(object_name, csprintf(
69 "No sim object type %s is available", object_type));
70 }
71
72 const CxxConfigDirectoryEntry *entry = cxx_config_directory[object_type];
73
74 return *entry;
75 }
76
77 std::string
78 CxxConfigManager::rename(const std::string &from_name)
79 {
80 for (auto i = renamings.begin(); i != renamings.end(); ++ i) {
81 const Renaming &renaming = *i;
82
83 if (from_name.find(renaming.fromPrefix) == 0) {
84 return renaming.toPrefix +
85 from_name.substr(renaming.fromPrefix.length());
86 }
87 }
88
89 return from_name;
90 }
91
92 std::string
93 CxxConfigManager::unRename(const std::string &to_name)
94 {
95 for (auto i = renamings.begin(); i != renamings.end(); ++ i) {
96 const Renaming &renaming = *i;
97
98 if (to_name.find(renaming.toPrefix) == 0) {
99 return renaming.fromPrefix +
100 to_name.substr(renaming.toPrefix.length());
101 }
102 }
103
104 return to_name;
105 }
106
107 static
108 std::string formatParamList(const std::vector<std::string> &param_values)
109 {
110 std::ostringstream params;
111
112 auto i = param_values.begin();
113 auto end_i = param_values.end();
114
115 params << '[';
116 while (i != end_i) {
117 params << (*i);
118 ++i;
119
120 if (i != end_i)
121 params << ", ";
122 }
123 params << ']';
124
125 return params.str();
126 }
127
128 SimObject *
129 CxxConfigManager::findObject(const std::string &object_name,
130 bool visit_children)
131 {
132 std::string instance_name = rename(object_name);
133
134 if (object_name == "Null")
135 return NULL;
136
137 /* Already constructed */
138 if (objectsByName.find(instance_name) != objectsByName.end())
139 return objectsByName[instance_name];
140
141 if (inVisit.find(instance_name) != inVisit.end())
142 throw Exception(instance_name, "Cycle in configuration");
143
144 std::string object_type;
145 const CxxConfigDirectoryEntry &entry =
146 findObjectType(object_name, object_type);
147
148 SimObject *object = NULL;
149
150 CxxConfigParams *object_params = findObjectParams(object_name);
151
152 try {
153 DPRINTF(CxxConfig, "Configuring sim object references for: %s"
154 " (%s from object %s)\n", instance_name, object_type,
155 object_name);
156
157 /* Remember the path back to the top of the recursion to detect
158 * cycles */
159 inVisit.insert(instance_name);
160
161 /* Resolve pointed-to SimObjects by recursing into them */
162 for (auto i = entry.parameters.begin();
163 i != entry.parameters.end(); ++i)
164 {
165 const CxxConfigDirectoryEntry::ParamDesc *param = (*i).second;
166
167 if (param->isSimObject) {
168 if (param->isVector) {
169 std::vector<std::string> sub_object_names;
170
171 if (!configFile.getParamVector(object_name, param->name,
172 sub_object_names))
173 {
174 throw Exception(object_name, csprintf(
175 "Element not found: %s", param->name));
176 }
177
178 std::vector<SimObject *> sub_objects;
179
180 for (auto n = sub_object_names.begin();
181 n != sub_object_names.end(); ++n)
182 {
183 SimObject *sub_object = findObject(*n,
184 visit_children);
185
186 if (sub_object)
187 sub_objects.push_back(sub_object);
188 }
189
190 if (!object_params->setSimObjectVector(param->name,
191 sub_objects))
192 {
193 throw Exception(object_name, csprintf(
194 "Can't assign sim object element %s from \"%s\"",
195 param->name, formatParamList(sub_object_names)));
196 }
197
198 DPRINTF(CxxConfig, "Setting sim object(s): %s.%s=%s\n",
199 object_name, param->name,
200 formatParamList(sub_object_names));
201 } else {
202 std::string sub_object_name;
203
204 if (!configFile.getParam(object_name, param->name,
205 sub_object_name))
206 {
207 throw Exception(object_name, csprintf(
208 "Element not found: %s", param->name));
209 }
210
211 SimObject *sub_object = findObject(sub_object_name,
212 visit_children);
213
214 if (sub_object) {
215 if (!object_params->setSimObject(param->name,
216 sub_object))
217 {
218 throw Exception(object_name, csprintf(
219 "Can't assign sim object element %s from"
220 " \"%s\"", param->name, sub_object_name));
221 }
222 }
223
224 DPRINTF(CxxConfig, "Setting sim object(s):"
225 " %s.%s=%s\n", object_name, param->name,
226 sub_object_name);
227 }
228 }
229 }
230
231 DPRINTF(CxxConfig, "Creating SimObject: %s\n", instance_name);
232 object = object_params->simObjectCreate();
233
234 if (!object) {
235 throw Exception(object_name, csprintf("Couldn't create object of"
236 " type: %s", object_type));
237 }
238
239 objectsByName[instance_name] = object;
240 objectParamsByName[instance_name] = object_params;
241
242 if (visit_children) {
243 std::vector<std::string> children;
244 configFile.getObjectChildren(object_name, children, true);
245
246 /* Visit all your children */
247 for (auto i = children.begin(); i != children.end(); ++i)
248 findObject(*i, visit_children);
249 }
250 } catch (Exception &) {
251 delete object_params;
252 throw;
253 }
254
255 /* Mark that we've exited object
256 * construction and so 'find'ing this object again won't be a
257 * configuration loop */
258 inVisit.erase(object_name);
259 return object;
260 }
261
262 CxxConfigParams *
263 CxxConfigManager::findObjectParams(const std::string &object_name)
264 {
265 std::string instance_name = rename(object_name);
266
267 /* Already constructed */
268 if (objectParamsByName.find(instance_name) != objectParamsByName.end())
269 return objectParamsByName[instance_name];
270
271 std::string object_type;
272 const CxxConfigDirectoryEntry &entry =
273 findObjectType(object_name, object_type);
274
275 DPRINTF(CxxConfig, "Configuring parameters of object: %s (%s)\n",
276 instance_name, object_type);
277
278 CxxConfigParams *object_params = entry.makeParamsObject();
279
280 try {
281 /* Fill in the implicit parameters that don't necessarily
282 * appear in config files */
283 object_params->setName(instance_name);
284
285 /* Fill in parameters */
286 for (auto i = entry.parameters.begin();
287 i != entry.parameters.end(); ++i)
288 {
289 const CxxConfigDirectoryEntry::ParamDesc *param = (*i).second;
290
291 if (!param->isSimObject) {
292 /* Only handle non-SimObject parameters here (see below) */
293
294 if (param->isVector) {
295 std::vector<std::string> param_values;
296
297 if (!configFile.getParamVector(object_name, param->name,
298 param_values))
299 {
300 throw Exception(object_name, csprintf(
301 "Element not found for parameter: %s",
302 param->name));
303 }
304
305 if (!object_params->setParamVector(param->name,
306 param_values, flags))
307 {
308 throw Exception(instance_name, csprintf(
309 "Bad parameter value: .%s=X=\"%s\"",
310 param->name, formatParamList(param_values)));
311 }
312
313 DPRINTF(CxxConfig, "Setting parameter"
314 " %s.%s=%s\n", instance_name, param->name,
315 formatParamList(param_values));
316 } else {
317 std::string param_value;
318
319 if (!configFile.getParam(object_name, param->name,
320 param_value))
321 {
322 throw Exception(object_name, csprintf(
323 "Element not found for parameter: %s",
324 param->name));
325 }
326
327 if (!object_params->setParam(param->name, param_value,
328 flags))
329 {
330 throw Exception(instance_name, csprintf(
331 "Bad parameter value: .%s=X=\"%s\"",
332 param->name, param_value));
333 }
334
335 DPRINTF(CxxConfig, "Setting parameter %s.%s=%s\n",
336 instance_name, param->name, param_value);
337 }
338 }
339 }
340
341 /* Find the number of ports that will need binding and set the
342 * appropriate port_..._connection_count parameters */
343 for (auto i = entry.ports.begin(); i != entry.ports.end(); ++i) {
344 const CxxConfigDirectoryEntry::PortDesc *port = (*i).second;
345 std::vector<std::string> peers;
346
347 if (!configFile.getPortPeers(object_name, port->name, peers)) {
348 DPRINTF(CxxConfig, "Port not found: %s.%s,"
349 " assuming there are no connections\n",
350 instance_name, port->name);
351 }
352
353 unsigned int peer_count = peers.size();
354
355 /* It would be more efficient to split the peer list and
356 * save the values for peer binding later but that would
357 * require another annoying intermediate structure to
358 * hold for little performance increase */
359
360 if (!object_params->setPortConnectionCount(port->name,
361 peer_count))
362 {
363 throw Exception(instance_name, csprintf(
364 "Unconnected port: %s", port->name));
365 }
366
367 DPRINTF(CxxConfig, "Setting port connection count"
368 " for: %s.%s to %d\n",
369 instance_name, port->name, peer_count);
370 }
371
372 /* Set pointed-to SimObjects to NULL */
373 for (auto i = entry.parameters.begin();
374 i != entry.parameters.end(); ++i)
375 {
376 const CxxConfigDirectoryEntry::ParamDesc *param = (*i).second;
377
378 if (param->isSimObject) {
379 bool ret;
380
381 DPRINTF(CxxConfig, "Nulling sim object reference: %s.%s\n",
382 instance_name, param->name);
383
384 if (param->isVector) {
385 /* Clear the reference list. */
386 std::vector<SimObject *> empty;
387 ret = object_params->setSimObjectVector(param->name,
388 empty);
389 } else {
390 ret = object_params->setSimObject(param->name, NULL);
391 }
392
393 if (!ret) {
394 throw Exception(instance_name, csprintf(
395 "Error nulling sim object reference(s): %s",
396 param->name));
397 }
398 }
399 }
400 } catch (Exception &) {
401 delete object_params;
402 throw;
403 }
404
405 objectParamsByName[instance_name] = object_params;
406
407 return object_params;
408 }
409
410 void
411 CxxConfigManager::findAllObjects()
412 {
413 std::vector<std::string> objects;
414 configFile.getAllObjectNames(objects);
415
416 /* Sort the object names to get a consistent initialisation order
417 * even with config file reorganisation */
418 std::sort(objects.begin(), objects.end());
419
420 for (auto i = objects.begin(); i != objects.end(); ++i)
421 findObject(*i);
422
423 /* Set the traversal order for further iterators */
424 objectsInOrder.clear();
425 findTraversalOrder("root");
426 }
427
428 void
429 CxxConfigManager::findTraversalOrder(const std::string &object_name)
430 {
431 SimObject *object = findObject(object_name);
432
433 if (object) {
434 objectsInOrder.push_back(object);
435
436 std::vector<std::string> children;
437 configFile.getObjectChildren(object_name, children, true);
438
439 /* Visit all your children */
440 for (auto i = children.begin(); i != children.end(); ++i)
441 findTraversalOrder(*i);
442 }
443 }
444
445 void
446 CxxConfigManager::bindAllPorts()
447 {
448 for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++i)
449 bindObjectPorts(*i);
450 }
451
452 void
453 CxxConfigManager::bindPort(
454 SimObject *master_object, const std::string &master_port_name,
455 PortID master_port_index,
456 SimObject *slave_object, const std::string &slave_port_name,
457 PortID slave_port_index)
458 {
459 MemObject *master_mem_object = dynamic_cast<MemObject *>(master_object);
460 MemObject *slave_mem_object = dynamic_cast<MemObject *>(slave_object);
461
462 if (!master_mem_object) {
463 throw Exception(master_object->name(), csprintf(
464 "Object isn't a mem object and so can have master port:"
465 " %s[%d]", master_port_name, master_port_index));
466 }
467
468 if (!slave_mem_object) {
469 throw Exception(slave_object->name(), csprintf(
470 "Object isn't a mem object and so can have slave port:"
471 " %s[%d]", slave_port_name, slave_port_index));
472 }
473
474 /* FIXME, check slave_port_index against connection_count
475 * defined for port, need getPortConnectionCount and a
476 * getCxxConfigDirectoryEntry for each object. */
477
478 /* It would be nice to be able to catch the errors from these calls. */
479 BaseMasterPort &master_port = master_mem_object->getMasterPort(
480 master_port_name, master_port_index);
481 BaseSlavePort &slave_port = slave_mem_object->getSlavePort(
482 slave_port_name, slave_port_index);
483
484 if (master_port.isConnected()) {
485 throw Exception(master_object->name(), csprintf(
486 "Master port: %s[%d] is already connected\n", master_port_name,
487 master_port_index));
488 }
489
490 if (slave_port.isConnected()) {
491 throw Exception(slave_object->name(), csprintf(
492 "Slave port: %s[%d] is already connected\n", slave_port_name,
493 slave_port_index));
494 }
495
496 DPRINTF(CxxConfig, "Binding port %s.%s[%d]"
497 " to %s:%s[%d]\n",
498 master_object->name(), master_port_name, master_port_index,
499 slave_object->name(), slave_port_name, slave_port_index);
500
501 master_port.bind(slave_port);
502 }
503
504 void
505 CxxConfigManager::bindMasterPort(SimObject *object,
506 const CxxConfigDirectoryEntry::PortDesc &port,
507 const std::vector<std::string> &peers)
508 {
509 unsigned int master_port_index = 0;
510
511 for (auto peer_i = peers.begin(); peer_i != peers.end();
512 ++peer_i)
513 {
514 const std::string &peer = *peer_i;
515 std::string slave_object_name;
516 std::string slave_port_name;
517 unsigned int slave_port_index;
518
519 parsePort(peer, slave_object_name, slave_port_name,
520 slave_port_index);
521
522 std::string slave_instance_name = rename(slave_object_name);
523
524 if (objectsByName.find(slave_instance_name) == objectsByName.end()) {
525 throw Exception(object->name(), csprintf(
526 "Can't find slave port object: %s", slave_instance_name));
527 }
528
529 SimObject *slave_object = objectsByName[slave_instance_name];
530
531 bindPort(object, port.name, master_port_index,
532 slave_object, slave_port_name, slave_port_index);
533
534 master_port_index++;
535 }
536 }
537
538 void
539 CxxConfigManager::bindObjectPorts(SimObject *object)
540 {
541 /* We may want to separate object->name() from the name in configuration
542 * later to allow (for example) repetition of fragments of configs */
543 const std::string &instance_name = object->name();
544
545 std::string object_name = unRename(instance_name);
546
547 std::string object_type;
548 const CxxConfigDirectoryEntry &entry =
549 findObjectType(object_name, object_type);
550
551 DPRINTF(CxxConfig, "Binding ports of object: %s (%s)\n",
552 instance_name, object_type);
553
554 for (auto i = entry.ports.begin(); i != entry.ports.end(); ++i) {
555 const CxxConfigDirectoryEntry::PortDesc *port = (*i).second;
556
557 DPRINTF(CxxConfig, "Binding port: %s.%s\n", instance_name,
558 port->name);
559
560 std::vector<std::string> peers;
561 configFile.getPortPeers(object_name, port->name, peers);
562
563 /* Only handle master ports as binding only needs to happen once
564 * for each observed pair of ports */
565 if (port->isMaster) {
566 if (!port->isVector && peers.size() > 1) {
567 throw Exception(instance_name, csprintf(
568 "Too many connections to non-vector port %s (%d)\n",
569 port->name, peers.size()));
570 }
571
572 bindMasterPort(object, *port, peers);
573 }
574 }
575 }
576
577 void
578 CxxConfigManager::parsePort(const std::string &inp,
579 std::string &path, std::string &port, unsigned int &index)
580 {
581 std::size_t dot_i = inp.rfind('.');
582 std::size_t open_square_i = inp.rfind('[');
583
584 if (dot_i == std::string::npos) {
585 DPRINTF(CxxConfig, "Bad port string: %s\n", inp);
586 path = "";
587 port = "";
588 index = 0;
589 } else {
590 path = std::string(inp, 0, dot_i);
591
592 if (open_square_i == std::string::npos) {
593 /* Singleton port */
594 port = std::string(inp, dot_i + 1, inp.length() - dot_i);
595 index = 0;
596 } else {
597 /* Vectored port elemnt */
598 port = std::string(inp, dot_i + 1, (open_square_i - 1) - dot_i);
599 index = std::atoi(inp.c_str() + open_square_i + 1);
600 }
601 }
602 }
603
604 void
605 CxxConfigManager::forEachObject(void (SimObject::*mem_func)())
606 {
607 for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++i)
608 ((*i)->*mem_func)();
609 }
610
611 void
612 CxxConfigManager::instantiate(bool build_all)
613 {
614 if (build_all) {
615 findAllObjects();
616 bindAllPorts();
617 }
618
619 DPRINTF(CxxConfig, "Initialising all objects\n");
620 forEachObject(&SimObject::init);
621
622 DPRINTF(CxxConfig, "Registering stats\n");
623 forEachObject(&SimObject::regStats);
624
625 DPRINTF(CxxConfig, "Registering probe points\n");
626 forEachObject(&SimObject::regProbePoints);
627
628 DPRINTF(CxxConfig, "Connecting probe listeners\n");
629 forEachObject(&SimObject::regProbeListeners);
630 }
631
632 void
633 CxxConfigManager::initState()
634 {
635 DPRINTF(CxxConfig, "Calling initState on all objects\n");
636 forEachObject(&SimObject::initState);
637 }
638
639 void
640 CxxConfigManager::startup()
641 {
642 DPRINTF(CxxConfig, "Starting up all objects\n");
643 forEachObject(&SimObject::startup);
644 }
645
646 unsigned int
647 CxxConfigManager::drain(DrainManager *drain_manager)
648 {
649 unsigned int ret = 0;
650
651 for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i)
652 ret += (*i)->drain(drain_manager);
653
654 return ret;
655 }
656
657 void
658 CxxConfigManager::drainResume()
659 {
660 forEachObject(&SimObject::drainResume);
661 }
662
663 void
664 CxxConfigManager::serialize(std::ostream &os)
665 {
666 for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i) {
667 // (*i)->nameOut(os); FIXME, change access spec. for nameOut
668 os << '[' << (*i)->name() << "]\n";
669 (*i)->serialize(os);
670 }
671 }
672
673 void
674 CxxConfigManager::loadState(Checkpoint *checkpoint)
675 {
676 for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i)
677 (*i)->loadState(checkpoint);
678 }
679
680 void
681 CxxConfigManager::deleteObjects()
682 {
683 for (auto i = objectsInOrder.rbegin(); i != objectsInOrder.rend(); ++i) {
684 DPRINTF(CxxConfig, "Freeing sim object: %s\n", (*i)->name());
685 delete *i;
686 }
687
688 for (auto i = objectParamsByName.rbegin();
689 i != objectParamsByName.rend(); ++i)
690 {
691 CxxConfigParams *params = (*i).second;
692
693 DPRINTF(CxxConfig, "Freeing sim object params: %s\n",
694 params->getName());
695 delete params;
696 }
697
698 objectsInOrder.clear();
699 objectsByName.clear();
700 }
701
702 void
703 CxxConfigManager::setParam(const std::string &object_name,
704 const std::string &param_name, const std::string &param_value)
705 {
706 CxxConfigParams *params = findObjectParams(object_name);
707
708 if (!params->setParam(param_name, param_value, flags)) {
709 throw Exception(object_name, csprintf("Bad parameter value:"
710 " .%s=X=\"%s\"", param_name, param_value));
711 } else {
712 std::string instance_name = rename(object_name);
713
714 DPRINTF(CxxConfig, "Setting parameter %s.%s=%s\n",
715 instance_name, param_name, param_value);
716 }
717 }
718
719 void
720 CxxConfigManager::setParamVector(const std::string &object_name,
721 const std::string &param_name,
722 const std::vector<std::string> &param_values)
723 {
724 CxxConfigParams *params = findObjectParams(object_name);
725
726 if (!params->setParamVector(param_name, param_values, flags)) {
727 throw Exception(object_name, csprintf("Bad vector parameter value:"
728 " .%s=X=\"%s\"", param_name, formatParamList(param_values)));
729 } else {
730 std::string instance_name = rename(object_name);
731
732 DPRINTF(CxxConfig, "Setting parameter %s.%s=\"%s\"\n",
733 instance_name, param_name, formatParamList(param_values));
734 }
735 }
736
737 void CxxConfigManager::addRenaming(const Renaming &renaming)
738 {
739 renamings.push_back(renaming);
740 }