params.py revision 13699
11758Ssaidi@eecs.umich.edu# Copyright (c) 2012-2014, 2017, 2018 ARM Limited
21758Ssaidi@eecs.umich.edu# All rights reserved.
31758Ssaidi@eecs.umich.edu#
41758Ssaidi@eecs.umich.edu# The license below extends only to copyright in the software and shall
51758Ssaidi@eecs.umich.edu# not be construed as granting a license to any other intellectual
61758Ssaidi@eecs.umich.edu# property including but not limited to intellectual property relating
71758Ssaidi@eecs.umich.edu# to a hardware implementation of the functionality of the software
81758Ssaidi@eecs.umich.edu# licensed hereunder.  You may use the software subject to the license
91758Ssaidi@eecs.umich.edu# terms below provided that you ensure that this notice is replicated
101758Ssaidi@eecs.umich.edu# unmodified and in its entirety in all distributions of the software,
111758Ssaidi@eecs.umich.edu# modified or unmodified, in source code or in binary form.
121758Ssaidi@eecs.umich.edu#
131758Ssaidi@eecs.umich.edu# Copyright (c) 2004-2006 The Regents of The University of Michigan
141758Ssaidi@eecs.umich.edu# Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
151758Ssaidi@eecs.umich.edu# All rights reserved.
161758Ssaidi@eecs.umich.edu#
171758Ssaidi@eecs.umich.edu# Redistribution and use in source and binary forms, with or without
181758Ssaidi@eecs.umich.edu# modification, are permitted provided that the following conditions are
191758Ssaidi@eecs.umich.edu# met: redistributions of source code must retain the above copyright
201758Ssaidi@eecs.umich.edu# notice, this list of conditions and the following disclaimer;
211758Ssaidi@eecs.umich.edu# redistributions in binary form must reproduce the above copyright
221758Ssaidi@eecs.umich.edu# notice, this list of conditions and the following disclaimer in the
231758Ssaidi@eecs.umich.edu# documentation and/or other materials provided with the distribution;
241758Ssaidi@eecs.umich.edu# neither the name of the copyright holders nor the names of its
251758Ssaidi@eecs.umich.edu# contributors may be used to endorse or promote products derived from
262665Ssaidi@eecs.umich.edu# this software without specific prior written permission.
272665Ssaidi@eecs.umich.edu#
281758Ssaidi@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
291049Sbinkertn@umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
301049Sbinkertn@umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
311049Sbinkertn@umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
321049Sbinkertn@umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
331049Sbinkertn@umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
341049Sbinkertn@umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
351049Sbinkertn@umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
361049Sbinkertn@umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
371049Sbinkertn@umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
381049Sbinkertn@umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
391049Sbinkertn@umich.edu#
401049Sbinkertn@umich.edu# Authors: Steve Reinhardt
411049Sbinkertn@umich.edu#          Nathan Binkert
421049Sbinkertn@umich.edu#          Gabe Black
431049Sbinkertn@umich.edu#          Andreas Hansson
441049Sbinkertn@umich.edu
451049Sbinkertn@umich.edu#####################################################################
461049Sbinkertn@umich.edu#
471049Sbinkertn@umich.edu# Parameter description classes
481049Sbinkertn@umich.edu#
491049Sbinkertn@umich.edu# The _params dictionary in each class maps parameter names to either
501049Sbinkertn@umich.edu# a Param or a VectorParam object.  These objects contain the
511049Sbinkertn@umich.edu# parameter description string, the parameter type, and the default
521049Sbinkertn@umich.edu# value (if any).  The convert() method on these objects is used to
531049Sbinkertn@umich.edu# force whatever value is assigned to the parameter to the appropriate
541049Sbinkertn@umich.edu# type.
551049Sbinkertn@umich.edu#
561049Sbinkertn@umich.edu# Note that the default values are loaded into the class's attribute
571049Sbinkertn@umich.edu# space when the parameter dictionary is initialized (in
581049Sbinkertn@umich.edu# MetaSimObject._new_param()); after that point they aren't used.
591049Sbinkertn@umich.edu#
601049Sbinkertn@umich.edu#####################################################################
611049Sbinkertn@umich.edu
621049Sbinkertn@umich.edufrom __future__ import print_function
631049Sbinkertn@umich.edu
641049Sbinkertn@umich.eduimport copy
651049Sbinkertn@umich.eduimport datetime
661049Sbinkertn@umich.eduimport re
671049Sbinkertn@umich.eduimport sys
681049Sbinkertn@umich.eduimport time
691049Sbinkertn@umich.eduimport math
701049Sbinkertn@umich.edu
711049Sbinkertn@umich.eduimport proxy
721049Sbinkertn@umich.eduimport ticks
731049Sbinkertn@umich.edufrom util import *
741049Sbinkertn@umich.edu
751049Sbinkertn@umich.edudef isSimObject(*args, **kwargs):
761049Sbinkertn@umich.edu    return SimObject.isSimObject(*args, **kwargs)
771049Sbinkertn@umich.edu
781049Sbinkertn@umich.edudef isSimObjectSequence(*args, **kwargs):
791049Sbinkertn@umich.edu    return SimObject.isSimObjectSequence(*args, **kwargs)
801049Sbinkertn@umich.edu
811049Sbinkertn@umich.edudef isSimObjectClass(*args, **kwargs):
821049Sbinkertn@umich.edu    return SimObject.isSimObjectClass(*args, **kwargs)
831049Sbinkertn@umich.edu
841049Sbinkertn@umich.eduallParams = {}
851049Sbinkertn@umich.edu
861049Sbinkertn@umich.educlass MetaParamValue(type):
871049Sbinkertn@umich.edu    def __new__(mcls, name, bases, dct):
881049Sbinkertn@umich.edu        cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
891049Sbinkertn@umich.edu        assert name not in allParams
901049Sbinkertn@umich.edu        allParams[name] = cls
911049Sbinkertn@umich.edu        return cls
921049Sbinkertn@umich.edu
931049Sbinkertn@umich.edu
941049Sbinkertn@umich.edu# Dummy base class to identify types that are legitimate for SimObject
951049Sbinkertn@umich.edu# parameters.
961049Sbinkertn@umich.educlass ParamValue(object):
971049Sbinkertn@umich.edu    __metaclass__ = MetaParamValue
981049Sbinkertn@umich.edu    cmd_line_settable = False
991049Sbinkertn@umich.edu
1001049Sbinkertn@umich.edu    # Generate the code needed as a prerequisite for declaring a C++
1011049Sbinkertn@umich.edu    # object of this type.  Typically generates one or more #include
1021049Sbinkertn@umich.edu    # statements.  Used when declaring parameters of this type.
1031049Sbinkertn@umich.edu    @classmethod
1041049Sbinkertn@umich.edu    def cxx_predecls(cls, code):
1051049Sbinkertn@umich.edu        pass
1061049Sbinkertn@umich.edu
1071049Sbinkertn@umich.edu    @classmethod
1081049Sbinkertn@umich.edu    def pybind_predecls(cls, code):
1091049Sbinkertn@umich.edu        cls.cxx_predecls(code)
1101049Sbinkertn@umich.edu
1111049Sbinkertn@umich.edu    # default for printing to .ini file is regular string conversion.
1121049Sbinkertn@umich.edu    # will be overridden in some cases
1131049Sbinkertn@umich.edu    def ini_str(self):
1141049Sbinkertn@umich.edu        return str(self)
1151049Sbinkertn@umich.edu
1161049Sbinkertn@umich.edu    # default for printing to .json file is regular string conversion.
1171049Sbinkertn@umich.edu    # will be overridden in some cases, mostly to use native Python
1181049Sbinkertn@umich.edu    # types where there are similar JSON types
1191049Sbinkertn@umich.edu    def config_value(self):
1201049Sbinkertn@umich.edu        return str(self)
1211049Sbinkertn@umich.edu
1221049Sbinkertn@umich.edu    # Prerequisites for .ini parsing with cxx_ini_parse
1231049Sbinkertn@umich.edu    @classmethod
1241049Sbinkertn@umich.edu    def cxx_ini_predecls(cls, code):
1251049Sbinkertn@umich.edu        pass
1261049Sbinkertn@umich.edu
1271049Sbinkertn@umich.edu    # parse a .ini file entry for this param from string expression
1281049Sbinkertn@umich.edu    # src into lvalue dest (of the param's C++ type)
1291049Sbinkertn@umich.edu    @classmethod
1301049Sbinkertn@umich.edu    def cxx_ini_parse(cls, code, src, dest, ret):
1311049Sbinkertn@umich.edu        code('// Unhandled param type: %s' % cls.__name__)
1321049Sbinkertn@umich.edu        code('%s false;' % ret)
1331049Sbinkertn@umich.edu
1341049Sbinkertn@umich.edu    # allows us to blithely call unproxy() on things without checking
1351049Sbinkertn@umich.edu    # if they're really proxies or not
1361049Sbinkertn@umich.edu    def unproxy(self, base):
1371049Sbinkertn@umich.edu        return self
1381049Sbinkertn@umich.edu
1391049Sbinkertn@umich.edu    # Produce a human readable version of the stored value
1401049Sbinkertn@umich.edu    def pretty_print(self, value):
1411049Sbinkertn@umich.edu        return str(value)
1421049Sbinkertn@umich.edu
1431049Sbinkertn@umich.edu# Regular parameter description.
1441049Sbinkertn@umich.educlass ParamDesc(object):
1451049Sbinkertn@umich.edu    def __init__(self, ptype_str, ptype, *args, **kwargs):
1461049Sbinkertn@umich.edu        self.ptype_str = ptype_str
1471049Sbinkertn@umich.edu        # remember ptype only if it is provided
1481049Sbinkertn@umich.edu        if ptype != None:
1491049Sbinkertn@umich.edu            self.ptype = ptype
1501049Sbinkertn@umich.edu
1511049Sbinkertn@umich.edu        if args:
1521049Sbinkertn@umich.edu            if len(args) == 1:
1531049Sbinkertn@umich.edu                self.desc = args[0]
1541049Sbinkertn@umich.edu            elif len(args) == 2:
1551049Sbinkertn@umich.edu                self.default = args[0]
1561049Sbinkertn@umich.edu                self.desc = args[1]
1571049Sbinkertn@umich.edu            else:
1581049Sbinkertn@umich.edu                raise TypeError('too many arguments')
1591049Sbinkertn@umich.edu
1601049Sbinkertn@umich.edu        if 'desc' in kwargs:
1611049Sbinkertn@umich.edu            assert(not hasattr(self, 'desc'))
1621049Sbinkertn@umich.edu            self.desc = kwargs['desc']
1631049Sbinkertn@umich.edu            del kwargs['desc']
1641049Sbinkertn@umich.edu
1651049Sbinkertn@umich.edu        if 'default' in kwargs:
1661049Sbinkertn@umich.edu            assert(not hasattr(self, 'default'))
1671049Sbinkertn@umich.edu            self.default = kwargs['default']
1681049Sbinkertn@umich.edu            del kwargs['default']
1691049Sbinkertn@umich.edu
1701049Sbinkertn@umich.edu        if kwargs:
1711049Sbinkertn@umich.edu            raise TypeError('extra unknown kwargs %s' % kwargs)
1721049Sbinkertn@umich.edu
1731049Sbinkertn@umich.edu        if not hasattr(self, 'desc'):
1741049Sbinkertn@umich.edu            raise TypeError('desc attribute missing')
1751049Sbinkertn@umich.edu
1761049Sbinkertn@umich.edu    def __getattr__(self, attr):
1771049Sbinkertn@umich.edu        if attr == 'ptype':
1781049Sbinkertn@umich.edu            ptype = SimObject.allClasses[self.ptype_str]
1791049Sbinkertn@umich.edu            assert isSimObjectClass(ptype)
1801049Sbinkertn@umich.edu            self.ptype = ptype
1811049Sbinkertn@umich.edu            return ptype
1821049Sbinkertn@umich.edu
1831049Sbinkertn@umich.edu        raise AttributeError("'%s' object has no attribute '%s'" % \
1841049Sbinkertn@umich.edu              (type(self).__name__, attr))
1851049Sbinkertn@umich.edu
1861049Sbinkertn@umich.edu    def example_str(self):
1871049Sbinkertn@umich.edu        if hasattr(self.ptype, "ex_str"):
1881049Sbinkertn@umich.edu            return self.ptype.ex_str
1891049Sbinkertn@umich.edu        else:
1901049Sbinkertn@umich.edu            return self.ptype_str
1911049Sbinkertn@umich.edu
1921049Sbinkertn@umich.edu    # Is the param available to be exposed on the command line
1931049Sbinkertn@umich.edu    def isCmdLineSettable(self):
1941049Sbinkertn@umich.edu        if hasattr(self.ptype, "cmd_line_settable"):
1951049Sbinkertn@umich.edu            return self.ptype.cmd_line_settable
1961049Sbinkertn@umich.edu        else:
1971049Sbinkertn@umich.edu            return False
1981049Sbinkertn@umich.edu
1991049Sbinkertn@umich.edu    def convert(self, value):
2001049Sbinkertn@umich.edu        if isinstance(value, proxy.BaseProxy):
2011049Sbinkertn@umich.edu            value.set_param_desc(self)
2021049Sbinkertn@umich.edu            return value
2031049Sbinkertn@umich.edu        if 'ptype' not in self.__dict__ and isNullPointer(value):
2041049Sbinkertn@umich.edu            # deferred evaluation of SimObject; continue to defer if
2051049Sbinkertn@umich.edu            # we're just assigning a null pointer
2061049Sbinkertn@umich.edu            return value
2071049Sbinkertn@umich.edu        if isinstance(value, self.ptype):
2082343Sbinkertn@umich.edu            return value
2091049Sbinkertn@umich.edu        if isNullPointer(value) and isSimObjectClass(self.ptype):
2101049Sbinkertn@umich.edu            return value
2111049Sbinkertn@umich.edu        return self.ptype(value)
2121049Sbinkertn@umich.edu
2131049Sbinkertn@umich.edu    def pretty_print(self, value):
2141049Sbinkertn@umich.edu        if isinstance(value, proxy.BaseProxy):
2151049Sbinkertn@umich.edu           return str(value)
2161049Sbinkertn@umich.edu        if isNullPointer(value):
2171049Sbinkertn@umich.edu           return NULL
2181049Sbinkertn@umich.edu        return self.ptype(value).pretty_print(value)
2191049Sbinkertn@umich.edu
2201049Sbinkertn@umich.edu    def cxx_predecls(self, code):
2211049Sbinkertn@umich.edu        code('#include <cstddef>')
2221049Sbinkertn@umich.edu        self.ptype.cxx_predecls(code)
2231049Sbinkertn@umich.edu
2242343Sbinkertn@umich.edu    def pybind_predecls(self, code):
2251049Sbinkertn@umich.edu        self.ptype.pybind_predecls(code)
2261049Sbinkertn@umich.edu
2271049Sbinkertn@umich.edu    def cxx_decl(self, code):
2281049Sbinkertn@umich.edu        code('${{self.ptype.cxx_type}} ${{self.name}};')
2291049Sbinkertn@umich.edu
2301049Sbinkertn@umich.edu# Vector-valued parameter description.  Just like ParamDesc, except
2311049Sbinkertn@umich.edu# that the value is a vector (list) of the specified type instead of a
2321049Sbinkertn@umich.edu# single value.
2331049Sbinkertn@umich.edu
2341049Sbinkertn@umich.educlass VectorParamValue(list):
2351049Sbinkertn@umich.edu    __metaclass__ = MetaParamValue
2361049Sbinkertn@umich.edu    def __setattr__(self, attr, value):
2371049Sbinkertn@umich.edu        raise AttributeError("Not allowed to set %s on '%s'" % \
2381049Sbinkertn@umich.edu                             (attr, type(self).__name__))
2391049Sbinkertn@umich.edu
2401049Sbinkertn@umich.edu    def config_value(self):
2411049Sbinkertn@umich.edu        return [v.config_value() for v in self]
2421049Sbinkertn@umich.edu
2431049Sbinkertn@umich.edu    def ini_str(self):
2441049Sbinkertn@umich.edu        return ' '.join([v.ini_str() for v in self])
2451049Sbinkertn@umich.edu
2461049Sbinkertn@umich.edu    def getValue(self):
2471049Sbinkertn@umich.edu        return [ v.getValue() for v in self ]
2481049Sbinkertn@umich.edu
2491049Sbinkertn@umich.edu    def unproxy(self, base):
2501049Sbinkertn@umich.edu        if len(self) == 1 and isinstance(self[0], proxy.BaseProxy):
2511049Sbinkertn@umich.edu            # The value is a proxy (e.g. Parent.any, Parent.all or
2521049Sbinkertn@umich.edu            # Parent.x) therefore try resolve it
2531049Sbinkertn@umich.edu            return self[0].unproxy(base)
2541049Sbinkertn@umich.edu        else:
2551049Sbinkertn@umich.edu            return [v.unproxy(base) for v in self]
2561049Sbinkertn@umich.edu
2571049Sbinkertn@umich.educlass SimObjectVector(VectorParamValue):
2581049Sbinkertn@umich.edu    # support clone operation
2591049Sbinkertn@umich.edu    def __call__(self, **kwargs):
2601049Sbinkertn@umich.edu        return SimObjectVector([v(**kwargs) for v in self])
2611049Sbinkertn@umich.edu
2621049Sbinkertn@umich.edu    def clear_parent(self, old_parent):
2631049Sbinkertn@umich.edu        for v in self:
2641049Sbinkertn@umich.edu            v.clear_parent(old_parent)
2651049Sbinkertn@umich.edu
2661049Sbinkertn@umich.edu    def set_parent(self, parent, name):
2671049Sbinkertn@umich.edu        if len(self) == 1:
2681049Sbinkertn@umich.edu            self[0].set_parent(parent, name)
2691049Sbinkertn@umich.edu        else:
2701049Sbinkertn@umich.edu            width = int(math.ceil(math.log(len(self))/math.log(10)))
2711049Sbinkertn@umich.edu            for i,v in enumerate(self):
2721049Sbinkertn@umich.edu                v.set_parent(parent, "%s%0*d" % (name, width, i))
2731049Sbinkertn@umich.edu
2741049Sbinkertn@umich.edu    def has_parent(self):
2751049Sbinkertn@umich.edu        return any([e.has_parent() for e in self if not isNullPointer(e)])
2761049Sbinkertn@umich.edu
2771049Sbinkertn@umich.edu    # return 'cpu0 cpu1' etc. for print_ini()
2781049Sbinkertn@umich.edu    def get_name(self):
2791049Sbinkertn@umich.edu        return ' '.join([v._name for v in self])
2801049Sbinkertn@umich.edu
2811049Sbinkertn@umich.edu    # By iterating through the constituent members of the vector here
2821049Sbinkertn@umich.edu    # we can nicely handle iterating over all a SimObject's children
2831049Sbinkertn@umich.edu    # without having to provide lots of special functions on
2841049Sbinkertn@umich.edu    # SimObjectVector directly.
2851049Sbinkertn@umich.edu    def descendants(self):
2861049Sbinkertn@umich.edu        for v in self:
2871049Sbinkertn@umich.edu            for obj in v.descendants():
2881049Sbinkertn@umich.edu                yield obj
2891049Sbinkertn@umich.edu
2901049Sbinkertn@umich.edu    def get_config_as_dict(self):
2911049Sbinkertn@umich.edu        a = []
2921049Sbinkertn@umich.edu        for v in self:
2931049Sbinkertn@umich.edu            a.append(v.get_config_as_dict())
2941049Sbinkertn@umich.edu        return a
2951049Sbinkertn@umich.edu
2961049Sbinkertn@umich.edu    # If we are replacing an item in the vector, make sure to set the
2971049Sbinkertn@umich.edu    # parent reference of the new SimObject to be the same as the parent
2981049Sbinkertn@umich.edu    # of the SimObject being replaced. Useful to have if we created
2991049Sbinkertn@umich.edu    # a SimObjectVector of temporary objects that will be modified later in
3001049Sbinkertn@umich.edu    # configuration scripts.
3011049Sbinkertn@umich.edu    def __setitem__(self, key, value):
3021049Sbinkertn@umich.edu        val = self[key]
3031049Sbinkertn@umich.edu        if value.has_parent():
3041049Sbinkertn@umich.edu            warn("SimObject %s already has a parent" % value.get_name() +\
3051049Sbinkertn@umich.edu                 " that is being overwritten by a SimObjectVector")
3061049Sbinkertn@umich.edu        value.set_parent(val.get_parent(), val._name)
3071049Sbinkertn@umich.edu        super(SimObjectVector, self).__setitem__(key, value)
3081049Sbinkertn@umich.edu
3091049Sbinkertn@umich.edu    # Enumerate the params of each member of the SimObject vector. Creates
3101049Sbinkertn@umich.edu    # strings that will allow indexing into the vector by the python code and
3111049Sbinkertn@umich.edu    # allow it to be specified on the command line.
3121049Sbinkertn@umich.edu    def enumerateParams(self, flags_dict = {},
3131049Sbinkertn@umich.edu                        cmd_line_str = "",
3141049Sbinkertn@umich.edu                        access_str = ""):
3151049Sbinkertn@umich.edu        if hasattr(self, "_paramEnumed"):
3161049Sbinkertn@umich.edu            print("Cycle detected enumerating params at %s?!" % (cmd_line_str))
3171049Sbinkertn@umich.edu        else:
3181049Sbinkertn@umich.edu            x = 0
3191049Sbinkertn@umich.edu            for vals in self:
3201049Sbinkertn@umich.edu                # Each entry in the SimObjectVector should be an
3211049Sbinkertn@umich.edu                # instance of a SimObject
3221049Sbinkertn@umich.edu                flags_dict = vals.enumerateParams(flags_dict,
3231049Sbinkertn@umich.edu                                                  cmd_line_str + "%d." % x,
3241049Sbinkertn@umich.edu                                                  access_str + "[%d]." % x)
3251049Sbinkertn@umich.edu                x = x + 1
3261049Sbinkertn@umich.edu
3271049Sbinkertn@umich.edu        return flags_dict
3281049Sbinkertn@umich.edu
3291049Sbinkertn@umich.educlass VectorParamDesc(ParamDesc):
3301049Sbinkertn@umich.edu    # Convert assigned value to appropriate type.  If the RHS is not a
3311049Sbinkertn@umich.edu    # list or tuple, it generates a single-element list.
3321049Sbinkertn@umich.edu    def convert(self, value):
3331049Sbinkertn@umich.edu        if isinstance(value, (list, tuple)):
3341049Sbinkertn@umich.edu            # list: coerce each element into new list
3351049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
3361049Sbinkertn@umich.edu        elif isinstance(value, str):
3371049Sbinkertn@umich.edu            # If input is a csv string
3381049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.convert(self, v) \
3391049Sbinkertn@umich.edu                         for v in value.strip('[').strip(']').split(',') ]
3401049Sbinkertn@umich.edu        else:
3411049Sbinkertn@umich.edu            # singleton: coerce to a single-element list
3421049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.convert(self, value) ]
3431049Sbinkertn@umich.edu
3441049Sbinkertn@umich.edu        if isSimObjectSequence(tmp_list):
3451049Sbinkertn@umich.edu            return SimObjectVector(tmp_list)
3461049Sbinkertn@umich.edu        else:
3471049Sbinkertn@umich.edu            return VectorParamValue(tmp_list)
3481049Sbinkertn@umich.edu
3491049Sbinkertn@umich.edu    # Produce a human readable example string that describes
3501049Sbinkertn@umich.edu    # how to set this vector parameter in the absence of a default
3511049Sbinkertn@umich.edu    # value.
3521049Sbinkertn@umich.edu    def example_str(self):
3531049Sbinkertn@umich.edu        s = super(VectorParamDesc, self).example_str()
3541049Sbinkertn@umich.edu        help_str = "[" + s + "," + s + ", ...]"
3551049Sbinkertn@umich.edu        return help_str
3561049Sbinkertn@umich.edu
3571049Sbinkertn@umich.edu    # Produce a human readable representation of the value of this vector param.
3581049Sbinkertn@umich.edu    def pretty_print(self, value):
3591049Sbinkertn@umich.edu        if isinstance(value, (list, tuple)):
3601049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ]
3611049Sbinkertn@umich.edu        elif isinstance(value, str):
3621049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ]
3631049Sbinkertn@umich.edu        else:
3641049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.pretty_print(self, value) ]
3651049Sbinkertn@umich.edu
3661049Sbinkertn@umich.edu        return tmp_list
3671049Sbinkertn@umich.edu
3681049Sbinkertn@umich.edu    # This is a helper function for the new config system
3691049Sbinkertn@umich.edu    def __call__(self, value):
3701049Sbinkertn@umich.edu        if isinstance(value, (list, tuple)):
3711049Sbinkertn@umich.edu            # list: coerce each element into new list
3721049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
3731049Sbinkertn@umich.edu        elif isinstance(value, str):
3741049Sbinkertn@umich.edu            # If input is a csv string
3751049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.convert(self, v) \
3761049Sbinkertn@umich.edu                         for v in value.strip('[').strip(']').split(',') ]
3771049Sbinkertn@umich.edu        else:
3781049Sbinkertn@umich.edu            # singleton: coerce to a single-element list
3791049Sbinkertn@umich.edu            tmp_list = [ ParamDesc.convert(self, value) ]
3801049Sbinkertn@umich.edu
3811049Sbinkertn@umich.edu        return VectorParamValue(tmp_list)
3821049Sbinkertn@umich.edu
3831049Sbinkertn@umich.edu    def cxx_predecls(self, code):
3841049Sbinkertn@umich.edu        code('#include <vector>')
3851049Sbinkertn@umich.edu        self.ptype.cxx_predecls(code)
386
387    def pybind_predecls(self, code):
388        code('#include <vector>')
389        self.ptype.pybind_predecls(code)
390
391    def cxx_decl(self, code):
392        code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
393
394class ParamFactory(object):
395    def __init__(self, param_desc_class, ptype_str = None):
396        self.param_desc_class = param_desc_class
397        self.ptype_str = ptype_str
398
399    def __getattr__(self, attr):
400        if self.ptype_str:
401            attr = self.ptype_str + '.' + attr
402        return ParamFactory(self.param_desc_class, attr)
403
404    # E.g., Param.Int(5, "number of widgets")
405    def __call__(self, *args, **kwargs):
406        ptype = None
407        try:
408            ptype = allParams[self.ptype_str]
409        except KeyError:
410            # if name isn't defined yet, assume it's a SimObject, and
411            # try to resolve it later
412            pass
413        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
414
415Param = ParamFactory(ParamDesc)
416VectorParam = ParamFactory(VectorParamDesc)
417
418#####################################################################
419#
420# Parameter Types
421#
422# Though native Python types could be used to specify parameter types
423# (the 'ptype' field of the Param and VectorParam classes), it's more
424# flexible to define our own set of types.  This gives us more control
425# over how Python expressions are converted to values (via the
426# __init__() constructor) and how these values are printed out (via
427# the __str__() conversion method).
428#
429#####################################################################
430
431# String-valued parameter.  Just mixin the ParamValue class with the
432# built-in str class.
433class String(ParamValue,str):
434    cxx_type = 'std::string'
435    cmd_line_settable = True
436
437    @classmethod
438    def cxx_predecls(self, code):
439        code('#include <string>')
440
441    def __call__(self, value):
442        self = value
443        return value
444
445    @classmethod
446    def cxx_ini_parse(self, code, src, dest, ret):
447        code('%s = %s;' % (dest, src))
448        code('%s true;' % ret)
449
450    def getValue(self):
451        return self
452
453# superclass for "numeric" parameter values, to emulate math
454# operations in a type-safe way.  e.g., a Latency times an int returns
455# a new Latency object.
456class NumericParamValue(ParamValue):
457    def __str__(self):
458        return str(self.value)
459
460    def __float__(self):
461        return float(self.value)
462
463    def __long__(self):
464        return long(self.value)
465
466    def __int__(self):
467        return int(self.value)
468
469    # hook for bounds checking
470    def _check(self):
471        return
472
473    def __mul__(self, other):
474        newobj = self.__class__(self)
475        newobj.value *= other
476        newobj._check()
477        return newobj
478
479    __rmul__ = __mul__
480
481    def __div__(self, other):
482        newobj = self.__class__(self)
483        newobj.value /= other
484        newobj._check()
485        return newobj
486
487    def __sub__(self, other):
488        newobj = self.__class__(self)
489        newobj.value -= other
490        newobj._check()
491        return newobj
492
493    def config_value(self):
494        return self.value
495
496    @classmethod
497    def cxx_ini_predecls(cls, code):
498        # Assume that base/str.hh will be included anyway
499        # code('#include "base/str.hh"')
500        pass
501
502    # The default for parsing PODs from an .ini entry is to extract from an
503    # istringstream and let overloading choose the right type according to
504    # the dest type.
505    @classmethod
506    def cxx_ini_parse(self, code, src, dest, ret):
507        code('%s to_number(%s, %s);' % (ret, src, dest))
508
509# Metaclass for bounds-checked integer parameters.  See CheckedInt.
510class CheckedIntType(MetaParamValue):
511    def __init__(cls, name, bases, dict):
512        super(CheckedIntType, cls).__init__(name, bases, dict)
513
514        # CheckedInt is an abstract base class, so we actually don't
515        # want to do any processing on it... the rest of this code is
516        # just for classes that derive from CheckedInt.
517        if name == 'CheckedInt':
518            return
519
520        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
521            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
522                panic("CheckedInt subclass %s must define either\n" \
523                      "    'min' and 'max' or 'size' and 'unsigned'\n",
524                      name);
525            if cls.unsigned:
526                cls.min = 0
527                cls.max = 2 ** cls.size - 1
528            else:
529                cls.min = -(2 ** (cls.size - 1))
530                cls.max = (2 ** (cls.size - 1)) - 1
531
532# Abstract superclass for bounds-checked integer parameters.  This
533# class is subclassed to generate parameter classes with specific
534# bounds.  Initialization of the min and max bounds is done in the
535# metaclass CheckedIntType.__init__.
536class CheckedInt(NumericParamValue):
537    __metaclass__ = CheckedIntType
538    cmd_line_settable = True
539
540    def _check(self):
541        if not self.min <= self.value <= self.max:
542            raise TypeError('Integer param out of bounds %d < %d < %d' % \
543                  (self.min, self.value, self.max))
544
545    def __init__(self, value):
546        if isinstance(value, str):
547            self.value = convert.toInteger(value)
548        elif isinstance(value, (int, long, float, NumericParamValue)):
549            self.value = long(value)
550        else:
551            raise TypeError("Can't convert object of type %s to CheckedInt" \
552                  % type(value).__name__)
553        self._check()
554
555    def __call__(self, value):
556        self.__init__(value)
557        return value
558
559    @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" % list(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 __bool__(self):
833        return bool(self.value)
834
835    # Python 2.7 uses __nonzero__ instead of __bool__
836    __nonzero__ = __bool__
837
838    def ini_str(self):
839        if self.value:
840            return 'true'
841        return 'false'
842
843    def config_value(self):
844        return self.value
845
846    @classmethod
847    def cxx_ini_predecls(cls, code):
848        # Assume that base/str.hh will be included anyway
849        # code('#include "base/str.hh"')
850        pass
851
852    @classmethod
853    def cxx_ini_parse(cls, code, src, dest, ret):
854        code('%s to_bool(%s, %s);' % (ret, src, dest))
855
856def IncEthernetAddr(addr, val = 1):
857    bytes = map(lambda x: int(x, 16), addr.split(':'))
858    bytes[5] += val
859    for i in (5, 4, 3, 2, 1):
860        val,rem = divmod(bytes[i], 256)
861        bytes[i] = rem
862        if val == 0:
863            break
864        bytes[i - 1] += val
865    assert(bytes[0] <= 255)
866    return ':'.join(map(lambda x: '%02x' % x, bytes))
867
868_NextEthernetAddr = "00:90:00:00:00:01"
869def NextEthernetAddr():
870    global _NextEthernetAddr
871
872    value = _NextEthernetAddr
873    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
874    return value
875
876class EthernetAddr(ParamValue):
877    cxx_type = 'Net::EthAddr'
878    ex_str = "00:90:00:00:00:01"
879    cmd_line_settable = True
880
881    @classmethod
882    def cxx_predecls(cls, code):
883        code('#include "base/inet.hh"')
884
885    def __init__(self, value):
886        if value == NextEthernetAddr:
887            self.value = value
888            return
889
890        if not isinstance(value, str):
891            raise TypeError("expected an ethernet address and didn't get one")
892
893        bytes = value.split(':')
894        if len(bytes) != 6:
895            raise TypeError('invalid ethernet address %s' % value)
896
897        for byte in bytes:
898            if not 0 <= int(byte, base=16) <= 0xff:
899                raise TypeError('invalid ethernet address %s' % value)
900
901        self.value = value
902
903    def __call__(self, value):
904        self.__init__(value)
905        return value
906
907    def unproxy(self, base):
908        if self.value == NextEthernetAddr:
909            return EthernetAddr(self.value())
910        return self
911
912    def getValue(self):
913        from _m5.net import EthAddr
914        return EthAddr(self.value)
915
916    def __str__(self):
917        return self.value
918
919    def ini_str(self):
920        return self.value
921
922    @classmethod
923    def cxx_ini_parse(self, code, src, dest, ret):
924        code('%s = Net::EthAddr(%s);' % (dest, src))
925        code('%s true;' % ret)
926
927# When initializing an IpAddress, pass in an existing IpAddress, a string of
928# the form "a.b.c.d", or an integer representing an IP.
929class IpAddress(ParamValue):
930    cxx_type = 'Net::IpAddress'
931    ex_str = "127.0.0.1"
932    cmd_line_settable = True
933
934    @classmethod
935    def cxx_predecls(cls, code):
936        code('#include "base/inet.hh"')
937
938    def __init__(self, value):
939        if isinstance(value, IpAddress):
940            self.ip = value.ip
941        else:
942            try:
943                self.ip = convert.toIpAddress(value)
944            except TypeError:
945                self.ip = long(value)
946        self.verifyIp()
947
948    def __call__(self, value):
949        self.__init__(value)
950        return value
951
952    def __str__(self):
953        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
954        return '%d.%d.%d.%d' % tuple(tup)
955
956    def __eq__(self, other):
957        if isinstance(other, IpAddress):
958            return self.ip == other.ip
959        elif isinstance(other, str):
960            try:
961                return self.ip == convert.toIpAddress(other)
962            except:
963                return False
964        else:
965            return self.ip == other
966
967    def __ne__(self, other):
968        return not (self == other)
969
970    def verifyIp(self):
971        if self.ip < 0 or self.ip >= (1 << 32):
972            raise TypeError("invalid ip address %#08x" % self.ip)
973
974    def getValue(self):
975        from _m5.net import IpAddress
976        return IpAddress(self.ip)
977
978# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
979# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
980# positional or keyword arguments.
981class IpNetmask(IpAddress):
982    cxx_type = 'Net::IpNetmask'
983    ex_str = "127.0.0.0/24"
984    cmd_line_settable = True
985
986    @classmethod
987    def cxx_predecls(cls, code):
988        code('#include "base/inet.hh"')
989
990    def __init__(self, *args, **kwargs):
991        def handle_kwarg(self, kwargs, key, elseVal = None):
992            if key in kwargs:
993                setattr(self, key, kwargs.pop(key))
994            elif elseVal:
995                setattr(self, key, elseVal)
996            else:
997                raise TypeError("No value set for %s" % key)
998
999        if len(args) == 0:
1000            handle_kwarg(self, kwargs, 'ip')
1001            handle_kwarg(self, kwargs, 'netmask')
1002
1003        elif len(args) == 1:
1004            if kwargs:
1005                if not 'ip' in kwargs and not 'netmask' in kwargs:
1006                    raise TypeError("Invalid arguments")
1007                handle_kwarg(self, kwargs, 'ip', args[0])
1008                handle_kwarg(self, kwargs, 'netmask', args[0])
1009            elif isinstance(args[0], IpNetmask):
1010                self.ip = args[0].ip
1011                self.netmask = args[0].netmask
1012            else:
1013                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
1014
1015        elif len(args) == 2:
1016            self.ip = args[0]
1017            self.netmask = args[1]
1018        else:
1019            raise TypeError("Too many arguments specified")
1020
1021        if kwargs:
1022            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1023
1024        self.verify()
1025
1026    def __call__(self, value):
1027        self.__init__(value)
1028        return value
1029
1030    def __str__(self):
1031        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
1032
1033    def __eq__(self, other):
1034        if isinstance(other, IpNetmask):
1035            return self.ip == other.ip and self.netmask == other.netmask
1036        elif isinstance(other, str):
1037            try:
1038                return (self.ip, self.netmask) == convert.toIpNetmask(other)
1039            except:
1040                return False
1041        else:
1042            return False
1043
1044    def verify(self):
1045        self.verifyIp()
1046        if self.netmask < 0 or self.netmask > 32:
1047            raise TypeError("invalid netmask %d" % netmask)
1048
1049    def getValue(self):
1050        from _m5.net import IpNetmask
1051        return IpNetmask(self.ip, self.netmask)
1052
1053# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1054# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1055class IpWithPort(IpAddress):
1056    cxx_type = 'Net::IpWithPort'
1057    ex_str = "127.0.0.1:80"
1058    cmd_line_settable = True
1059
1060    @classmethod
1061    def cxx_predecls(cls, code):
1062        code('#include "base/inet.hh"')
1063
1064    def __init__(self, *args, **kwargs):
1065        def handle_kwarg(self, kwargs, key, elseVal = None):
1066            if key in kwargs:
1067                setattr(self, key, kwargs.pop(key))
1068            elif elseVal:
1069                setattr(self, key, elseVal)
1070            else:
1071                raise TypeError("No value set for %s" % key)
1072
1073        if len(args) == 0:
1074            handle_kwarg(self, kwargs, 'ip')
1075            handle_kwarg(self, kwargs, 'port')
1076
1077        elif len(args) == 1:
1078            if kwargs:
1079                if not 'ip' in kwargs and not 'port' in kwargs:
1080                    raise TypeError("Invalid arguments")
1081                handle_kwarg(self, kwargs, 'ip', args[0])
1082                handle_kwarg(self, kwargs, 'port', args[0])
1083            elif isinstance(args[0], IpWithPort):
1084                self.ip = args[0].ip
1085                self.port = args[0].port
1086            else:
1087                (self.ip, self.port) = convert.toIpWithPort(args[0])
1088
1089        elif len(args) == 2:
1090            self.ip = args[0]
1091            self.port = args[1]
1092        else:
1093            raise TypeError("Too many arguments specified")
1094
1095        if kwargs:
1096            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1097
1098        self.verify()
1099
1100    def __call__(self, value):
1101        self.__init__(value)
1102        return value
1103
1104    def __str__(self):
1105        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1106
1107    def __eq__(self, other):
1108        if isinstance(other, IpWithPort):
1109            return self.ip == other.ip and self.port == other.port
1110        elif isinstance(other, str):
1111            try:
1112                return (self.ip, self.port) == convert.toIpWithPort(other)
1113            except:
1114                return False
1115        else:
1116            return False
1117
1118    def verify(self):
1119        self.verifyIp()
1120        if self.port < 0 or self.port > 0xffff:
1121            raise TypeError("invalid port %d" % self.port)
1122
1123    def getValue(self):
1124        from _m5.net import IpWithPort
1125        return IpWithPort(self.ip, self.port)
1126
1127time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1128                 "%a %b %d %H:%M:%S %Y",
1129                 "%Y/%m/%d %H:%M:%S",
1130                 "%Y/%m/%d %H:%M",
1131                 "%Y/%m/%d",
1132                 "%m/%d/%Y %H:%M:%S",
1133                 "%m/%d/%Y %H:%M",
1134                 "%m/%d/%Y",
1135                 "%m/%d/%y %H:%M:%S",
1136                 "%m/%d/%y %H:%M",
1137                 "%m/%d/%y"]
1138
1139
1140def parse_time(value):
1141    from time import gmtime, strptime, struct_time, time
1142    from datetime import datetime, date
1143
1144    if isinstance(value, struct_time):
1145        return value
1146
1147    if isinstance(value, (int, long)):
1148        return gmtime(value)
1149
1150    if isinstance(value, (datetime, date)):
1151        return value.timetuple()
1152
1153    if isinstance(value, str):
1154        if value in ('Now', 'Today'):
1155            return time.gmtime(time.time())
1156
1157        for format in time_formats:
1158            try:
1159                return strptime(value, format)
1160            except ValueError:
1161                pass
1162
1163    raise ValueError("Could not parse '%s' as a time" % value)
1164
1165class Time(ParamValue):
1166    cxx_type = 'tm'
1167
1168    @classmethod
1169    def cxx_predecls(cls, code):
1170        code('#include <time.h>')
1171
1172    def __init__(self, value):
1173        self.value = parse_time(value)
1174
1175    def __call__(self, value):
1176        self.__init__(value)
1177        return value
1178
1179    def getValue(self):
1180        from _m5.core import tm
1181        import calendar
1182
1183        return tm.gmtime(calendar.timegm(self.value))
1184
1185    def __str__(self):
1186        return time.asctime(self.value)
1187
1188    def ini_str(self):
1189        return str(self)
1190
1191    def get_config_as_dict(self):
1192        assert false
1193        return str(self)
1194
1195    @classmethod
1196    def cxx_ini_predecls(cls, code):
1197        code('#include <time.h>')
1198
1199    @classmethod
1200    def cxx_ini_parse(cls, code, src, dest, ret):
1201        code('char *_parse_ret = strptime((${src}).c_str(),')
1202        code('    "%a %b %d %H:%M:%S %Y", &(${dest}));')
1203        code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1204
1205# Enumerated types are a little more complex.  The user specifies the
1206# type as Enum(foo) where foo is either a list or dictionary of
1207# alternatives (typically strings, but not necessarily so).  (In the
1208# long run, the integer value of the parameter will be the list index
1209# or the corresponding dictionary value.  For now, since we only check
1210# that the alternative is valid and then spit it into a .ini file,
1211# there's not much point in using the dictionary.)
1212
1213# What Enum() must do is generate a new type encapsulating the
1214# provided list/dictionary so that specific values of the parameter
1215# can be instances of that type.  We define two hidden internal
1216# classes (_ListEnum and _DictEnum) to serve as base classes, then
1217# derive the new type from the appropriate base class on the fly.
1218
1219allEnums = {}
1220# Metaclass for Enum types
1221class MetaEnum(MetaParamValue):
1222    def __new__(mcls, name, bases, dict):
1223        assert name not in allEnums
1224
1225        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1226        allEnums[name] = cls
1227        return cls
1228
1229    def __init__(cls, name, bases, init_dict):
1230        if 'map' in init_dict:
1231            if not isinstance(cls.map, dict):
1232                raise TypeError("Enum-derived class attribute 'map' " \
1233                      "must be of type dict")
1234            # build list of value strings from map
1235            cls.vals = cls.map.keys()
1236            cls.vals.sort()
1237        elif 'vals' in init_dict:
1238            if not isinstance(cls.vals, list):
1239                raise TypeError("Enum-derived class attribute 'vals' " \
1240                      "must be of type list")
1241            # build string->value map from vals sequence
1242            cls.map = {}
1243            for idx,val in enumerate(cls.vals):
1244                cls.map[val] = idx
1245        else:
1246            raise TypeError("Enum-derived class must define "\
1247                  "attribute 'map' or 'vals'")
1248
1249        if cls.is_class:
1250            cls.cxx_type = '%s' % name
1251        else:
1252            cls.cxx_type = 'Enums::%s' % name
1253
1254        super(MetaEnum, cls).__init__(name, bases, init_dict)
1255
1256    # Generate C++ class declaration for this enum type.
1257    # Note that we wrap the enum in a class/struct to act as a namespace,
1258    # so that the enum strings can be brief w/o worrying about collisions.
1259    def cxx_decl(cls, code):
1260        wrapper_name = cls.wrapper_name
1261        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1262        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1263        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1264
1265        code('''\
1266#ifndef $idem_macro
1267#define $idem_macro
1268
1269''')
1270        if cls.is_class:
1271            code('''\
1272enum class $name {
1273''')
1274        else:
1275            code('''\
1276$wrapper $wrapper_name {
1277    enum $name {
1278''')
1279            code.indent(1)
1280        code.indent(1)
1281        for val in cls.vals:
1282            code('$val = ${{cls.map[val]}},')
1283        code('Num_$name = ${{len(cls.vals)}}')
1284        code.dedent(1)
1285        code('};')
1286
1287        if cls.is_class:
1288            code('''\
1289extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
1290''')
1291        elif cls.wrapper_is_struct:
1292            code('static const char *${name}Strings[Num_${name}];')
1293        else:
1294            code('extern const char *${name}Strings[Num_${name}];')
1295
1296        if not cls.is_class:
1297            code.dedent(1)
1298            code('};')
1299
1300        code()
1301        code('#endif // $idem_macro')
1302
1303    def cxx_def(cls, code):
1304        wrapper_name = cls.wrapper_name
1305        file_name = cls.__name__
1306        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1307
1308        code('#include "enums/$file_name.hh"')
1309        if cls.wrapper_is_struct:
1310            code('const char *${wrapper_name}::${name}Strings'
1311                '[Num_${name}] =')
1312        else:
1313            if cls.is_class:
1314                code('''\
1315const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
1316''')
1317            else:
1318                code('namespace Enums {')
1319                code.indent(1)
1320                code('const char *${name}Strings[Num_${name}] =')
1321
1322        code('{')
1323        code.indent(1)
1324        for val in cls.vals:
1325            code('"$val",')
1326        code.dedent(1)
1327        code('};')
1328
1329        if not cls.wrapper_is_struct and not cls.is_class:
1330            code.dedent(1)
1331            code('} // namespace $wrapper_name')
1332
1333
1334    def pybind_def(cls, code):
1335        name = cls.__name__
1336        enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
1337        wrapper_name = enum_name if cls.is_class else cls.wrapper_name
1338
1339        code('''#include "pybind11/pybind11.h"
1340#include "pybind11/stl.h"
1341
1342#include <sim/init.hh>
1343
1344namespace py = pybind11;
1345
1346static void
1347module_init(py::module &m_internal)
1348{
1349    py::module m = m_internal.def_submodule("enum_${name}");
1350
1351''')
1352        if cls.is_class:
1353            code('py::enum_<${enum_name}>(m, "enum_${name}")')
1354        else:
1355            code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
1356
1357        code.indent()
1358        code.indent()
1359        for val in cls.vals:
1360            code('.value("${val}", ${wrapper_name}::${val})')
1361        code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1362        code('.export_values()')
1363        code(';')
1364        code.dedent()
1365
1366        code('}')
1367        code.dedent()
1368        code()
1369        code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1370
1371
1372# Base class for enum types.
1373class Enum(ParamValue):
1374    __metaclass__ = MetaEnum
1375    vals = []
1376    cmd_line_settable = True
1377
1378    # The name of the wrapping namespace or struct
1379    wrapper_name = 'Enums'
1380
1381    # If true, the enum is wrapped in a struct rather than a namespace
1382    wrapper_is_struct = False
1383
1384    is_class = False
1385
1386    # If not None, use this as the enum name rather than this class name
1387    enum_name = None
1388
1389    def __init__(self, value):
1390        if value not in self.map:
1391            raise TypeError("Enum param got bad value '%s' (not in %s)" \
1392                  % (value, self.vals))
1393        self.value = value
1394
1395    def __call__(self, value):
1396        self.__init__(value)
1397        return value
1398
1399    @classmethod
1400    def cxx_predecls(cls, code):
1401        code('#include "enums/$0.hh"', cls.__name__)
1402
1403    @classmethod
1404    def cxx_ini_parse(cls, code, src, dest, ret):
1405        code('if (false) {')
1406        for elem_name in cls.map.iterkeys():
1407            code('} else if (%s == "%s") {' % (src, elem_name))
1408            code.indent()
1409            code('%s = Enums::%s;' % (dest, elem_name))
1410            code('%s true;' % ret)
1411            code.dedent()
1412        code('} else {')
1413        code('    %s false;' % ret)
1414        code('}')
1415
1416    def getValue(self):
1417        import m5.internal.params
1418        e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
1419        return e(self.map[self.value])
1420
1421    def __str__(self):
1422        return self.value
1423
1424# This param will generate a scoped c++ enum and its python bindings.
1425class ScopedEnum(Enum):
1426    __metaclass__ = MetaEnum
1427    vals = []
1428    cmd_line_settable = True
1429
1430    # The name of the wrapping namespace or struct
1431    wrapper_name = None
1432
1433    # If true, the enum is wrapped in a struct rather than a namespace
1434    wrapper_is_struct = False
1435
1436    # If true, the generated enum is a scoped enum
1437    is_class = True
1438
1439    # If not None, use this as the enum name rather than this class name
1440    enum_name = None
1441
1442# how big does a rounding error need to be before we warn about it?
1443frequency_tolerance = 0.001  # 0.1%
1444
1445class TickParamValue(NumericParamValue):
1446    cxx_type = 'Tick'
1447    ex_str = "1MHz"
1448    cmd_line_settable = True
1449
1450    @classmethod
1451    def cxx_predecls(cls, code):
1452        code('#include "base/types.hh"')
1453
1454    def __call__(self, value):
1455        self.__init__(value)
1456        return value
1457
1458    def getValue(self):
1459        return long(self.value)
1460
1461    @classmethod
1462    def cxx_ini_predecls(cls, code):
1463        code('#include <sstream>')
1464
1465    # Ticks are expressed in seconds in JSON files and in plain
1466    # Ticks in .ini files.  Switch based on a config flag
1467    @classmethod
1468    def cxx_ini_parse(self, code, src, dest, ret):
1469        code('${ret} to_number(${src}, ${dest});')
1470
1471class Latency(TickParamValue):
1472    ex_str = "100ns"
1473
1474    def __init__(self, value):
1475        if isinstance(value, (Latency, Clock)):
1476            self.ticks = value.ticks
1477            self.value = value.value
1478        elif isinstance(value, Frequency):
1479            self.ticks = value.ticks
1480            self.value = 1.0 / value.value
1481        elif value.endswith('t'):
1482            self.ticks = True
1483            self.value = int(value[:-1])
1484        else:
1485            self.ticks = False
1486            self.value = convert.toLatency(value)
1487
1488    def __call__(self, value):
1489        self.__init__(value)
1490        return value
1491
1492    def __getattr__(self, attr):
1493        if attr in ('latency', 'period'):
1494            return self
1495        if attr == 'frequency':
1496            return Frequency(self)
1497        raise AttributeError("Latency object has no attribute '%s'" % attr)
1498
1499    def getValue(self):
1500        if self.ticks or self.value == 0:
1501            value = self.value
1502        else:
1503            value = ticks.fromSeconds(self.value)
1504        return long(value)
1505
1506    def config_value(self):
1507        return self.getValue()
1508
1509    # convert latency to ticks
1510    def ini_str(self):
1511        return '%d' % self.getValue()
1512
1513class Frequency(TickParamValue):
1514    ex_str = "1GHz"
1515
1516    def __init__(self, value):
1517        if isinstance(value, (Latency, Clock)):
1518            if value.value == 0:
1519                self.value = 0
1520            else:
1521                self.value = 1.0 / value.value
1522            self.ticks = value.ticks
1523        elif isinstance(value, Frequency):
1524            self.value = value.value
1525            self.ticks = value.ticks
1526        else:
1527            self.ticks = False
1528            self.value = convert.toFrequency(value)
1529
1530    def __call__(self, value):
1531        self.__init__(value)
1532        return value
1533
1534    def __getattr__(self, attr):
1535        if attr == 'frequency':
1536            return self
1537        if attr in ('latency', 'period'):
1538            return Latency(self)
1539        raise AttributeError("Frequency object has no attribute '%s'" % attr)
1540
1541    # convert latency to ticks
1542    def getValue(self):
1543        if self.ticks or self.value == 0:
1544            value = self.value
1545        else:
1546            value = ticks.fromSeconds(1.0 / self.value)
1547        return long(value)
1548
1549    def config_value(self):
1550        return self.getValue()
1551
1552    def ini_str(self):
1553        return '%d' % self.getValue()
1554
1555# A generic Frequency and/or Latency value. Value is stored as a
1556# latency, just like Latency and Frequency.
1557class Clock(TickParamValue):
1558    def __init__(self, value):
1559        if isinstance(value, (Latency, Clock)):
1560            self.ticks = value.ticks
1561            self.value = value.value
1562        elif isinstance(value, Frequency):
1563            self.ticks = value.ticks
1564            self.value = 1.0 / value.value
1565        elif value.endswith('t'):
1566            self.ticks = True
1567            self.value = int(value[:-1])
1568        else:
1569            self.ticks = False
1570            self.value = convert.anyToLatency(value)
1571
1572    def __call__(self, value):
1573        self.__init__(value)
1574        return value
1575
1576    def __str__(self):
1577        return "%s" % Latency(self)
1578
1579    def __getattr__(self, attr):
1580        if attr == 'frequency':
1581            return Frequency(self)
1582        if attr in ('latency', 'period'):
1583            return Latency(self)
1584        raise AttributeError("Frequency object has no attribute '%s'" % attr)
1585
1586    def getValue(self):
1587        return self.period.getValue()
1588
1589    def config_value(self):
1590        return self.period.config_value()
1591
1592    def ini_str(self):
1593        return self.period.ini_str()
1594
1595class Voltage(Float):
1596    ex_str = "1V"
1597
1598    def __new__(cls, value):
1599        value = convert.toVoltage(value)
1600        return super(cls, Voltage).__new__(cls, value)
1601
1602    def __init__(self, value):
1603        value = convert.toVoltage(value)
1604        super(Voltage, self).__init__(value)
1605
1606class Current(Float):
1607    ex_str = "1mA"
1608
1609    def __new__(cls, value):
1610        value = convert.toCurrent(value)
1611        return super(cls, Current).__new__(cls, value)
1612
1613    def __init__(self, value):
1614        value = convert.toCurrent(value)
1615        super(Current, self).__init__(value)
1616
1617class Energy(Float):
1618    ex_str = "1pJ"
1619
1620    def __new__(cls, value):
1621        value = convert.toEnergy(value)
1622        return super(cls, Energy).__new__(cls, value)
1623
1624    def __init__(self, value):
1625        value = convert.toEnergy(value)
1626        super(Energy, self).__init__(value)
1627
1628class NetworkBandwidth(float,ParamValue):
1629    cxx_type = 'float'
1630    ex_str = "1Gbps"
1631    cmd_line_settable = True
1632
1633    def __new__(cls, value):
1634        # convert to bits per second
1635        val = convert.toNetworkBandwidth(value)
1636        return super(cls, NetworkBandwidth).__new__(cls, val)
1637
1638    def __str__(self):
1639        return str(self.val)
1640
1641    def __call__(self, value):
1642        val = convert.toNetworkBandwidth(value)
1643        self.__init__(val)
1644        return value
1645
1646    def getValue(self):
1647        # convert to seconds per byte
1648        value = 8.0 / float(self)
1649        # convert to ticks per byte
1650        value = ticks.fromSeconds(value)
1651        return float(value)
1652
1653    def ini_str(self):
1654        return '%f' % self.getValue()
1655
1656    def config_value(self):
1657        return '%f' % self.getValue()
1658
1659    @classmethod
1660    def cxx_ini_predecls(cls, code):
1661        code('#include <sstream>')
1662
1663    @classmethod
1664    def cxx_ini_parse(self, code, src, dest, ret):
1665        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1666
1667class MemoryBandwidth(float,ParamValue):
1668    cxx_type = 'float'
1669    ex_str = "1GB/s"
1670    cmd_line_settable = True
1671
1672    def __new__(cls, value):
1673        # convert to bytes per second
1674        val = convert.toMemoryBandwidth(value)
1675        return super(cls, MemoryBandwidth).__new__(cls, val)
1676
1677    def __call__(self, value):
1678        val = convert.toMemoryBandwidth(value)
1679        self.__init__(val)
1680        return value
1681
1682    def getValue(self):
1683        # convert to seconds per byte
1684        value = float(self)
1685        if value:
1686            value = 1.0 / float(self)
1687        # convert to ticks per byte
1688        value = ticks.fromSeconds(value)
1689        return float(value)
1690
1691    def ini_str(self):
1692        return '%f' % self.getValue()
1693
1694    def config_value(self):
1695        return '%f' % self.getValue()
1696
1697    @classmethod
1698    def cxx_ini_predecls(cls, code):
1699        code('#include <sstream>')
1700
1701    @classmethod
1702    def cxx_ini_parse(self, code, src, dest, ret):
1703        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1704
1705#
1706# "Constants"... handy aliases for various values.
1707#
1708
1709# Special class for NULL pointers.  Note the special check in
1710# make_param_value() above that lets these be assigned where a
1711# SimObject is required.
1712# only one copy of a particular node
1713class NullSimObject(object):
1714    __metaclass__ = Singleton
1715    _name = 'Null'
1716
1717    def __call__(cls):
1718        return cls
1719
1720    def _instantiate(self, parent = None, path = ''):
1721        pass
1722
1723    def ini_str(self):
1724        return 'Null'
1725
1726    def unproxy(self, base):
1727        return self
1728
1729    def set_path(self, parent, name):
1730        pass
1731
1732    def set_parent(self, parent, name):
1733        pass
1734
1735    def clear_parent(self, old_parent):
1736        pass
1737
1738    def descendants(self):
1739        return
1740        yield None
1741
1742    def get_config_as_dict(self):
1743        return {}
1744
1745    def __str__(self):
1746        return self._name
1747
1748    def config_value(self):
1749        return None
1750
1751    def getValue(self):
1752        return None
1753
1754# The only instance you'll ever need...
1755NULL = NullSimObject()
1756
1757def isNullPointer(value):
1758    return isinstance(value, NullSimObject)
1759
1760# Some memory range specifications use this as a default upper bound.
1761MaxAddr = Addr.max
1762MaxTick = Tick.max
1763AllMemory = AddrRange(0, MaxAddr)
1764
1765
1766#####################################################################
1767#
1768# Port objects
1769#
1770# Ports are used to interconnect objects in the memory system.
1771#
1772#####################################################################
1773
1774# Port reference: encapsulates a reference to a particular port on a
1775# particular SimObject.
1776class PortRef(object):
1777    def __init__(self, simobj, name, role):
1778        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1779        self.simobj = simobj
1780        self.name = name
1781        self.role = role
1782        self.peer = None   # not associated with another port yet
1783        self.ccConnected = False # C++ port connection done?
1784        self.index = -1  # always -1 for non-vector ports
1785
1786    def __str__(self):
1787        return '%s.%s' % (self.simobj, self.name)
1788
1789    def __len__(self):
1790        # Return the number of connected ports, i.e. 0 is we have no
1791        # peer and 1 if we do.
1792        return int(self.peer != None)
1793
1794    # for config.ini, print peer's name (not ours)
1795    def ini_str(self):
1796        return str(self.peer)
1797
1798    # for config.json
1799    def get_config_as_dict(self):
1800        return {'role' : self.role, 'peer' : str(self.peer)}
1801
1802    def __getattr__(self, attr):
1803        if attr == 'peerObj':
1804            # shorthand for proxies
1805            return self.peer.simobj
1806        raise AttributeError("'%s' object has no attribute '%s'" % \
1807              (self.__class__.__name__, attr))
1808
1809    # Full connection is symmetric (both ways).  Called via
1810    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1811    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1812    # e.g., "obj1.portA[3] = obj2.portB".
1813    def connect(self, other):
1814        if isinstance(other, VectorPortRef):
1815            # reference to plain VectorPort is implicit append
1816            other = other._get_next()
1817        if self.peer and not proxy.isproxy(self.peer):
1818            fatal("Port %s is already connected to %s, cannot connect %s\n",
1819                  self, self.peer, other);
1820        self.peer = other
1821        if proxy.isproxy(other):
1822            other.set_param_desc(PortParamDesc())
1823        elif isinstance(other, PortRef):
1824            if other.peer is not self:
1825                other.connect(self)
1826        else:
1827            raise TypeError("assigning non-port reference '%s' to port '%s'" \
1828                  % (other, self))
1829
1830    # Allow a master/slave port pair to be spliced between
1831    # a port and its connected peer. Useful operation for connecting
1832    # instrumentation structures into a system when it is necessary
1833    # to connect the instrumentation after the full system has been
1834    # constructed.
1835    def splice(self, new_master_peer, new_slave_peer):
1836        if not self.peer or proxy.isproxy(self.peer):
1837            fatal("Port %s not connected, cannot splice in new peers\n", self)
1838
1839        if not isinstance(new_master_peer, PortRef) or \
1840           not isinstance(new_slave_peer, PortRef):
1841            raise TypeError(
1842                  "Splicing non-port references '%s','%s' to port '%s'" % \
1843                  (new_master_peer, new_slave_peer, self))
1844
1845        old_peer = self.peer
1846        if self.role == 'SLAVE':
1847            self.peer = new_master_peer
1848            old_peer.peer = new_slave_peer
1849            new_master_peer.connect(self)
1850            new_slave_peer.connect(old_peer)
1851        elif self.role == 'MASTER':
1852            self.peer = new_slave_peer
1853            old_peer.peer = new_master_peer
1854            new_slave_peer.connect(self)
1855            new_master_peer.connect(old_peer)
1856        else:
1857            panic("Port %s has unknown role, "+\
1858                  "cannot splice in new peers\n", self)
1859
1860    def clone(self, simobj, memo):
1861        if self in memo:
1862            return memo[self]
1863        newRef = copy.copy(self)
1864        memo[self] = newRef
1865        newRef.simobj = simobj
1866        assert(isSimObject(newRef.simobj))
1867        if self.peer and not proxy.isproxy(self.peer):
1868            peerObj = self.peer.simobj(_memo=memo)
1869            newRef.peer = self.peer.clone(peerObj, memo)
1870            assert(not isinstance(newRef.peer, VectorPortRef))
1871        return newRef
1872
1873    def unproxy(self, simobj):
1874        assert(simobj is self.simobj)
1875        if proxy.isproxy(self.peer):
1876            try:
1877                realPeer = self.peer.unproxy(self.simobj)
1878            except:
1879                print("Error in unproxying port '%s' of %s" %
1880                      (self.name, self.simobj.path()))
1881                raise
1882            self.connect(realPeer)
1883
1884    # Call C++ to create corresponding port connection between C++ objects
1885    def ccConnect(self):
1886        from _m5.pyobject import connectPorts
1887
1888        if self.ccConnected: # already done this
1889            return
1890
1891        peer = self.peer
1892        if not self.peer: # nothing to connect to
1893            return
1894
1895        # check that we connect a master to a slave
1896        if self.role == peer.role:
1897            raise TypeError(
1898                "cannot connect '%s' and '%s' due to identical role '%s'" % \
1899                (peer, self, self.role))
1900
1901        if self.role == 'SLAVE':
1902            # do nothing and let the master take care of it
1903            return
1904
1905        try:
1906            # self is always the master and peer the slave
1907            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1908                         peer.simobj.getCCObject(), peer.name, peer.index)
1909        except:
1910            print("Error connecting port %s.%s to %s.%s" %
1911                  (self.simobj.path(), self.name,
1912                   peer.simobj.path(), peer.name))
1913            raise
1914        self.ccConnected = True
1915        peer.ccConnected = True
1916
1917# A reference to an individual element of a VectorPort... much like a
1918# PortRef, but has an index.
1919class VectorPortElementRef(PortRef):
1920    def __init__(self, simobj, name, role, index):
1921        PortRef.__init__(self, simobj, name, role)
1922        self.index = index
1923
1924    def __str__(self):
1925        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1926
1927# A reference to a complete vector-valued port (not just a single element).
1928# Can be indexed to retrieve individual VectorPortElementRef instances.
1929class VectorPortRef(object):
1930    def __init__(self, simobj, name, role):
1931        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1932        self.simobj = simobj
1933        self.name = name
1934        self.role = role
1935        self.elements = []
1936
1937    def __str__(self):
1938        return '%s.%s[:]' % (self.simobj, self.name)
1939
1940    def __len__(self):
1941        # Return the number of connected peers, corresponding the the
1942        # length of the elements.
1943        return len(self.elements)
1944
1945    # for config.ini, print peer's name (not ours)
1946    def ini_str(self):
1947        return ' '.join([el.ini_str() for el in self.elements])
1948
1949    # for config.json
1950    def get_config_as_dict(self):
1951        return {'role' : self.role,
1952                'peer' : [el.ini_str() for el in self.elements]}
1953
1954    def __getitem__(self, key):
1955        if not isinstance(key, int):
1956            raise TypeError("VectorPort index must be integer")
1957        if key >= len(self.elements):
1958            # need to extend list
1959            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1960                   for i in range(len(self.elements), key+1)]
1961            self.elements.extend(ext)
1962        return self.elements[key]
1963
1964    def _get_next(self):
1965        return self[len(self.elements)]
1966
1967    def __setitem__(self, key, value):
1968        if not isinstance(key, int):
1969            raise TypeError("VectorPort index must be integer")
1970        self[key].connect(value)
1971
1972    def connect(self, other):
1973        if isinstance(other, (list, tuple)):
1974            # Assign list of port refs to vector port.
1975            # For now, append them... not sure if that's the right semantics
1976            # or if it should replace the current vector.
1977            for ref in other:
1978                self._get_next().connect(ref)
1979        else:
1980            # scalar assignment to plain VectorPort is implicit append
1981            self._get_next().connect(other)
1982
1983    def clone(self, simobj, memo):
1984        if self in memo:
1985            return memo[self]
1986        newRef = copy.copy(self)
1987        memo[self] = newRef
1988        newRef.simobj = simobj
1989        assert(isSimObject(newRef.simobj))
1990        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1991        return newRef
1992
1993    def unproxy(self, simobj):
1994        [el.unproxy(simobj) for el in self.elements]
1995
1996    def ccConnect(self):
1997        [el.ccConnect() for el in self.elements]
1998
1999# Port description object.  Like a ParamDesc object, this represents a
2000# logical port in the SimObject class, not a particular port on a
2001# SimObject instance.  The latter are represented by PortRef objects.
2002class Port(object):
2003    # Generate a PortRef for this port on the given SimObject with the
2004    # given name
2005    def makeRef(self, simobj):
2006        return PortRef(simobj, self.name, self.role)
2007
2008    # Connect an instance of this port (on the given SimObject with
2009    # the given name) with the port described by the supplied PortRef
2010    def connect(self, simobj, ref):
2011        self.makeRef(simobj).connect(ref)
2012
2013    # No need for any pre-declarations at the moment as we merely rely
2014    # on an unsigned int.
2015    def cxx_predecls(self, code):
2016        pass
2017
2018    def pybind_predecls(self, code):
2019        cls.cxx_predecls(self, code)
2020
2021    # Declare an unsigned int with the same name as the port, that
2022    # will eventually hold the number of connected ports (and thus the
2023    # number of elements for a VectorPort).
2024    def cxx_decl(self, code):
2025        code('unsigned int port_${{self.name}}_connection_count;')
2026
2027class MasterPort(Port):
2028    # MasterPort("description")
2029    def __init__(self, *args):
2030        if len(args) == 1:
2031            self.desc = args[0]
2032            self.role = 'MASTER'
2033        else:
2034            raise TypeError('wrong number of arguments')
2035
2036class SlavePort(Port):
2037    # SlavePort("description")
2038    def __init__(self, *args):
2039        if len(args) == 1:
2040            self.desc = args[0]
2041            self.role = 'SLAVE'
2042        else:
2043            raise TypeError('wrong number of arguments')
2044
2045# VectorPort description object.  Like Port, but represents a vector
2046# of connections (e.g., as on a XBar).
2047class VectorPort(Port):
2048    def __init__(self, *args):
2049        self.isVec = True
2050
2051    def makeRef(self, simobj):
2052        return VectorPortRef(simobj, self.name, self.role)
2053
2054class VectorMasterPort(VectorPort):
2055    # VectorMasterPort("description")
2056    def __init__(self, *args):
2057        if len(args) == 1:
2058            self.desc = args[0]
2059            self.role = 'MASTER'
2060            VectorPort.__init__(self, *args)
2061        else:
2062            raise TypeError('wrong number of arguments')
2063
2064class VectorSlavePort(VectorPort):
2065    # VectorSlavePort("description")
2066    def __init__(self, *args):
2067        if len(args) == 1:
2068            self.desc = args[0]
2069            self.role = 'SLAVE'
2070            VectorPort.__init__(self, *args)
2071        else:
2072            raise TypeError('wrong number of arguments')
2073
2074# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2075# proxy objects (via set_param_desc()) so that proxy error messages
2076# make sense.
2077class PortParamDesc(object):
2078    __metaclass__ = Singleton
2079
2080    ptype_str = 'Port'
2081    ptype = Port
2082
2083baseEnums = allEnums.copy()
2084baseParams = allParams.copy()
2085
2086def clear():
2087    global allEnums, allParams
2088
2089    allEnums = baseEnums.copy()
2090    allParams = baseParams.copy()
2091
2092__all__ = ['Param', 'VectorParam',
2093           'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
2094           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2095           'Int32', 'UInt32', 'Int64', 'UInt64',
2096           'Counter', 'Addr', 'Tick', 'Percent',
2097           'TcpPort', 'UdpPort', 'EthernetAddr',
2098           'IpAddress', 'IpNetmask', 'IpWithPort',
2099           'MemorySize', 'MemorySize32',
2100           'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2101           'NetworkBandwidth', 'MemoryBandwidth',
2102           'AddrRange',
2103           'MaxAddr', 'MaxTick', 'AllMemory',
2104           'Time',
2105           'NextEthernetAddr', 'NULL',
2106           'MasterPort', 'SlavePort',
2107           'VectorMasterPort', 'VectorSlavePort']
2108
2109import SimObject
2110