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