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