cxx_manager.cc revision 10905:a6ca6831e775
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
49CxxConfigManager::CxxConfigManager(CxxConfigFileBase &configFile_) :
50    configFile(configFile_), flags(configFile_.getFlags()),
51    simObjectResolver(*this)
52{
53}
54
55const CxxConfigDirectoryEntry &
56CxxConfigManager::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
77std::string
78CxxConfigManager::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
92std::string
93CxxConfigManager::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
107static
108std::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
128SimObject *
129CxxConfigManager::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
262CxxConfigParams *
263CxxConfigManager::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
410void
411CxxConfigManager::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
428void
429CxxConfigManager::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
445void
446CxxConfigManager::bindAllPorts()
447{
448    for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++i)
449        bindObjectPorts(*i);
450}
451
452void
453CxxConfigManager::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
504void
505CxxConfigManager::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
538void
539CxxConfigManager::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
577void
578CxxConfigManager::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
604void
605CxxConfigManager::forEachObject(void (SimObject::*mem_func)())
606{
607    for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++i)
608        ((*i)->*mem_func)();
609}
610
611void
612CxxConfigManager::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
632void
633CxxConfigManager::initState()
634{
635    DPRINTF(CxxConfig, "Calling initState on all objects\n");
636    forEachObject(&SimObject::initState);
637}
638
639void
640CxxConfigManager::startup()
641{
642    DPRINTF(CxxConfig, "Starting up all objects\n");
643    forEachObject(&SimObject::startup);
644}
645
646unsigned int
647CxxConfigManager::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
657void
658CxxConfigManager::drainResume()
659{
660    forEachObject(&SimObject::drainResume);
661}
662
663void
664CxxConfigManager::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
673void
674CxxConfigManager::loadState(CheckpointIn &checkpoint)
675{
676    for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i)
677        (*i)->loadState(checkpoint);
678}
679
680void
681CxxConfigManager::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
702void
703CxxConfigManager::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
719void
720CxxConfigManager::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
737void CxxConfigManager::addRenaming(const Renaming &renaming)
738{
739    renamings.push_back(renaming);
740}
741