params.py revision 13543
110461SAndreas.Sandberg@ARM.com# Copyright (c) 2012-2014, 2017 ARM Limited
210461SAndreas.Sandberg@ARM.com# All rights reserved.
310461SAndreas.Sandberg@ARM.com#
410461SAndreas.Sandberg@ARM.com# The license below extends only to copyright in the software and shall
510461SAndreas.Sandberg@ARM.com# not be construed as granting a license to any other intellectual
610461SAndreas.Sandberg@ARM.com# property including but not limited to intellectual property relating
710461SAndreas.Sandberg@ARM.com# to a hardware implementation of the functionality of the software
810461SAndreas.Sandberg@ARM.com# licensed hereunder.  You may use the software subject to the license
910461SAndreas.Sandberg@ARM.com# terms below provided that you ensure that this notice is replicated
1010461SAndreas.Sandberg@ARM.com# unmodified and in its entirety in all distributions of the software,
1110461SAndreas.Sandberg@ARM.com# modified or unmodified, in source code or in binary form.
1210461SAndreas.Sandberg@ARM.com#
1310461SAndreas.Sandberg@ARM.com# Copyright (c) 2004-2006 The Regents of The University of Michigan
1410461SAndreas.Sandberg@ARM.com# Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
1510461SAndreas.Sandberg@ARM.com# All rights reserved.
1610461SAndreas.Sandberg@ARM.com#
1710461SAndreas.Sandberg@ARM.com# Redistribution and use in source and binary forms, with or without
1810461SAndreas.Sandberg@ARM.com# modification, are permitted provided that the following conditions are
1910461SAndreas.Sandberg@ARM.com# met: redistributions of source code must retain the above copyright
2010461SAndreas.Sandberg@ARM.com# notice, this list of conditions and the following disclaimer;
2110461SAndreas.Sandberg@ARM.com# redistributions in binary form must reproduce the above copyright
2210461SAndreas.Sandberg@ARM.com# notice, this list of conditions and the following disclaimer in the
2310461SAndreas.Sandberg@ARM.com# documentation and/or other materials provided with the distribution;
2410461SAndreas.Sandberg@ARM.com# neither the name of the copyright holders nor the names of its
2510461SAndreas.Sandberg@ARM.com# contributors may be used to endorse or promote products derived from
2610461SAndreas.Sandberg@ARM.com# this software without specific prior written permission.
2710461SAndreas.Sandberg@ARM.com#
2810461SAndreas.Sandberg@ARM.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2910461SAndreas.Sandberg@ARM.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3010461SAndreas.Sandberg@ARM.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3110461SAndreas.Sandberg@ARM.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3210461SAndreas.Sandberg@ARM.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3310461SAndreas.Sandberg@ARM.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3410461SAndreas.Sandberg@ARM.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3510461SAndreas.Sandberg@ARM.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3610461SAndreas.Sandberg@ARM.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3710461SAndreas.Sandberg@ARM.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3810461SAndreas.Sandberg@ARM.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3910461SAndreas.Sandberg@ARM.com#
4010461SAndreas.Sandberg@ARM.com# Authors: Steve Reinhardt
4110461SAndreas.Sandberg@ARM.com#          Nathan Binkert
4210461SAndreas.Sandberg@ARM.com#          Gabe Black
4310461SAndreas.Sandberg@ARM.com#          Andreas Hansson
4410461SAndreas.Sandberg@ARM.com
4510461SAndreas.Sandberg@ARM.com#####################################################################
4610461SAndreas.Sandberg@ARM.com#
4710461SAndreas.Sandberg@ARM.com# Parameter description classes
4810461SAndreas.Sandberg@ARM.com#
4910461SAndreas.Sandberg@ARM.com# The _params dictionary in each class maps parameter names to either
5010461SAndreas.Sandberg@ARM.com# a Param or a VectorParam object.  These objects contain the
5110461SAndreas.Sandberg@ARM.com# parameter description string, the parameter type, and the default
5210461SAndreas.Sandberg@ARM.com# value (if any).  The convert() method on these objects is used to
5310461SAndreas.Sandberg@ARM.com# force whatever value is assigned to the parameter to the appropriate
5410461SAndreas.Sandberg@ARM.com# type.
5510461SAndreas.Sandberg@ARM.com#
5610461SAndreas.Sandberg@ARM.com# Note that the default values are loaded into the class's attribute
5710461SAndreas.Sandberg@ARM.com# space when the parameter dictionary is initialized (in
5810461SAndreas.Sandberg@ARM.com# MetaSimObject._new_param()); after that point they aren't used.
5910461SAndreas.Sandberg@ARM.com#
6010461SAndreas.Sandberg@ARM.com#####################################################################
6110461SAndreas.Sandberg@ARM.com
6210461SAndreas.Sandberg@ARM.comfrom __future__ import print_function
6310461SAndreas.Sandberg@ARM.com
6410461SAndreas.Sandberg@ARM.comimport copy
6510461SAndreas.Sandberg@ARM.comimport datetime
6610461SAndreas.Sandberg@ARM.comimport re
6710461SAndreas.Sandberg@ARM.comimport sys
6810461SAndreas.Sandberg@ARM.comimport time
6910461SAndreas.Sandberg@ARM.comimport math
7010461SAndreas.Sandberg@ARM.com
7110461SAndreas.Sandberg@ARM.comimport proxy
7210461SAndreas.Sandberg@ARM.comimport ticks
7310461SAndreas.Sandberg@ARM.comfrom util import *
7410461SAndreas.Sandberg@ARM.com
7510461SAndreas.Sandberg@ARM.comdef isSimObject(*args, **kwargs):
7610461SAndreas.Sandberg@ARM.com    return SimObject.isSimObject(*args, **kwargs)
7710461SAndreas.Sandberg@ARM.com
7810461SAndreas.Sandberg@ARM.comdef isSimObjectSequence(*args, **kwargs):
7910461SAndreas.Sandberg@ARM.com    return SimObject.isSimObjectSequence(*args, **kwargs)
8010461SAndreas.Sandberg@ARM.com
8110461SAndreas.Sandberg@ARM.comdef isSimObjectClass(*args, **kwargs):
8210461SAndreas.Sandberg@ARM.com    return SimObject.isSimObjectClass(*args, **kwargs)
8310461SAndreas.Sandberg@ARM.com
8410461SAndreas.Sandberg@ARM.comallParams = {}
8510461SAndreas.Sandberg@ARM.com
8610461SAndreas.Sandberg@ARM.comclass MetaParamValue(type):
8710461SAndreas.Sandberg@ARM.com    def __new__(mcls, name, bases, dct):
8810461SAndreas.Sandberg@ARM.com        cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
8910461SAndreas.Sandberg@ARM.com        assert name not in allParams
9010461SAndreas.Sandberg@ARM.com        allParams[name] = cls
9110461SAndreas.Sandberg@ARM.com        return cls
9210461SAndreas.Sandberg@ARM.com
9310461SAndreas.Sandberg@ARM.com
9410461SAndreas.Sandberg@ARM.com# Dummy base class to identify types that are legitimate for SimObject
9510461SAndreas.Sandberg@ARM.com# parameters.
9610461SAndreas.Sandberg@ARM.comclass ParamValue(object):
9710461SAndreas.Sandberg@ARM.com    __metaclass__ = MetaParamValue
9810461SAndreas.Sandberg@ARM.com    cmd_line_settable = False
9910461SAndreas.Sandberg@ARM.com
10010461SAndreas.Sandberg@ARM.com    # Generate the code needed as a prerequisite for declaring a C++
10110461SAndreas.Sandberg@ARM.com    # object of this type.  Typically generates one or more #include
10210461SAndreas.Sandberg@ARM.com    # statements.  Used when declaring parameters of this type.
10310461SAndreas.Sandberg@ARM.com    @classmethod
10410461SAndreas.Sandberg@ARM.com    def cxx_predecls(cls, code):
10510461SAndreas.Sandberg@ARM.com        pass
10610461SAndreas.Sandberg@ARM.com
10710461SAndreas.Sandberg@ARM.com    @classmethod
10810461SAndreas.Sandberg@ARM.com    def pybind_predecls(cls, code):
10910461SAndreas.Sandberg@ARM.com        cls.cxx_predecls(code)
11010461SAndreas.Sandberg@ARM.com
11110461SAndreas.Sandberg@ARM.com    # default for printing to .ini file is regular string conversion.
11210461SAndreas.Sandberg@ARM.com    # will be overridden in some cases
11310461SAndreas.Sandberg@ARM.com    def ini_str(self):
11410461SAndreas.Sandberg@ARM.com        return str(self)
11510461SAndreas.Sandberg@ARM.com
11610461SAndreas.Sandberg@ARM.com    # default for printing to .json file is regular string conversion.
11710461SAndreas.Sandberg@ARM.com    # will be overridden in some cases, mostly to use native Python
11810461SAndreas.Sandberg@ARM.com    # types where there are similar JSON types
11910461SAndreas.Sandberg@ARM.com    def config_value(self):
12010461SAndreas.Sandberg@ARM.com        return str(self)
12110461SAndreas.Sandberg@ARM.com
12210461SAndreas.Sandberg@ARM.com    # Prerequisites for .ini parsing with cxx_ini_parse
12310461SAndreas.Sandberg@ARM.com    @classmethod
12410461SAndreas.Sandberg@ARM.com    def cxx_ini_predecls(cls, code):
12510461SAndreas.Sandberg@ARM.com        pass
12610461SAndreas.Sandberg@ARM.com
12710461SAndreas.Sandberg@ARM.com    # parse a .ini file entry for this param from string expression
12810461SAndreas.Sandberg@ARM.com    # src into lvalue dest (of the param's C++ type)
12910461SAndreas.Sandberg@ARM.com    @classmethod
13010461SAndreas.Sandberg@ARM.com    def cxx_ini_parse(cls, code, src, dest, ret):
13110461SAndreas.Sandberg@ARM.com        code('// Unhandled param type: %s' % cls.__name__)
13210461SAndreas.Sandberg@ARM.com        code('%s false;' % ret)
13310461SAndreas.Sandberg@ARM.com
13410461SAndreas.Sandberg@ARM.com    # allows us to blithely call unproxy() on things without checking
13510461SAndreas.Sandberg@ARM.com    # if they're really proxies or not
13610461SAndreas.Sandberg@ARM.com    def unproxy(self, base):
13710461SAndreas.Sandberg@ARM.com        return self
13810461SAndreas.Sandberg@ARM.com
13910461SAndreas.Sandberg@ARM.com    # Produce a human readable version of the stored value
14010461SAndreas.Sandberg@ARM.com    def pretty_print(self, value):
14110461SAndreas.Sandberg@ARM.com        return str(value)
14210461SAndreas.Sandberg@ARM.com
14310461SAndreas.Sandberg@ARM.com# Regular parameter description.
14410461SAndreas.Sandberg@ARM.comclass ParamDesc(object):
14510461SAndreas.Sandberg@ARM.com    def __init__(self, ptype_str, ptype, *args, **kwargs):
14610461SAndreas.Sandberg@ARM.com        self.ptype_str = ptype_str
14710461SAndreas.Sandberg@ARM.com        # remember ptype only if it is provided
14810461SAndreas.Sandberg@ARM.com        if ptype != None:
14910461SAndreas.Sandberg@ARM.com            self.ptype = ptype
15010461SAndreas.Sandberg@ARM.com
15110461SAndreas.Sandberg@ARM.com        if args:
15210461SAndreas.Sandberg@ARM.com            if len(args) == 1:
15310461SAndreas.Sandberg@ARM.com                self.desc = args[0]
15410461SAndreas.Sandberg@ARM.com            elif len(args) == 2:
15510461SAndreas.Sandberg@ARM.com                self.default = args[0]
15610461SAndreas.Sandberg@ARM.com                self.desc = args[1]
15710461SAndreas.Sandberg@ARM.com            else:
15810461SAndreas.Sandberg@ARM.com                raise TypeError, 'too many arguments'
15910461SAndreas.Sandberg@ARM.com
16010461SAndreas.Sandberg@ARM.com        if kwargs.has_key('desc'):
16110461SAndreas.Sandberg@ARM.com            assert(not hasattr(self, 'desc'))
16210461SAndreas.Sandberg@ARM.com            self.desc = kwargs['desc']
16310461SAndreas.Sandberg@ARM.com            del kwargs['desc']
16410461SAndreas.Sandberg@ARM.com
16510461SAndreas.Sandberg@ARM.com        if kwargs.has_key('default'):
16610461SAndreas.Sandberg@ARM.com            assert(not hasattr(self, 'default'))
16710461SAndreas.Sandberg@ARM.com            self.default = kwargs['default']
16810461SAndreas.Sandberg@ARM.com            del kwargs['default']
16910461SAndreas.Sandberg@ARM.com
17010461SAndreas.Sandberg@ARM.com        if kwargs:
17110461SAndreas.Sandberg@ARM.com            raise TypeError, 'extra unknown kwargs %s' % kwargs
17210461SAndreas.Sandberg@ARM.com
17310461SAndreas.Sandberg@ARM.com        if not hasattr(self, 'desc'):
17410461SAndreas.Sandberg@ARM.com            raise TypeError, 'desc attribute missing'
17510461SAndreas.Sandberg@ARM.com
17610461SAndreas.Sandberg@ARM.com    def __getattr__(self, attr):
17710461SAndreas.Sandberg@ARM.com        if attr == 'ptype':
17810461SAndreas.Sandberg@ARM.com            ptype = SimObject.allClasses[self.ptype_str]
17910461SAndreas.Sandberg@ARM.com            assert isSimObjectClass(ptype)
18010461SAndreas.Sandberg@ARM.com            self.ptype = ptype
18110461SAndreas.Sandberg@ARM.com            return ptype
18210461SAndreas.Sandberg@ARM.com
18310461SAndreas.Sandberg@ARM.com        raise AttributeError, "'%s' object has no attribute '%s'" % \
18410461SAndreas.Sandberg@ARM.com              (type(self).__name__, attr)
18510461SAndreas.Sandberg@ARM.com
18610461SAndreas.Sandberg@ARM.com    def example_str(self):
18710461SAndreas.Sandberg@ARM.com        if hasattr(self.ptype, "ex_str"):
18810461SAndreas.Sandberg@ARM.com            return self.ptype.ex_str
18910461SAndreas.Sandberg@ARM.com        else:
19010461SAndreas.Sandberg@ARM.com            return self.ptype_str
19110461SAndreas.Sandberg@ARM.com
19210461SAndreas.Sandberg@ARM.com    # Is the param available to be exposed on the command line
19310461SAndreas.Sandberg@ARM.com    def isCmdLineSettable(self):
19410461SAndreas.Sandberg@ARM.com        if hasattr(self.ptype, "cmd_line_settable"):
19510461SAndreas.Sandberg@ARM.com            return self.ptype.cmd_line_settable
19610461SAndreas.Sandberg@ARM.com        else:
19710461SAndreas.Sandberg@ARM.com            return False
19810461SAndreas.Sandberg@ARM.com
19910461SAndreas.Sandberg@ARM.com    def convert(self, value):
20010461SAndreas.Sandberg@ARM.com        if isinstance(value, proxy.BaseProxy):
20110461SAndreas.Sandberg@ARM.com            value.set_param_desc(self)
20210461SAndreas.Sandberg@ARM.com            return value
20310461SAndreas.Sandberg@ARM.com        if not hasattr(self, 'ptype') and isNullPointer(value):
20410461SAndreas.Sandberg@ARM.com            # deferred evaluation of SimObject; continue to defer if
20510461SAndreas.Sandberg@ARM.com            # we're just assigning a null pointer
20610461SAndreas.Sandberg@ARM.com            return value
20710461SAndreas.Sandberg@ARM.com        if isinstance(value, self.ptype):
20810461SAndreas.Sandberg@ARM.com            return value
20910461SAndreas.Sandberg@ARM.com        if isNullPointer(value) and isSimObjectClass(self.ptype):
21010461SAndreas.Sandberg@ARM.com            return value
21110461SAndreas.Sandberg@ARM.com        return self.ptype(value)
21210461SAndreas.Sandberg@ARM.com
21310461SAndreas.Sandberg@ARM.com    def pretty_print(self, value):
21410461SAndreas.Sandberg@ARM.com        if isinstance(value, proxy.BaseProxy):
21510461SAndreas.Sandberg@ARM.com           return str(value)
21610461SAndreas.Sandberg@ARM.com        if isNullPointer(value):
21710461SAndreas.Sandberg@ARM.com           return NULL
21810461SAndreas.Sandberg@ARM.com        return self.ptype(value).pretty_print(value)
21910461SAndreas.Sandberg@ARM.com
22010461SAndreas.Sandberg@ARM.com    def cxx_predecls(self, code):
22110461SAndreas.Sandberg@ARM.com        code('#include <cstddef>')
22210461SAndreas.Sandberg@ARM.com        self.ptype.cxx_predecls(code)
22310461SAndreas.Sandberg@ARM.com
22410461SAndreas.Sandberg@ARM.com    def pybind_predecls(self, code):
22510461SAndreas.Sandberg@ARM.com        self.ptype.pybind_predecls(code)
22610461SAndreas.Sandberg@ARM.com
22710461SAndreas.Sandberg@ARM.com    def cxx_decl(self, code):
22810461SAndreas.Sandberg@ARM.com        code('${{self.ptype.cxx_type}} ${{self.name}};')
22910461SAndreas.Sandberg@ARM.com
23010461SAndreas.Sandberg@ARM.com# Vector-valued parameter description.  Just like ParamDesc, except
23110461SAndreas.Sandberg@ARM.com# that the value is a vector (list) of the specified type instead of a
23210461SAndreas.Sandberg@ARM.com# single value.
23310461SAndreas.Sandberg@ARM.com
23410461SAndreas.Sandberg@ARM.comclass VectorParamValue(list):
23510461SAndreas.Sandberg@ARM.com    __metaclass__ = MetaParamValue
23610461SAndreas.Sandberg@ARM.com    def __setattr__(self, attr, value):
23710461SAndreas.Sandberg@ARM.com        raise AttributeError, \
23810461SAndreas.Sandberg@ARM.com              "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
23910461SAndreas.Sandberg@ARM.com
24010461SAndreas.Sandberg@ARM.com    def config_value(self):
24110461SAndreas.Sandberg@ARM.com        return [v.config_value() for v in self]
24210461SAndreas.Sandberg@ARM.com
24310461SAndreas.Sandberg@ARM.com    def ini_str(self):
24410461SAndreas.Sandberg@ARM.com        return ' '.join([v.ini_str() for v in self])
24510461SAndreas.Sandberg@ARM.com
24610461SAndreas.Sandberg@ARM.com    def getValue(self):
24710461SAndreas.Sandberg@ARM.com        return [ v.getValue() for v in self ]
24810461SAndreas.Sandberg@ARM.com
24910461SAndreas.Sandberg@ARM.com    def unproxy(self, base):
25010461SAndreas.Sandberg@ARM.com        if len(self) == 1 and isinstance(self[0], proxy.BaseProxy):
25110461SAndreas.Sandberg@ARM.com            # The value is a proxy (e.g. Parent.any, Parent.all or
25210461SAndreas.Sandberg@ARM.com            # Parent.x) therefore try resolve it
25310461SAndreas.Sandberg@ARM.com            return self[0].unproxy(base)
25410461SAndreas.Sandberg@ARM.com        else:
25510461SAndreas.Sandberg@ARM.com            return [v.unproxy(base) for v in self]
25610461SAndreas.Sandberg@ARM.com
25710461SAndreas.Sandberg@ARM.comclass SimObjectVector(VectorParamValue):
25810461SAndreas.Sandberg@ARM.com    # support clone operation
25910461SAndreas.Sandberg@ARM.com    def __call__(self, **kwargs):
26010461SAndreas.Sandberg@ARM.com        return SimObjectVector([v(**kwargs) for v in self])
26110461SAndreas.Sandberg@ARM.com
26210461SAndreas.Sandberg@ARM.com    def clear_parent(self, old_parent):
26310461SAndreas.Sandberg@ARM.com        for v in self:
26410461SAndreas.Sandberg@ARM.com            v.clear_parent(old_parent)
26510461SAndreas.Sandberg@ARM.com
26610461SAndreas.Sandberg@ARM.com    def set_parent(self, parent, name):
26710461SAndreas.Sandberg@ARM.com        if len(self) == 1:
26810461SAndreas.Sandberg@ARM.com            self[0].set_parent(parent, name)
26910461SAndreas.Sandberg@ARM.com        else:
27010461SAndreas.Sandberg@ARM.com            width = int(math.ceil(math.log(len(self))/math.log(10)))
27110461SAndreas.Sandberg@ARM.com            for i,v in enumerate(self):
27210461SAndreas.Sandberg@ARM.com                v.set_parent(parent, "%s%0*d" % (name, width, i))
27310461SAndreas.Sandberg@ARM.com
27410461SAndreas.Sandberg@ARM.com    def has_parent(self):
27510461SAndreas.Sandberg@ARM.com        return any([e.has_parent() for e in self if not isNullPointer(e)])
27610461SAndreas.Sandberg@ARM.com
27710461SAndreas.Sandberg@ARM.com    # return 'cpu0 cpu1' etc. for print_ini()
27810461SAndreas.Sandberg@ARM.com    def get_name(self):
27910461SAndreas.Sandberg@ARM.com        return ' '.join([v._name for v in self])
28010461SAndreas.Sandberg@ARM.com
28110461SAndreas.Sandberg@ARM.com    # By iterating through the constituent members of the vector here
28210461SAndreas.Sandberg@ARM.com    # we can nicely handle iterating over all a SimObject's children
28310461SAndreas.Sandberg@ARM.com    # without having to provide lots of special functions on
28410461SAndreas.Sandberg@ARM.com    # SimObjectVector directly.
28510461SAndreas.Sandberg@ARM.com    def descendants(self):
28610461SAndreas.Sandberg@ARM.com        for v in self:
28710461SAndreas.Sandberg@ARM.com            for obj in v.descendants():
28810461SAndreas.Sandberg@ARM.com                yield obj
28910461SAndreas.Sandberg@ARM.com
29010461SAndreas.Sandberg@ARM.com    def get_config_as_dict(self):
29110461SAndreas.Sandberg@ARM.com        a = []
29210461SAndreas.Sandberg@ARM.com        for v in self:
29310461SAndreas.Sandberg@ARM.com            a.append(v.get_config_as_dict())
29410461SAndreas.Sandberg@ARM.com        return a
29510461SAndreas.Sandberg@ARM.com
29610461SAndreas.Sandberg@ARM.com    # If we are replacing an item in the vector, make sure to set the
29710461SAndreas.Sandberg@ARM.com    # parent reference of the new SimObject to be the same as the parent
29810461SAndreas.Sandberg@ARM.com    # of the SimObject being replaced. Useful to have if we created
29910461SAndreas.Sandberg@ARM.com    # a SimObjectVector of temporary objects that will be modified later in
30010461SAndreas.Sandberg@ARM.com    # configuration scripts.
30110461SAndreas.Sandberg@ARM.com    def __setitem__(self, key, value):
30210461SAndreas.Sandberg@ARM.com        val = self[key]
30310461SAndreas.Sandberg@ARM.com        if value.has_parent():
30410461SAndreas.Sandberg@ARM.com            warn("SimObject %s already has a parent" % value.get_name() +\
30510461SAndreas.Sandberg@ARM.com                 " that is being overwritten by a SimObjectVector")
30610461SAndreas.Sandberg@ARM.com        value.set_parent(val.get_parent(), val._name)
30710461SAndreas.Sandberg@ARM.com        super(SimObjectVector, self).__setitem__(key, value)
30810461SAndreas.Sandberg@ARM.com
30910461SAndreas.Sandberg@ARM.com    # Enumerate the params of each member of the SimObject vector. Creates
31010461SAndreas.Sandberg@ARM.com    # strings that will allow indexing into the vector by the python code and
31110461SAndreas.Sandberg@ARM.com    # allow it to be specified on the command line.
31210461SAndreas.Sandberg@ARM.com    def enumerateParams(self, flags_dict = {},
31310461SAndreas.Sandberg@ARM.com                        cmd_line_str = "",
31410461SAndreas.Sandberg@ARM.com                        access_str = ""):
31510461SAndreas.Sandberg@ARM.com        if hasattr(self, "_paramEnumed"):
31610461SAndreas.Sandberg@ARM.com            print("Cycle detected enumerating params at %s?!" % (cmd_line_str))
31710461SAndreas.Sandberg@ARM.com        else:
31810461SAndreas.Sandberg@ARM.com            x = 0
31910461SAndreas.Sandberg@ARM.com            for vals in self:
32010461SAndreas.Sandberg@ARM.com                # Each entry in the SimObjectVector should be an
32110461SAndreas.Sandberg@ARM.com                # instance of a SimObject
32210461SAndreas.Sandberg@ARM.com                flags_dict = vals.enumerateParams(flags_dict,
32310461SAndreas.Sandberg@ARM.com                                                  cmd_line_str + "%d." % x,
32410461SAndreas.Sandberg@ARM.com                                                  access_str + "[%d]." % x)
32510461SAndreas.Sandberg@ARM.com                x = x + 1
32610461SAndreas.Sandberg@ARM.com
32710461SAndreas.Sandberg@ARM.com        return flags_dict
32810461SAndreas.Sandberg@ARM.com
32910461SAndreas.Sandberg@ARM.comclass VectorParamDesc(ParamDesc):
33010461SAndreas.Sandberg@ARM.com    # Convert assigned value to appropriate type.  If the RHS is not a
33110461SAndreas.Sandberg@ARM.com    # list or tuple, it generates a single-element list.
33210461SAndreas.Sandberg@ARM.com    def convert(self, value):
33310461SAndreas.Sandberg@ARM.com        if isinstance(value, (list, tuple)):
33410461SAndreas.Sandberg@ARM.com            # list: coerce each element into new list
33510461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
33610461SAndreas.Sandberg@ARM.com        elif isinstance(value, str):
33710461SAndreas.Sandberg@ARM.com            # If input is a csv string
33810461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.convert(self, v) \
33910461SAndreas.Sandberg@ARM.com                         for v in value.strip('[').strip(']').split(',') ]
34010461SAndreas.Sandberg@ARM.com        else:
34110461SAndreas.Sandberg@ARM.com            # singleton: coerce to a single-element list
34210461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.convert(self, value) ]
34310461SAndreas.Sandberg@ARM.com
34410461SAndreas.Sandberg@ARM.com        if isSimObjectSequence(tmp_list):
34510461SAndreas.Sandberg@ARM.com            return SimObjectVector(tmp_list)
34610461SAndreas.Sandberg@ARM.com        else:
34710461SAndreas.Sandberg@ARM.com            return VectorParamValue(tmp_list)
34810461SAndreas.Sandberg@ARM.com
34910461SAndreas.Sandberg@ARM.com    # Produce a human readable example string that describes
35010461SAndreas.Sandberg@ARM.com    # how to set this vector parameter in the absence of a default
35110461SAndreas.Sandberg@ARM.com    # value.
35210461SAndreas.Sandberg@ARM.com    def example_str(self):
35310461SAndreas.Sandberg@ARM.com        s = super(VectorParamDesc, self).example_str()
35410461SAndreas.Sandberg@ARM.com        help_str = "[" + s + "," + s + ", ...]"
35510461SAndreas.Sandberg@ARM.com        return help_str
35610461SAndreas.Sandberg@ARM.com
35710461SAndreas.Sandberg@ARM.com    # Produce a human readable representation of the value of this vector param.
35810461SAndreas.Sandberg@ARM.com    def pretty_print(self, value):
35910461SAndreas.Sandberg@ARM.com        if isinstance(value, (list, tuple)):
36010461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ]
36110461SAndreas.Sandberg@ARM.com        elif isinstance(value, str):
36210461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ]
36310461SAndreas.Sandberg@ARM.com        else:
36410461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.pretty_print(self, value) ]
36510461SAndreas.Sandberg@ARM.com
36610461SAndreas.Sandberg@ARM.com        return tmp_list
36710461SAndreas.Sandberg@ARM.com
36810461SAndreas.Sandberg@ARM.com    # This is a helper function for the new config system
36910461SAndreas.Sandberg@ARM.com    def __call__(self, value):
37010461SAndreas.Sandberg@ARM.com        if isinstance(value, (list, tuple)):
37110461SAndreas.Sandberg@ARM.com            # list: coerce each element into new list
37210461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
37310461SAndreas.Sandberg@ARM.com        elif isinstance(value, str):
37410461SAndreas.Sandberg@ARM.com            # If input is a csv string
37510461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.convert(self, v) \
37610461SAndreas.Sandberg@ARM.com                         for v in value.strip('[').strip(']').split(',') ]
37710461SAndreas.Sandberg@ARM.com        else:
37810461SAndreas.Sandberg@ARM.com            # singleton: coerce to a single-element list
37910461SAndreas.Sandberg@ARM.com            tmp_list = [ ParamDesc.convert(self, value) ]
38010461SAndreas.Sandberg@ARM.com
38110461SAndreas.Sandberg@ARM.com        return VectorParamValue(tmp_list)
38210461SAndreas.Sandberg@ARM.com
38310461SAndreas.Sandberg@ARM.com    def cxx_predecls(self, code):
38410461SAndreas.Sandberg@ARM.com        code('#include <vector>')
38510461SAndreas.Sandberg@ARM.com        self.ptype.cxx_predecls(code)
38610461SAndreas.Sandberg@ARM.com
38710461SAndreas.Sandberg@ARM.com    def pybind_predecls(self, code):
38810461SAndreas.Sandberg@ARM.com        code('#include <vector>')
38910461SAndreas.Sandberg@ARM.com        self.ptype.pybind_predecls(code)
39010461SAndreas.Sandberg@ARM.com
39110461SAndreas.Sandberg@ARM.com    def cxx_decl(self, code):
39210461SAndreas.Sandberg@ARM.com        code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
39310461SAndreas.Sandberg@ARM.com
39410461SAndreas.Sandberg@ARM.comclass ParamFactory(object):
39510461SAndreas.Sandberg@ARM.com    def __init__(self, param_desc_class, ptype_str = None):
39610461SAndreas.Sandberg@ARM.com        self.param_desc_class = param_desc_class
39710461SAndreas.Sandberg@ARM.com        self.ptype_str = ptype_str
39810461SAndreas.Sandberg@ARM.com
39910461SAndreas.Sandberg@ARM.com    def __getattr__(self, attr):
40010461SAndreas.Sandberg@ARM.com        if self.ptype_str:
40110461SAndreas.Sandberg@ARM.com            attr = self.ptype_str + '.' + attr
40210461SAndreas.Sandberg@ARM.com        return ParamFactory(self.param_desc_class, attr)
40310461SAndreas.Sandberg@ARM.com
40410461SAndreas.Sandberg@ARM.com    # E.g., Param.Int(5, "number of widgets")
40510461SAndreas.Sandberg@ARM.com    def __call__(self, *args, **kwargs):
40610461SAndreas.Sandberg@ARM.com        ptype = None
40710461SAndreas.Sandberg@ARM.com        try:
40810461SAndreas.Sandberg@ARM.com            ptype = allParams[self.ptype_str]
40910461SAndreas.Sandberg@ARM.com        except KeyError:
41010461SAndreas.Sandberg@ARM.com            # if name isn't defined yet, assume it's a SimObject, and
41110461SAndreas.Sandberg@ARM.com            # try to resolve it later
41210461SAndreas.Sandberg@ARM.com            pass
41310461SAndreas.Sandberg@ARM.com        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
41410461SAndreas.Sandberg@ARM.com
41510461SAndreas.Sandberg@ARM.comParam = ParamFactory(ParamDesc)
41610461SAndreas.Sandberg@ARM.comVectorParam = ParamFactory(VectorParamDesc)
41710461SAndreas.Sandberg@ARM.com
41810461SAndreas.Sandberg@ARM.com#####################################################################
41910461SAndreas.Sandberg@ARM.com#
42010461SAndreas.Sandberg@ARM.com# Parameter Types
42110461SAndreas.Sandberg@ARM.com#
42210461SAndreas.Sandberg@ARM.com# Though native Python types could be used to specify parameter types
42310461SAndreas.Sandberg@ARM.com# (the 'ptype' field of the Param and VectorParam classes), it's more
42410461SAndreas.Sandberg@ARM.com# flexible to define our own set of types.  This gives us more control
42510461SAndreas.Sandberg@ARM.com# over how Python expressions are converted to values (via the
42610461SAndreas.Sandberg@ARM.com# __init__() constructor) and how these values are printed out (via
42710461SAndreas.Sandberg@ARM.com# the __str__() conversion method).
42810461SAndreas.Sandberg@ARM.com#
42910461SAndreas.Sandberg@ARM.com#####################################################################
43010461SAndreas.Sandberg@ARM.com
43110461SAndreas.Sandberg@ARM.com# String-valued parameter.  Just mixin the ParamValue class with the
43210461SAndreas.Sandberg@ARM.com# built-in str class.
43310461SAndreas.Sandberg@ARM.comclass String(ParamValue,str):
43410461SAndreas.Sandberg@ARM.com    cxx_type = 'std::string'
43510461SAndreas.Sandberg@ARM.com    cmd_line_settable = True
43610461SAndreas.Sandberg@ARM.com
43710461SAndreas.Sandberg@ARM.com    @classmethod
43810461SAndreas.Sandberg@ARM.com    def cxx_predecls(self, code):
43910461SAndreas.Sandberg@ARM.com        code('#include <string>')
44010461SAndreas.Sandberg@ARM.com
44110461SAndreas.Sandberg@ARM.com    def __call__(self, value):
44210461SAndreas.Sandberg@ARM.com        self = value
44310461SAndreas.Sandberg@ARM.com        return value
44410461SAndreas.Sandberg@ARM.com
44510461SAndreas.Sandberg@ARM.com    @classmethod
44610461SAndreas.Sandberg@ARM.com    def cxx_ini_parse(self, code, src, dest, ret):
44710461SAndreas.Sandberg@ARM.com        code('%s = %s;' % (dest, src))
44810461SAndreas.Sandberg@ARM.com        code('%s true;' % ret)
44910461SAndreas.Sandberg@ARM.com
45010461SAndreas.Sandberg@ARM.com    def getValue(self):
45110461SAndreas.Sandberg@ARM.com        return self
45210461SAndreas.Sandberg@ARM.com
45310461SAndreas.Sandberg@ARM.com# superclass for "numeric" parameter values, to emulate math
45410461SAndreas.Sandberg@ARM.com# operations in a type-safe way.  e.g., a Latency times an int returns
45510461SAndreas.Sandberg@ARM.com# a new Latency object.
45610461SAndreas.Sandberg@ARM.comclass NumericParamValue(ParamValue):
45710461SAndreas.Sandberg@ARM.com    def __str__(self):
45810461SAndreas.Sandberg@ARM.com        return str(self.value)
45910461SAndreas.Sandberg@ARM.com
46010461SAndreas.Sandberg@ARM.com    def __float__(self):
46110461SAndreas.Sandberg@ARM.com        return float(self.value)
46210461SAndreas.Sandberg@ARM.com
46310461SAndreas.Sandberg@ARM.com    def __long__(self):
46410461SAndreas.Sandberg@ARM.com        return long(self.value)
46510461SAndreas.Sandberg@ARM.com
46610461SAndreas.Sandberg@ARM.com    def __int__(self):
46710461SAndreas.Sandberg@ARM.com        return int(self.value)
46810461SAndreas.Sandberg@ARM.com
46910461SAndreas.Sandberg@ARM.com    # hook for bounds checking
47010461SAndreas.Sandberg@ARM.com    def _check(self):
47110461SAndreas.Sandberg@ARM.com        return
47210461SAndreas.Sandberg@ARM.com
47310461SAndreas.Sandberg@ARM.com    def __mul__(self, other):
47410461SAndreas.Sandberg@ARM.com        newobj = self.__class__(self)
47510461SAndreas.Sandberg@ARM.com        newobj.value *= other
47610461SAndreas.Sandberg@ARM.com        newobj._check()
47710461SAndreas.Sandberg@ARM.com        return newobj
47810461SAndreas.Sandberg@ARM.com
47910461SAndreas.Sandberg@ARM.com    __rmul__ = __mul__
48010461SAndreas.Sandberg@ARM.com
48110461SAndreas.Sandberg@ARM.com    def __div__(self, other):
48210461SAndreas.Sandberg@ARM.com        newobj = self.__class__(self)
48310461SAndreas.Sandberg@ARM.com        newobj.value /= other
48410461SAndreas.Sandberg@ARM.com        newobj._check()
48510461SAndreas.Sandberg@ARM.com        return newobj
48610461SAndreas.Sandberg@ARM.com
48710461SAndreas.Sandberg@ARM.com    def __sub__(self, other):
48810461SAndreas.Sandberg@ARM.com        newobj = self.__class__(self)
48910461SAndreas.Sandberg@ARM.com        newobj.value -= other
49010461SAndreas.Sandberg@ARM.com        newobj._check()
49110461SAndreas.Sandberg@ARM.com        return newobj
49210461SAndreas.Sandberg@ARM.com
49310461SAndreas.Sandberg@ARM.com    def config_value(self):
49410461SAndreas.Sandberg@ARM.com        return self.value
49510461SAndreas.Sandberg@ARM.com
49610461SAndreas.Sandberg@ARM.com    @classmethod
49710461SAndreas.Sandberg@ARM.com    def cxx_ini_predecls(cls, code):
49810461SAndreas.Sandberg@ARM.com        # Assume that base/str.hh will be included anyway
49910461SAndreas.Sandberg@ARM.com        # code('#include "base/str.hh"')
50010461SAndreas.Sandberg@ARM.com        pass
50110461SAndreas.Sandberg@ARM.com
50210461SAndreas.Sandberg@ARM.com    # The default for parsing PODs from an .ini entry is to extract from an
50310461SAndreas.Sandberg@ARM.com    # istringstream and let overloading choose the right type according to
50410461SAndreas.Sandberg@ARM.com    # the dest type.
50510461SAndreas.Sandberg@ARM.com    @classmethod
50610461SAndreas.Sandberg@ARM.com    def cxx_ini_parse(self, code, src, dest, ret):
50710461SAndreas.Sandberg@ARM.com        code('%s to_number(%s, %s);' % (ret, src, dest))
50810461SAndreas.Sandberg@ARM.com
50910461SAndreas.Sandberg@ARM.com# Metaclass for bounds-checked integer parameters.  See CheckedInt.
51010461SAndreas.Sandberg@ARM.comclass CheckedIntType(MetaParamValue):
51110461SAndreas.Sandberg@ARM.com    def __init__(cls, name, bases, dict):
51210461SAndreas.Sandberg@ARM.com        super(CheckedIntType, cls).__init__(name, bases, dict)
51310461SAndreas.Sandberg@ARM.com
51410461SAndreas.Sandberg@ARM.com        # CheckedInt is an abstract base class, so we actually don't
51510461SAndreas.Sandberg@ARM.com        # want to do any processing on it... the rest of this code is
51610461SAndreas.Sandberg@ARM.com        # just for classes that derive from CheckedInt.
51710461SAndreas.Sandberg@ARM.com        if name == 'CheckedInt':
51810461SAndreas.Sandberg@ARM.com            return
51910461SAndreas.Sandberg@ARM.com
52010461SAndreas.Sandberg@ARM.com        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
52110461SAndreas.Sandberg@ARM.com            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
52210461SAndreas.Sandberg@ARM.com                panic("CheckedInt subclass %s must define either\n" \
52310461SAndreas.Sandberg@ARM.com                      "    'min' and 'max' or 'size' and 'unsigned'\n",
52410461SAndreas.Sandberg@ARM.com                      name);
52510461SAndreas.Sandberg@ARM.com            if cls.unsigned:
52610461SAndreas.Sandberg@ARM.com                cls.min = 0
52710461SAndreas.Sandberg@ARM.com                cls.max = 2 ** cls.size - 1
52810461SAndreas.Sandberg@ARM.com            else:
52910461SAndreas.Sandberg@ARM.com                cls.min = -(2 ** (cls.size - 1))
53010461SAndreas.Sandberg@ARM.com                cls.max = (2 ** (cls.size - 1)) - 1
53110461SAndreas.Sandberg@ARM.com
53210461SAndreas.Sandberg@ARM.com# Abstract superclass for bounds-checked integer parameters.  This
53310461SAndreas.Sandberg@ARM.com# class is subclassed to generate parameter classes with specific
53410461SAndreas.Sandberg@ARM.com# bounds.  Initialization of the min and max bounds is done in the
53510461SAndreas.Sandberg@ARM.com# metaclass CheckedIntType.__init__.
53610461SAndreas.Sandberg@ARM.comclass CheckedInt(NumericParamValue):
53710461SAndreas.Sandberg@ARM.com    __metaclass__ = CheckedIntType
53810461SAndreas.Sandberg@ARM.com    cmd_line_settable = True
53910461SAndreas.Sandberg@ARM.com
54010461SAndreas.Sandberg@ARM.com    def _check(self):
54110461SAndreas.Sandberg@ARM.com        if not self.min <= self.value <= self.max:
54210461SAndreas.Sandberg@ARM.com            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
54310461SAndreas.Sandberg@ARM.com                  (self.min, self.value, self.max)
54410461SAndreas.Sandberg@ARM.com
54510461SAndreas.Sandberg@ARM.com    def __init__(self, value):
54610461SAndreas.Sandberg@ARM.com        if isinstance(value, str):
54710461SAndreas.Sandberg@ARM.com            self.value = convert.toInteger(value)
54810461SAndreas.Sandberg@ARM.com        elif isinstance(value, (int, long, float, NumericParamValue)):
54910461SAndreas.Sandberg@ARM.com            self.value = long(value)
55010461SAndreas.Sandberg@ARM.com        else:
55110461SAndreas.Sandberg@ARM.com            raise TypeError, "Can't convert object of type %s to CheckedInt" \
55210461SAndreas.Sandberg@ARM.com                  % type(value).__name__
55310461SAndreas.Sandberg@ARM.com        self._check()
55410461SAndreas.Sandberg@ARM.com
55510461SAndreas.Sandberg@ARM.com    def __call__(self, value):
55610461SAndreas.Sandberg@ARM.com        self.__init__(value)
55710461SAndreas.Sandberg@ARM.com        return value
55810461SAndreas.Sandberg@ARM.com
55910461SAndreas.Sandberg@ARM.com    @classmethod
560    def cxx_predecls(cls, code):
561        # most derived types require this, so we just do it here once
562        code('#include "base/types.hh"')
563
564    def getValue(self):
565        return long(self.value)
566
567class Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
568class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
569
570class Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
571class UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
572class Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
573class UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
574class Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
575class UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
576class Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
577class UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
578
579class Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
580class Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
581class TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
582class UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
583
584class Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
585
586class Cycles(CheckedInt):
587    cxx_type = 'Cycles'
588    size = 64
589    unsigned = True
590
591    def getValue(self):
592        from _m5.core import Cycles
593        return Cycles(self.value)
594
595    @classmethod
596    def cxx_ini_predecls(cls, code):
597        # Assume that base/str.hh will be included anyway
598        # code('#include "base/str.hh"')
599        pass
600
601    @classmethod
602    def cxx_ini_parse(cls, code, src, dest, ret):
603        code('uint64_t _temp;')
604        code('bool _ret = to_number(%s, _temp);' % src)
605        code('if (_ret)')
606        code('    %s = Cycles(_temp);' % dest)
607        code('%s _ret;' % ret)
608
609class Float(ParamValue, float):
610    cxx_type = 'double'
611    cmd_line_settable = True
612
613    def __init__(self, value):
614        if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
615            self.value = float(value)
616        else:
617            raise TypeError, "Can't convert object of type %s to Float" \
618                  % type(value).__name__
619
620    def __call__(self, value):
621        self.__init__(value)
622        return value
623
624    def getValue(self):
625        return float(self.value)
626
627    def config_value(self):
628        return self
629
630    @classmethod
631    def cxx_ini_predecls(cls, code):
632        code('#include <sstream>')
633
634    @classmethod
635    def cxx_ini_parse(self, code, src, dest, ret):
636        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
637
638class MemorySize(CheckedInt):
639    cxx_type = 'uint64_t'
640    ex_str = '512MB'
641    size = 64
642    unsigned = True
643    def __init__(self, value):
644        if isinstance(value, MemorySize):
645            self.value = value.value
646        else:
647            self.value = convert.toMemorySize(value)
648        self._check()
649
650class MemorySize32(CheckedInt):
651    cxx_type = 'uint32_t'
652    ex_str = '512MB'
653    size = 32
654    unsigned = True
655    def __init__(self, value):
656        if isinstance(value, MemorySize):
657            self.value = value.value
658        else:
659            self.value = convert.toMemorySize(value)
660        self._check()
661
662class Addr(CheckedInt):
663    cxx_type = 'Addr'
664    size = 64
665    unsigned = True
666    def __init__(self, value):
667        if isinstance(value, Addr):
668            self.value = value.value
669        else:
670            try:
671                # Often addresses are referred to with sizes. Ex: A device
672                # base address is at "512MB".  Use toMemorySize() to convert
673                # these into addresses. If the address is not specified with a
674                # "size", an exception will occur and numeric translation will
675                # proceed below.
676                self.value = convert.toMemorySize(value)
677            except (TypeError, ValueError):
678                # Convert number to string and use long() to do automatic
679                # base conversion (requires base=0 for auto-conversion)
680                self.value = long(str(value), base=0)
681
682        self._check()
683    def __add__(self, other):
684        if isinstance(other, Addr):
685            return self.value + other.value
686        else:
687            return self.value + other
688    def pretty_print(self, value):
689        try:
690            val = convert.toMemorySize(value)
691        except TypeError:
692            val = long(value)
693        return "0x%x" % long(val)
694
695class AddrRange(ParamValue):
696    cxx_type = 'AddrRange'
697
698    def __init__(self, *args, **kwargs):
699        # Disable interleaving and hashing by default
700        self.intlvHighBit = 0
701        self.xorHighBit = 0
702        self.intlvBits = 0
703        self.intlvMatch = 0
704
705        def handle_kwargs(self, kwargs):
706            # An address range needs to have an upper limit, specified
707            # either explicitly with an end, or as an offset using the
708            # size keyword.
709            if 'end' in kwargs:
710                self.end = Addr(kwargs.pop('end'))
711            elif 'size' in kwargs:
712                self.end = self.start + Addr(kwargs.pop('size')) - 1
713            else:
714                raise TypeError, "Either end or size must be specified"
715
716            # Now on to the optional bit
717            if 'intlvHighBit' in kwargs:
718                self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
719            if 'xorHighBit' in kwargs:
720                self.xorHighBit = int(kwargs.pop('xorHighBit'))
721            if 'intlvBits' in kwargs:
722                self.intlvBits = int(kwargs.pop('intlvBits'))
723            if 'intlvMatch' in kwargs:
724                self.intlvMatch = int(kwargs.pop('intlvMatch'))
725
726        if len(args) == 0:
727            self.start = Addr(kwargs.pop('start'))
728            handle_kwargs(self, kwargs)
729
730        elif len(args) == 1:
731            if kwargs:
732                self.start = Addr(args[0])
733                handle_kwargs(self, kwargs)
734            elif isinstance(args[0], (list, tuple)):
735                self.start = Addr(args[0][0])
736                self.end = Addr(args[0][1])
737            else:
738                self.start = Addr(0)
739                self.end = Addr(args[0]) - 1
740
741        elif len(args) == 2:
742            self.start = Addr(args[0])
743            self.end = Addr(args[1])
744        else:
745            raise TypeError, "Too many arguments specified"
746
747        if kwargs:
748            raise TypeError, "Too many keywords: %s" % kwargs.keys()
749
750    def __str__(self):
751        return '%s:%s:%s:%s:%s:%s' \
752            % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\
753               self.intlvBits, self.intlvMatch)
754
755    def size(self):
756        # Divide the size by the size of the interleaving slice
757        return (long(self.end) - long(self.start) + 1) >> self.intlvBits
758
759    @classmethod
760    def cxx_predecls(cls, code):
761        Addr.cxx_predecls(code)
762        code('#include "base/addr_range.hh"')
763
764    @classmethod
765    def pybind_predecls(cls, code):
766        Addr.pybind_predecls(code)
767        code('#include "base/addr_range.hh"')
768
769    @classmethod
770    def cxx_ini_predecls(cls, code):
771        code('#include <sstream>')
772
773    @classmethod
774    def cxx_ini_parse(cls, code, src, dest, ret):
775        code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;')
776        code('uint64_t _intlvBits = 0, _intlvMatch = 0;')
777        code('char _sep;')
778        code('std::istringstream _stream(${src});')
779        code('_stream >> _start;')
780        code('_stream.get(_sep);')
781        code('_stream >> _end;')
782        code('if (!_stream.fail() && !_stream.eof()) {')
783        code('    _stream.get(_sep);')
784        code('    _stream >> _intlvHighBit;')
785        code('    _stream.get(_sep);')
786        code('    _stream >> _xorHighBit;')
787        code('    _stream.get(_sep);')
788        code('    _stream >> _intlvBits;')
789        code('    _stream.get(_sep);')
790        code('    _stream >> _intlvMatch;')
791        code('}')
792        code('bool _ret = !_stream.fail() &&'
793            '_stream.eof() && _sep == \':\';')
794        code('if (_ret)')
795        code('   ${dest} = AddrRange(_start, _end, _intlvHighBit, \
796                _xorHighBit, _intlvBits, _intlvMatch);')
797        code('${ret} _ret;')
798
799    def getValue(self):
800        # Go from the Python class to the wrapped C++ class
801        from _m5.range import AddrRange
802
803        return AddrRange(long(self.start), long(self.end),
804                         int(self.intlvHighBit), int(self.xorHighBit),
805                         int(self.intlvBits), int(self.intlvMatch))
806
807# Boolean parameter type.  Python doesn't let you subclass bool, since
808# it doesn't want to let you create multiple instances of True and
809# False.  Thus this is a little more complicated than String.
810class Bool(ParamValue):
811    cxx_type = 'bool'
812    cmd_line_settable = True
813
814    def __init__(self, value):
815        try:
816            self.value = convert.toBool(value)
817        except TypeError:
818            self.value = bool(value)
819
820    def __call__(self, value):
821        self.__init__(value)
822        return value
823
824    def getValue(self):
825        return bool(self.value)
826
827    def __str__(self):
828        return str(self.value)
829
830    # implement truth value testing for Bool parameters so that these params
831    # evaluate correctly during the python configuration phase
832    def __nonzero__(self):
833        return bool(self.value)
834
835    def ini_str(self):
836        if self.value:
837            return 'true'
838        return 'false'
839
840    def config_value(self):
841        return self.value
842
843    @classmethod
844    def cxx_ini_predecls(cls, code):
845        # Assume that base/str.hh will be included anyway
846        # code('#include "base/str.hh"')
847        pass
848
849    @classmethod
850    def cxx_ini_parse(cls, code, src, dest, ret):
851        code('%s to_bool(%s, %s);' % (ret, src, dest))
852
853def IncEthernetAddr(addr, val = 1):
854    bytes = map(lambda x: int(x, 16), addr.split(':'))
855    bytes[5] += val
856    for i in (5, 4, 3, 2, 1):
857        val,rem = divmod(bytes[i], 256)
858        bytes[i] = rem
859        if val == 0:
860            break
861        bytes[i - 1] += val
862    assert(bytes[0] <= 255)
863    return ':'.join(map(lambda x: '%02x' % x, bytes))
864
865_NextEthernetAddr = "00:90:00:00:00:01"
866def NextEthernetAddr():
867    global _NextEthernetAddr
868
869    value = _NextEthernetAddr
870    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
871    return value
872
873class EthernetAddr(ParamValue):
874    cxx_type = 'Net::EthAddr'
875    ex_str = "00:90:00:00:00:01"
876    cmd_line_settable = True
877
878    @classmethod
879    def cxx_predecls(cls, code):
880        code('#include "base/inet.hh"')
881
882    def __init__(self, value):
883        if value == NextEthernetAddr:
884            self.value = value
885            return
886
887        if not isinstance(value, str):
888            raise TypeError, "expected an ethernet address and didn't get one"
889
890        bytes = value.split(':')
891        if len(bytes) != 6:
892            raise TypeError, 'invalid ethernet address %s' % value
893
894        for byte in bytes:
895            if not 0 <= int(byte, base=16) <= 0xff:
896                raise TypeError, 'invalid ethernet address %s' % value
897
898        self.value = value
899
900    def __call__(self, value):
901        self.__init__(value)
902        return value
903
904    def unproxy(self, base):
905        if self.value == NextEthernetAddr:
906            return EthernetAddr(self.value())
907        return self
908
909    def getValue(self):
910        from _m5.net import EthAddr
911        return EthAddr(self.value)
912
913    def __str__(self):
914        return self.value
915
916    def ini_str(self):
917        return self.value
918
919    @classmethod
920    def cxx_ini_parse(self, code, src, dest, ret):
921        code('%s = Net::EthAddr(%s);' % (dest, src))
922        code('%s true;' % ret)
923
924# When initializing an IpAddress, pass in an existing IpAddress, a string of
925# the form "a.b.c.d", or an integer representing an IP.
926class IpAddress(ParamValue):
927    cxx_type = 'Net::IpAddress'
928    ex_str = "127.0.0.1"
929    cmd_line_settable = True
930
931    @classmethod
932    def cxx_predecls(cls, code):
933        code('#include "base/inet.hh"')
934
935    def __init__(self, value):
936        if isinstance(value, IpAddress):
937            self.ip = value.ip
938        else:
939            try:
940                self.ip = convert.toIpAddress(value)
941            except TypeError:
942                self.ip = long(value)
943        self.verifyIp()
944
945    def __call__(self, value):
946        self.__init__(value)
947        return value
948
949    def __str__(self):
950        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
951        return '%d.%d.%d.%d' % tuple(tup)
952
953    def __eq__(self, other):
954        if isinstance(other, IpAddress):
955            return self.ip == other.ip
956        elif isinstance(other, str):
957            try:
958                return self.ip == convert.toIpAddress(other)
959            except:
960                return False
961        else:
962            return self.ip == other
963
964    def __ne__(self, other):
965        return not (self == other)
966
967    def verifyIp(self):
968        if self.ip < 0 or self.ip >= (1 << 32):
969            raise TypeError, "invalid ip address %#08x" % self.ip
970
971    def getValue(self):
972        from _m5.net import IpAddress
973        return IpAddress(self.ip)
974
975# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
976# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
977# positional or keyword arguments.
978class IpNetmask(IpAddress):
979    cxx_type = 'Net::IpNetmask'
980    ex_str = "127.0.0.0/24"
981    cmd_line_settable = True
982
983    @classmethod
984    def cxx_predecls(cls, code):
985        code('#include "base/inet.hh"')
986
987    def __init__(self, *args, **kwargs):
988        def handle_kwarg(self, kwargs, key, elseVal = None):
989            if key in kwargs:
990                setattr(self, key, kwargs.pop(key))
991            elif elseVal:
992                setattr(self, key, elseVal)
993            else:
994                raise TypeError, "No value set for %s" % key
995
996        if len(args) == 0:
997            handle_kwarg(self, kwargs, 'ip')
998            handle_kwarg(self, kwargs, 'netmask')
999
1000        elif len(args) == 1:
1001            if kwargs:
1002                if not 'ip' in kwargs and not 'netmask' in kwargs:
1003                    raise TypeError, "Invalid arguments"
1004                handle_kwarg(self, kwargs, 'ip', args[0])
1005                handle_kwarg(self, kwargs, 'netmask', args[0])
1006            elif isinstance(args[0], IpNetmask):
1007                self.ip = args[0].ip
1008                self.netmask = args[0].netmask
1009            else:
1010                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
1011
1012        elif len(args) == 2:
1013            self.ip = args[0]
1014            self.netmask = args[1]
1015        else:
1016            raise TypeError, "Too many arguments specified"
1017
1018        if kwargs:
1019            raise TypeError, "Too many keywords: %s" % kwargs.keys()
1020
1021        self.verify()
1022
1023    def __call__(self, value):
1024        self.__init__(value)
1025        return value
1026
1027    def __str__(self):
1028        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
1029
1030    def __eq__(self, other):
1031        if isinstance(other, IpNetmask):
1032            return self.ip == other.ip and self.netmask == other.netmask
1033        elif isinstance(other, str):
1034            try:
1035                return (self.ip, self.netmask) == convert.toIpNetmask(other)
1036            except:
1037                return False
1038        else:
1039            return False
1040
1041    def verify(self):
1042        self.verifyIp()
1043        if self.netmask < 0 or self.netmask > 32:
1044            raise TypeError, "invalid netmask %d" % netmask
1045
1046    def getValue(self):
1047        from _m5.net import IpNetmask
1048        return IpNetmask(self.ip, self.netmask)
1049
1050# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1051# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1052class IpWithPort(IpAddress):
1053    cxx_type = 'Net::IpWithPort'
1054    ex_str = "127.0.0.1:80"
1055    cmd_line_settable = True
1056
1057    @classmethod
1058    def cxx_predecls(cls, code):
1059        code('#include "base/inet.hh"')
1060
1061    def __init__(self, *args, **kwargs):
1062        def handle_kwarg(self, kwargs, key, elseVal = None):
1063            if key in kwargs:
1064                setattr(self, key, kwargs.pop(key))
1065            elif elseVal:
1066                setattr(self, key, elseVal)
1067            else:
1068                raise TypeError, "No value set for %s" % key
1069
1070        if len(args) == 0:
1071            handle_kwarg(self, kwargs, 'ip')
1072            handle_kwarg(self, kwargs, 'port')
1073
1074        elif len(args) == 1:
1075            if kwargs:
1076                if not 'ip' in kwargs and not 'port' in kwargs:
1077                    raise TypeError, "Invalid arguments"
1078                handle_kwarg(self, kwargs, 'ip', args[0])
1079                handle_kwarg(self, kwargs, 'port', args[0])
1080            elif isinstance(args[0], IpWithPort):
1081                self.ip = args[0].ip
1082                self.port = args[0].port
1083            else:
1084                (self.ip, self.port) = convert.toIpWithPort(args[0])
1085
1086        elif len(args) == 2:
1087            self.ip = args[0]
1088            self.port = args[1]
1089        else:
1090            raise TypeError, "Too many arguments specified"
1091
1092        if kwargs:
1093            raise TypeError, "Too many keywords: %s" % kwargs.keys()
1094
1095        self.verify()
1096
1097    def __call__(self, value):
1098        self.__init__(value)
1099        return value
1100
1101    def __str__(self):
1102        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1103
1104    def __eq__(self, other):
1105        if isinstance(other, IpWithPort):
1106            return self.ip == other.ip and self.port == other.port
1107        elif isinstance(other, str):
1108            try:
1109                return (self.ip, self.port) == convert.toIpWithPort(other)
1110            except:
1111                return False
1112        else:
1113            return False
1114
1115    def verify(self):
1116        self.verifyIp()
1117        if self.port < 0 or self.port > 0xffff:
1118            raise TypeError, "invalid port %d" % self.port
1119
1120    def getValue(self):
1121        from _m5.net import IpWithPort
1122        return IpWithPort(self.ip, self.port)
1123
1124time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1125                 "%a %b %d %H:%M:%S %Y",
1126                 "%Y/%m/%d %H:%M:%S",
1127                 "%Y/%m/%d %H:%M",
1128                 "%Y/%m/%d",
1129                 "%m/%d/%Y %H:%M:%S",
1130                 "%m/%d/%Y %H:%M",
1131                 "%m/%d/%Y",
1132                 "%m/%d/%y %H:%M:%S",
1133                 "%m/%d/%y %H:%M",
1134                 "%m/%d/%y"]
1135
1136
1137def parse_time(value):
1138    from time import gmtime, strptime, struct_time, time
1139    from datetime import datetime, date
1140
1141    if isinstance(value, struct_time):
1142        return value
1143
1144    if isinstance(value, (int, long)):
1145        return gmtime(value)
1146
1147    if isinstance(value, (datetime, date)):
1148        return value.timetuple()
1149
1150    if isinstance(value, str):
1151        if value in ('Now', 'Today'):
1152            return time.gmtime(time.time())
1153
1154        for format in time_formats:
1155            try:
1156                return strptime(value, format)
1157            except ValueError:
1158                pass
1159
1160    raise ValueError, "Could not parse '%s' as a time" % value
1161
1162class Time(ParamValue):
1163    cxx_type = 'tm'
1164
1165    @classmethod
1166    def cxx_predecls(cls, code):
1167        code('#include <time.h>')
1168
1169    def __init__(self, value):
1170        self.value = parse_time(value)
1171
1172    def __call__(self, value):
1173        self.__init__(value)
1174        return value
1175
1176    def getValue(self):
1177        from _m5.core import tm
1178        import calendar
1179
1180        return tm.gmtime(calendar.timegm(self.value))
1181
1182    def __str__(self):
1183        return time.asctime(self.value)
1184
1185    def ini_str(self):
1186        return str(self)
1187
1188    def get_config_as_dict(self):
1189        assert false
1190        return str(self)
1191
1192    @classmethod
1193    def cxx_ini_predecls(cls, code):
1194        code('#include <time.h>')
1195
1196    @classmethod
1197    def cxx_ini_parse(cls, code, src, dest, ret):
1198        code('char *_parse_ret = strptime((${src}).c_str(),')
1199        code('    "%a %b %d %H:%M:%S %Y", &(${dest}));')
1200        code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1201
1202# Enumerated types are a little more complex.  The user specifies the
1203# type as Enum(foo) where foo is either a list or dictionary of
1204# alternatives (typically strings, but not necessarily so).  (In the
1205# long run, the integer value of the parameter will be the list index
1206# or the corresponding dictionary value.  For now, since we only check
1207# that the alternative is valid and then spit it into a .ini file,
1208# there's not much point in using the dictionary.)
1209
1210# What Enum() must do is generate a new type encapsulating the
1211# provided list/dictionary so that specific values of the parameter
1212# can be instances of that type.  We define two hidden internal
1213# classes (_ListEnum and _DictEnum) to serve as base classes, then
1214# derive the new type from the appropriate base class on the fly.
1215
1216allEnums = {}
1217# Metaclass for Enum types
1218class MetaEnum(MetaParamValue):
1219    def __new__(mcls, name, bases, dict):
1220        assert name not in allEnums
1221
1222        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1223        allEnums[name] = cls
1224        return cls
1225
1226    def __init__(cls, name, bases, init_dict):
1227        if init_dict.has_key('map'):
1228            if not isinstance(cls.map, dict):
1229                raise TypeError, "Enum-derived class attribute 'map' " \
1230                      "must be of type dict"
1231            # build list of value strings from map
1232            cls.vals = cls.map.keys()
1233            cls.vals.sort()
1234        elif init_dict.has_key('vals'):
1235            if not isinstance(cls.vals, list):
1236                raise TypeError, "Enum-derived class attribute 'vals' " \
1237                      "must be of type list"
1238            # build string->value map from vals sequence
1239            cls.map = {}
1240            for idx,val in enumerate(cls.vals):
1241                cls.map[val] = idx
1242        else:
1243            raise TypeError, "Enum-derived class must define "\
1244                  "attribute 'map' or 'vals'"
1245
1246        cls.cxx_type = 'Enums::%s' % name
1247
1248        super(MetaEnum, cls).__init__(name, bases, init_dict)
1249
1250    # Generate C++ class declaration for this enum type.
1251    # Note that we wrap the enum in a class/struct to act as a namespace,
1252    # so that the enum strings can be brief w/o worrying about collisions.
1253    def cxx_decl(cls, code):
1254        wrapper_name = cls.wrapper_name
1255        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1256        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1257        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1258
1259        code('''\
1260#ifndef $idem_macro
1261#define $idem_macro
1262
1263$wrapper $wrapper_name {
1264    enum $name {
1265''')
1266        code.indent(2)
1267        for val in cls.vals:
1268            code('$val = ${{cls.map[val]}},')
1269        code('Num_$name = ${{len(cls.vals)}}')
1270        code.dedent(2)
1271        code('    };')
1272
1273        if cls.wrapper_is_struct:
1274            code('    static const char *${name}Strings[Num_${name}];')
1275            code('};')
1276        else:
1277            code('extern const char *${name}Strings[Num_${name}];')
1278            code('}')
1279
1280        code()
1281        code('#endif // $idem_macro')
1282
1283    def cxx_def(cls, code):
1284        wrapper_name = cls.wrapper_name
1285        file_name = cls.__name__
1286        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1287
1288        code('#include "enums/$file_name.hh"')
1289        if cls.wrapper_is_struct:
1290            code('const char *${wrapper_name}::${name}Strings'
1291                '[Num_${name}] =')
1292        else:
1293            code('namespace Enums {')
1294            code.indent(1)
1295            code(' const char *${name}Strings[Num_${name}] =')
1296
1297        code('{')
1298        code.indent(1)
1299        for val in cls.vals:
1300            code('"$val",')
1301        code.dedent(1)
1302        code('};')
1303
1304        if not cls.wrapper_is_struct:
1305            code('} // namespace $wrapper_name')
1306            code.dedent(1)
1307
1308    def pybind_def(cls, code):
1309        name = cls.__name__
1310        wrapper_name = cls.wrapper_name
1311        enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
1312
1313        code('''#include "pybind11/pybind11.h"
1314#include "pybind11/stl.h"
1315
1316#include <sim/init.hh>
1317
1318namespace py = pybind11;
1319
1320static void
1321module_init(py::module &m_internal)
1322{
1323    py::module m = m_internal.def_submodule("enum_${name}");
1324
1325    py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")
1326''')
1327
1328        code.indent()
1329        code.indent()
1330        for val in cls.vals:
1331            code('.value("${val}", ${wrapper_name}::${val})')
1332        code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1333        code('.export_values()')
1334        code(';')
1335        code.dedent()
1336
1337        code('}')
1338        code.dedent()
1339        code()
1340        code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1341
1342
1343# Base class for enum types.
1344class Enum(ParamValue):
1345    __metaclass__ = MetaEnum
1346    vals = []
1347    cmd_line_settable = True
1348
1349    # The name of the wrapping namespace or struct
1350    wrapper_name = 'Enums'
1351
1352    # If true, the enum is wrapped in a struct rather than a namespace
1353    wrapper_is_struct = False
1354
1355    # If not None, use this as the enum name rather than this class name
1356    enum_name = None
1357
1358    def __init__(self, value):
1359        if value not in self.map:
1360            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1361                  % (value, self.vals)
1362        self.value = value
1363
1364    def __call__(self, value):
1365        self.__init__(value)
1366        return value
1367
1368    @classmethod
1369    def cxx_predecls(cls, code):
1370        code('#include "enums/$0.hh"', cls.__name__)
1371
1372    @classmethod
1373    def cxx_ini_parse(cls, code, src, dest, ret):
1374        code('if (false) {')
1375        for elem_name in cls.map.iterkeys():
1376            code('} else if (%s == "%s") {' % (src, elem_name))
1377            code.indent()
1378            code('%s = Enums::%s;' % (dest, elem_name))
1379            code('%s true;' % ret)
1380            code.dedent()
1381        code('} else {')
1382        code('    %s false;' % ret)
1383        code('}')
1384
1385    def getValue(self):
1386        import m5.internal.params
1387        e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
1388        return e(self.map[self.value])
1389
1390    def __str__(self):
1391        return self.value
1392
1393# how big does a rounding error need to be before we warn about it?
1394frequency_tolerance = 0.001  # 0.1%
1395
1396class TickParamValue(NumericParamValue):
1397    cxx_type = 'Tick'
1398    ex_str = "1MHz"
1399    cmd_line_settable = True
1400
1401    @classmethod
1402    def cxx_predecls(cls, code):
1403        code('#include "base/types.hh"')
1404
1405    def __call__(self, value):
1406        self.__init__(value)
1407        return value
1408
1409    def getValue(self):
1410        return long(self.value)
1411
1412    @classmethod
1413    def cxx_ini_predecls(cls, code):
1414        code('#include <sstream>')
1415
1416    # Ticks are expressed in seconds in JSON files and in plain
1417    # Ticks in .ini files.  Switch based on a config flag
1418    @classmethod
1419    def cxx_ini_parse(self, code, src, dest, ret):
1420        code('${ret} to_number(${src}, ${dest});')
1421
1422class Latency(TickParamValue):
1423    ex_str = "100ns"
1424
1425    def __init__(self, value):
1426        if isinstance(value, (Latency, Clock)):
1427            self.ticks = value.ticks
1428            self.value = value.value
1429        elif isinstance(value, Frequency):
1430            self.ticks = value.ticks
1431            self.value = 1.0 / value.value
1432        elif value.endswith('t'):
1433            self.ticks = True
1434            self.value = int(value[:-1])
1435        else:
1436            self.ticks = False
1437            self.value = convert.toLatency(value)
1438
1439    def __call__(self, value):
1440        self.__init__(value)
1441        return value
1442
1443    def __getattr__(self, attr):
1444        if attr in ('latency', 'period'):
1445            return self
1446        if attr == 'frequency':
1447            return Frequency(self)
1448        raise AttributeError, "Latency object has no attribute '%s'" % attr
1449
1450    def getValue(self):
1451        if self.ticks or self.value == 0:
1452            value = self.value
1453        else:
1454            value = ticks.fromSeconds(self.value)
1455        return long(value)
1456
1457    def config_value(self):
1458        return self.getValue()
1459
1460    # convert latency to ticks
1461    def ini_str(self):
1462        return '%d' % self.getValue()
1463
1464class Frequency(TickParamValue):
1465    ex_str = "1GHz"
1466
1467    def __init__(self, value):
1468        if isinstance(value, (Latency, Clock)):
1469            if value.value == 0:
1470                self.value = 0
1471            else:
1472                self.value = 1.0 / value.value
1473            self.ticks = value.ticks
1474        elif isinstance(value, Frequency):
1475            self.value = value.value
1476            self.ticks = value.ticks
1477        else:
1478            self.ticks = False
1479            self.value = convert.toFrequency(value)
1480
1481    def __call__(self, value):
1482        self.__init__(value)
1483        return value
1484
1485    def __getattr__(self, attr):
1486        if attr == 'frequency':
1487            return self
1488        if attr in ('latency', 'period'):
1489            return Latency(self)
1490        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1491
1492    # convert latency to ticks
1493    def getValue(self):
1494        if self.ticks or self.value == 0:
1495            value = self.value
1496        else:
1497            value = ticks.fromSeconds(1.0 / self.value)
1498        return long(value)
1499
1500    def config_value(self):
1501        return self.getValue()
1502
1503    def ini_str(self):
1504        return '%d' % self.getValue()
1505
1506# A generic Frequency and/or Latency value. Value is stored as a
1507# latency, just like Latency and Frequency.
1508class Clock(TickParamValue):
1509    def __init__(self, value):
1510        if isinstance(value, (Latency, Clock)):
1511            self.ticks = value.ticks
1512            self.value = value.value
1513        elif isinstance(value, Frequency):
1514            self.ticks = value.ticks
1515            self.value = 1.0 / value.value
1516        elif value.endswith('t'):
1517            self.ticks = True
1518            self.value = int(value[:-1])
1519        else:
1520            self.ticks = False
1521            self.value = convert.anyToLatency(value)
1522
1523    def __call__(self, value):
1524        self.__init__(value)
1525        return value
1526
1527    def __str__(self):
1528        return "%s" % Latency(self)
1529
1530    def __getattr__(self, attr):
1531        if attr == 'frequency':
1532            return Frequency(self)
1533        if attr in ('latency', 'period'):
1534            return Latency(self)
1535        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1536
1537    def getValue(self):
1538        return self.period.getValue()
1539
1540    def config_value(self):
1541        return self.period.config_value()
1542
1543    def ini_str(self):
1544        return self.period.ini_str()
1545
1546class Voltage(Float):
1547    ex_str = "1V"
1548
1549    def __new__(cls, value):
1550        value = convert.toVoltage(value)
1551        return super(cls, Voltage).__new__(cls, value)
1552
1553    def __init__(self, value):
1554        value = convert.toVoltage(value)
1555        super(Voltage, self).__init__(value)
1556
1557class Current(Float):
1558    ex_str = "1mA"
1559
1560    def __new__(cls, value):
1561        value = convert.toCurrent(value)
1562        return super(cls, Current).__new__(cls, value)
1563
1564    def __init__(self, value):
1565        value = convert.toCurrent(value)
1566        super(Current, self).__init__(value)
1567
1568class Energy(Float):
1569    ex_str = "1pJ"
1570
1571    def __new__(cls, value):
1572        value = convert.toEnergy(value)
1573        return super(cls, Energy).__new__(cls, value)
1574
1575    def __init__(self, value):
1576        value = convert.toEnergy(value)
1577        super(Energy, self).__init__(value)
1578
1579class NetworkBandwidth(float,ParamValue):
1580    cxx_type = 'float'
1581    ex_str = "1Gbps"
1582    cmd_line_settable = True
1583
1584    def __new__(cls, value):
1585        # convert to bits per second
1586        val = convert.toNetworkBandwidth(value)
1587        return super(cls, NetworkBandwidth).__new__(cls, val)
1588
1589    def __str__(self):
1590        return str(self.val)
1591
1592    def __call__(self, value):
1593        val = convert.toNetworkBandwidth(value)
1594        self.__init__(val)
1595        return value
1596
1597    def getValue(self):
1598        # convert to seconds per byte
1599        value = 8.0 / float(self)
1600        # convert to ticks per byte
1601        value = ticks.fromSeconds(value)
1602        return float(value)
1603
1604    def ini_str(self):
1605        return '%f' % self.getValue()
1606
1607    def config_value(self):
1608        return '%f' % self.getValue()
1609
1610    @classmethod
1611    def cxx_ini_predecls(cls, code):
1612        code('#include <sstream>')
1613
1614    @classmethod
1615    def cxx_ini_parse(self, code, src, dest, ret):
1616        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1617
1618class MemoryBandwidth(float,ParamValue):
1619    cxx_type = 'float'
1620    ex_str = "1GB/s"
1621    cmd_line_settable = True
1622
1623    def __new__(cls, value):
1624        # convert to bytes per second
1625        val = convert.toMemoryBandwidth(value)
1626        return super(cls, MemoryBandwidth).__new__(cls, val)
1627
1628    def __call__(self, value):
1629        val = convert.toMemoryBandwidth(value)
1630        self.__init__(val)
1631        return value
1632
1633    def getValue(self):
1634        # convert to seconds per byte
1635        value = float(self)
1636        if value:
1637            value = 1.0 / float(self)
1638        # convert to ticks per byte
1639        value = ticks.fromSeconds(value)
1640        return float(value)
1641
1642    def ini_str(self):
1643        return '%f' % self.getValue()
1644
1645    def config_value(self):
1646        return '%f' % self.getValue()
1647
1648    @classmethod
1649    def cxx_ini_predecls(cls, code):
1650        code('#include <sstream>')
1651
1652    @classmethod
1653    def cxx_ini_parse(self, code, src, dest, ret):
1654        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1655
1656#
1657# "Constants"... handy aliases for various values.
1658#
1659
1660# Special class for NULL pointers.  Note the special check in
1661# make_param_value() above that lets these be assigned where a
1662# SimObject is required.
1663# only one copy of a particular node
1664class NullSimObject(object):
1665    __metaclass__ = Singleton
1666    _name = 'Null'
1667
1668    def __call__(cls):
1669        return cls
1670
1671    def _instantiate(self, parent = None, path = ''):
1672        pass
1673
1674    def ini_str(self):
1675        return 'Null'
1676
1677    def unproxy(self, base):
1678        return self
1679
1680    def set_path(self, parent, name):
1681        pass
1682
1683    def set_parent(self, parent, name):
1684        pass
1685
1686    def clear_parent(self, old_parent):
1687        pass
1688
1689    def descendants(self):
1690        return
1691        yield None
1692
1693    def get_config_as_dict(self):
1694        return {}
1695
1696    def __str__(self):
1697        return self._name
1698
1699    def config_value(self):
1700        return None
1701
1702    def getValue(self):
1703        return None
1704
1705# The only instance you'll ever need...
1706NULL = NullSimObject()
1707
1708def isNullPointer(value):
1709    return isinstance(value, NullSimObject)
1710
1711# Some memory range specifications use this as a default upper bound.
1712MaxAddr = Addr.max
1713MaxTick = Tick.max
1714AllMemory = AddrRange(0, MaxAddr)
1715
1716
1717#####################################################################
1718#
1719# Port objects
1720#
1721# Ports are used to interconnect objects in the memory system.
1722#
1723#####################################################################
1724
1725# Port reference: encapsulates a reference to a particular port on a
1726# particular SimObject.
1727class PortRef(object):
1728    def __init__(self, simobj, name, role):
1729        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1730        self.simobj = simobj
1731        self.name = name
1732        self.role = role
1733        self.peer = None   # not associated with another port yet
1734        self.ccConnected = False # C++ port connection done?
1735        self.index = -1  # always -1 for non-vector ports
1736
1737    def __str__(self):
1738        return '%s.%s' % (self.simobj, self.name)
1739
1740    def __len__(self):
1741        # Return the number of connected ports, i.e. 0 is we have no
1742        # peer and 1 if we do.
1743        return int(self.peer != None)
1744
1745    # for config.ini, print peer's name (not ours)
1746    def ini_str(self):
1747        return str(self.peer)
1748
1749    # for config.json
1750    def get_config_as_dict(self):
1751        return {'role' : self.role, 'peer' : str(self.peer)}
1752
1753    def __getattr__(self, attr):
1754        if attr == 'peerObj':
1755            # shorthand for proxies
1756            return self.peer.simobj
1757        raise AttributeError, "'%s' object has no attribute '%s'" % \
1758              (self.__class__.__name__, attr)
1759
1760    # Full connection is symmetric (both ways).  Called via
1761    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1762    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1763    # e.g., "obj1.portA[3] = obj2.portB".
1764    def connect(self, other):
1765        if isinstance(other, VectorPortRef):
1766            # reference to plain VectorPort is implicit append
1767            other = other._get_next()
1768        if self.peer and not proxy.isproxy(self.peer):
1769            fatal("Port %s is already connected to %s, cannot connect %s\n",
1770                  self, self.peer, other);
1771        self.peer = other
1772        if proxy.isproxy(other):
1773            other.set_param_desc(PortParamDesc())
1774        elif isinstance(other, PortRef):
1775            if other.peer is not self:
1776                other.connect(self)
1777        else:
1778            raise TypeError, \
1779                  "assigning non-port reference '%s' to port '%s'" \
1780                  % (other, self)
1781
1782    # Allow a master/slave port pair to be spliced between
1783    # a port and its connected peer. Useful operation for connecting
1784    # instrumentation structures into a system when it is necessary
1785    # to connect the instrumentation after the full system has been
1786    # constructed.
1787    def splice(self, new_master_peer, new_slave_peer):
1788        if not self.peer or proxy.isproxy(self.peer):
1789            fatal("Port %s not connected, cannot splice in new peers\n", self)
1790
1791        if not isinstance(new_master_peer, PortRef) or \
1792           not isinstance(new_slave_peer, PortRef):
1793            raise TypeError, \
1794                  "Splicing non-port references '%s','%s' to port '%s'" % \
1795                  (new_master_peer, new_slave_peer, self)
1796
1797        old_peer = self.peer
1798        if self.role == 'SLAVE':
1799            self.peer = new_master_peer
1800            old_peer.peer = new_slave_peer
1801            new_master_peer.connect(self)
1802            new_slave_peer.connect(old_peer)
1803        elif self.role == 'MASTER':
1804            self.peer = new_slave_peer
1805            old_peer.peer = new_master_peer
1806            new_slave_peer.connect(self)
1807            new_master_peer.connect(old_peer)
1808        else:
1809            panic("Port %s has unknown role, "+\
1810                  "cannot splice in new peers\n", self)
1811
1812    def clone(self, simobj, memo):
1813        if memo.has_key(self):
1814            return memo[self]
1815        newRef = copy.copy(self)
1816        memo[self] = newRef
1817        newRef.simobj = simobj
1818        assert(isSimObject(newRef.simobj))
1819        if self.peer and not proxy.isproxy(self.peer):
1820            peerObj = self.peer.simobj(_memo=memo)
1821            newRef.peer = self.peer.clone(peerObj, memo)
1822            assert(not isinstance(newRef.peer, VectorPortRef))
1823        return newRef
1824
1825    def unproxy(self, simobj):
1826        assert(simobj is self.simobj)
1827        if proxy.isproxy(self.peer):
1828            try:
1829                realPeer = self.peer.unproxy(self.simobj)
1830            except:
1831                print("Error in unproxying port '%s' of %s" %
1832                      (self.name, self.simobj.path()))
1833                raise
1834            self.connect(realPeer)
1835
1836    # Call C++ to create corresponding port connection between C++ objects
1837    def ccConnect(self):
1838        from _m5.pyobject import connectPorts
1839
1840        if self.role == 'SLAVE':
1841            # do nothing and let the master take care of it
1842            return
1843
1844        if self.ccConnected: # already done this
1845            return
1846        peer = self.peer
1847        if not self.peer: # nothing to connect to
1848            return
1849
1850        # check that we connect a master to a slave
1851        if self.role == peer.role:
1852            raise TypeError, \
1853                "cannot connect '%s' and '%s' due to identical role '%s'" \
1854                % (peer, self, self.role)
1855
1856        try:
1857            # self is always the master and peer the slave
1858            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1859                         peer.simobj.getCCObject(), peer.name, peer.index)
1860        except:
1861            print("Error connecting port %s.%s to %s.%s" %
1862                  (self.simobj.path(), self.name,
1863                   peer.simobj.path(), peer.name))
1864            raise
1865        self.ccConnected = True
1866        peer.ccConnected = True
1867
1868# A reference to an individual element of a VectorPort... much like a
1869# PortRef, but has an index.
1870class VectorPortElementRef(PortRef):
1871    def __init__(self, simobj, name, role, index):
1872        PortRef.__init__(self, simobj, name, role)
1873        self.index = index
1874
1875    def __str__(self):
1876        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1877
1878# A reference to a complete vector-valued port (not just a single element).
1879# Can be indexed to retrieve individual VectorPortElementRef instances.
1880class VectorPortRef(object):
1881    def __init__(self, simobj, name, role):
1882        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1883        self.simobj = simobj
1884        self.name = name
1885        self.role = role
1886        self.elements = []
1887
1888    def __str__(self):
1889        return '%s.%s[:]' % (self.simobj, self.name)
1890
1891    def __len__(self):
1892        # Return the number of connected peers, corresponding the the
1893        # length of the elements.
1894        return len(self.elements)
1895
1896    # for config.ini, print peer's name (not ours)
1897    def ini_str(self):
1898        return ' '.join([el.ini_str() for el in self.elements])
1899
1900    # for config.json
1901    def get_config_as_dict(self):
1902        return {'role' : self.role,
1903                'peer' : [el.ini_str() for el in self.elements]}
1904
1905    def __getitem__(self, key):
1906        if not isinstance(key, int):
1907            raise TypeError, "VectorPort index must be integer"
1908        if key >= len(self.elements):
1909            # need to extend list
1910            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1911                   for i in range(len(self.elements), key+1)]
1912            self.elements.extend(ext)
1913        return self.elements[key]
1914
1915    def _get_next(self):
1916        return self[len(self.elements)]
1917
1918    def __setitem__(self, key, value):
1919        if not isinstance(key, int):
1920            raise TypeError, "VectorPort index must be integer"
1921        self[key].connect(value)
1922
1923    def connect(self, other):
1924        if isinstance(other, (list, tuple)):
1925            # Assign list of port refs to vector port.
1926            # For now, append them... not sure if that's the right semantics
1927            # or if it should replace the current vector.
1928            for ref in other:
1929                self._get_next().connect(ref)
1930        else:
1931            # scalar assignment to plain VectorPort is implicit append
1932            self._get_next().connect(other)
1933
1934    def clone(self, simobj, memo):
1935        if memo.has_key(self):
1936            return memo[self]
1937        newRef = copy.copy(self)
1938        memo[self] = newRef
1939        newRef.simobj = simobj
1940        assert(isSimObject(newRef.simobj))
1941        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1942        return newRef
1943
1944    def unproxy(self, simobj):
1945        [el.unproxy(simobj) for el in self.elements]
1946
1947    def ccConnect(self):
1948        [el.ccConnect() for el in self.elements]
1949
1950# Port description object.  Like a ParamDesc object, this represents a
1951# logical port in the SimObject class, not a particular port on a
1952# SimObject instance.  The latter are represented by PortRef objects.
1953class Port(object):
1954    # Generate a PortRef for this port on the given SimObject with the
1955    # given name
1956    def makeRef(self, simobj):
1957        return PortRef(simobj, self.name, self.role)
1958
1959    # Connect an instance of this port (on the given SimObject with
1960    # the given name) with the port described by the supplied PortRef
1961    def connect(self, simobj, ref):
1962        self.makeRef(simobj).connect(ref)
1963
1964    # No need for any pre-declarations at the moment as we merely rely
1965    # on an unsigned int.
1966    def cxx_predecls(self, code):
1967        pass
1968
1969    def pybind_predecls(self, code):
1970        cls.cxx_predecls(self, code)
1971
1972    # Declare an unsigned int with the same name as the port, that
1973    # will eventually hold the number of connected ports (and thus the
1974    # number of elements for a VectorPort).
1975    def cxx_decl(self, code):
1976        code('unsigned int port_${{self.name}}_connection_count;')
1977
1978class MasterPort(Port):
1979    # MasterPort("description")
1980    def __init__(self, *args):
1981        if len(args) == 1:
1982            self.desc = args[0]
1983            self.role = 'MASTER'
1984        else:
1985            raise TypeError, 'wrong number of arguments'
1986
1987class SlavePort(Port):
1988    # SlavePort("description")
1989    def __init__(self, *args):
1990        if len(args) == 1:
1991            self.desc = args[0]
1992            self.role = 'SLAVE'
1993        else:
1994            raise TypeError, 'wrong number of arguments'
1995
1996# VectorPort description object.  Like Port, but represents a vector
1997# of connections (e.g., as on a XBar).
1998class VectorPort(Port):
1999    def __init__(self, *args):
2000        self.isVec = True
2001
2002    def makeRef(self, simobj):
2003        return VectorPortRef(simobj, self.name, self.role)
2004
2005class VectorMasterPort(VectorPort):
2006    # VectorMasterPort("description")
2007    def __init__(self, *args):
2008        if len(args) == 1:
2009            self.desc = args[0]
2010            self.role = 'MASTER'
2011            VectorPort.__init__(self, *args)
2012        else:
2013            raise TypeError, 'wrong number of arguments'
2014
2015class VectorSlavePort(VectorPort):
2016    # VectorSlavePort("description")
2017    def __init__(self, *args):
2018        if len(args) == 1:
2019            self.desc = args[0]
2020            self.role = 'SLAVE'
2021            VectorPort.__init__(self, *args)
2022        else:
2023            raise TypeError, 'wrong number of arguments'
2024
2025# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2026# proxy objects (via set_param_desc()) so that proxy error messages
2027# make sense.
2028class PortParamDesc(object):
2029    __metaclass__ = Singleton
2030
2031    ptype_str = 'Port'
2032    ptype = Port
2033
2034baseEnums = allEnums.copy()
2035baseParams = allParams.copy()
2036
2037def clear():
2038    global allEnums, allParams
2039
2040    allEnums = baseEnums.copy()
2041    allParams = baseParams.copy()
2042
2043__all__ = ['Param', 'VectorParam',
2044           'Enum', 'Bool', 'String', 'Float',
2045           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2046           'Int32', 'UInt32', 'Int64', 'UInt64',
2047           'Counter', 'Addr', 'Tick', 'Percent',
2048           'TcpPort', 'UdpPort', 'EthernetAddr',
2049           'IpAddress', 'IpNetmask', 'IpWithPort',
2050           'MemorySize', 'MemorySize32',
2051           'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2052           'NetworkBandwidth', 'MemoryBandwidth',
2053           'AddrRange',
2054           'MaxAddr', 'MaxTick', 'AllMemory',
2055           'Time',
2056           'NextEthernetAddr', 'NULL',
2057           'MasterPort', 'SlavePort',
2058           'VectorMasterPort', 'VectorSlavePort']
2059
2060import SimObject
2061