params.py revision 13716:950f9a2ffb78
12623SN/A# Copyright (c) 2012-2014, 2017, 2018 ARM Limited
210596Sgabeblack@google.com# All rights reserved.
312276Sanouk.vanlaer@arm.com#
48926Sandreas.hansson@arm.com# The license below extends only to copyright in the software and shall
58926Sandreas.hansson@arm.com# not be construed as granting a license to any other intellectual
68926Sandreas.hansson@arm.com# property including but not limited to intellectual property relating
78926Sandreas.hansson@arm.com# to a hardware implementation of the functionality of the software
88926Sandreas.hansson@arm.com# licensed hereunder.  You may use the software subject to the license
98926Sandreas.hansson@arm.com# terms below provided that you ensure that this notice is replicated
108926Sandreas.hansson@arm.com# unmodified and in its entirety in all distributions of the software,
118926Sandreas.hansson@arm.com# modified or unmodified, in source code or in binary form.
128926Sandreas.hansson@arm.com#
138926Sandreas.hansson@arm.com# Copyright (c) 2004-2006 The Regents of The University of Michigan
148926Sandreas.hansson@arm.com# Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
152623SN/A# All rights reserved.
162623SN/A#
172623SN/A# Redistribution and use in source and binary forms, with or without
182623SN/A# modification, are permitted provided that the following conditions are
192623SN/A# met: redistributions of source code must retain the above copyright
202623SN/A# notice, this list of conditions and the following disclaimer;
212623SN/A# redistributions in binary form must reproduce the above copyright
222623SN/A# notice, this list of conditions and the following disclaimer in the
232623SN/A# documentation and/or other materials provided with the distribution;
242623SN/A# neither the name of the copyright holders nor the names of its
252623SN/A# contributors may be used to endorse or promote products derived from
262623SN/A# this software without specific prior written permission.
272623SN/A#
282623SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
292623SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
302623SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
312623SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
322623SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
332623SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
342623SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
352623SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
362623SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
372623SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
382623SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
392623SN/A#
402665Ssaidi@eecs.umich.edu# Authors: Steve Reinhardt
412665Ssaidi@eecs.umich.edu#          Nathan Binkert
422623SN/A#          Gabe Black
432623SN/A#          Andreas Hansson
4411793Sbrandon.potter@amd.com
4511793Sbrandon.potter@amd.com#####################################################################
463170Sstever@eecs.umich.edu#
478105Sgblack@eecs.umich.edu# Parameter description classes
482623SN/A#
494040Ssaidi@eecs.umich.edu# The _params dictionary in each class maps parameter names to either
509647Sdam.sunwoo@arm.com# a Param or a VectorParam object.  These objects contain the
516658Snate@binkert.org# parameter description string, the parameter type, and the default
522623SN/A# value (if any).  The convert() method on these objects is used to
539443SAndreas.Sandberg@ARM.com# force whatever value is assigned to the parameter to the appropriate
548232Snate@binkert.org# type.
558232Snate@binkert.org#
563348Sbinkertn@umich.edu# Note that the default values are loaded into the class's attribute
573348Sbinkertn@umich.edu# space when the parameter dictionary is initialized (in
588926Sandreas.hansson@arm.com# MetaSimObject._new_param()); after that point they aren't used.
594762Snate@binkert.org#
607678Sgblack@eecs.umich.edu#####################################################################
6111793Sbrandon.potter@amd.com
622901Ssaidi@eecs.umich.edufrom __future__ import print_function
632623SN/A
642623SN/Aimport copy
652623SN/Aimport datetime
662623SN/Aimport re
672623SN/Aimport sys
682623SN/Aimport time
692623SN/Aimport math
7011147Smitch.hayenga@arm.com
718921Sandreas.hansson@arm.comfrom . import proxy
7211148Smitch.hayenga@arm.comfrom . import ticks
7311435Smitch.hayenga@arm.comfrom .util import *
7411435Smitch.hayenga@arm.com
7511435Smitch.hayenga@arm.comdef isSimObject(*args, **kwargs):
762623SN/A    from . import SimObject
772623SN/A    return SimObject.isSimObject(*args, **kwargs)
785529Snate@binkert.org
7912127Sspwilson2@wisc.edudef isSimObjectSequence(*args, **kwargs):
8012127Sspwilson2@wisc.edu    from . import SimObject
8112127Sspwilson2@wisc.edu    return SimObject.isSimObjectSequence(*args, **kwargs)
8212127Sspwilson2@wisc.edu
835487Snate@binkert.orgdef isSimObjectClass(*args, **kwargs):
845487Snate@binkert.org    from . import SimObject
859095Sandreas.hansson@arm.com    return SimObject.isSimObjectClass(*args, **kwargs)
869095Sandreas.hansson@arm.com
8710537Sandreas.hansson@arm.comallParams = {}
8810537Sandreas.hansson@arm.com
892623SN/Aclass MetaParamValue(type):
902623SN/A    def __new__(mcls, name, bases, dct):
912623SN/A        cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
922623SN/A        assert name not in allParams
932623SN/A        allParams[name] = cls
942623SN/A        return cls
952623SN/A
966775SBrad.Beckmann@amd.com
976775SBrad.Beckmann@amd.com# Dummy base class to identify types that are legitimate for SimObject
986775SBrad.Beckmann@amd.com# parameters.
992623SN/Aclass ParamValue(object):
1002623SN/A    __metaclass__ = MetaParamValue
10110913Sandreas.sandberg@arm.com    cmd_line_settable = False
10210913Sandreas.sandberg@arm.com
1032623SN/A    # Generate the code needed as a prerequisite for declaring a C++
10412276Sanouk.vanlaer@arm.com    # object of this type.  Typically generates one or more #include
10512276Sanouk.vanlaer@arm.com    # statements.  Used when declaring parameters of this type.
10612276Sanouk.vanlaer@arm.com    @classmethod
1079448SAndreas.Sandberg@ARM.com    def cxx_predecls(cls, code):
10810913Sandreas.sandberg@arm.com        pass
1092623SN/A
1109443SAndreas.Sandberg@ARM.com    @classmethod
11111147Smitch.hayenga@arm.com    def pybind_predecls(cls, code):
11210913Sandreas.sandberg@arm.com        cls.cxx_predecls(code)
1139443SAndreas.Sandberg@ARM.com
1149443SAndreas.Sandberg@ARM.com    # default for printing to .ini file is regular string conversion.
1159443SAndreas.Sandberg@ARM.com    # will be overridden in some cases
1162915Sktlim@umich.edu    def ini_str(self):
11711147Smitch.hayenga@arm.com        return str(self)
1189443SAndreas.Sandberg@ARM.com
11910913Sandreas.sandberg@arm.com    # default for printing to .json file is regular string conversion.
1209443SAndreas.Sandberg@ARM.com    # will be overridden in some cases, mostly to use native Python
1219342SAndreas.Sandberg@arm.com    # types where there are similar JSON types
1229342SAndreas.Sandberg@arm.com    def config_value(self):
1232915Sktlim@umich.edu        return str(self)
12411148Smitch.hayenga@arm.com
12511148Smitch.hayenga@arm.com    # Prerequisites for .ini parsing with cxx_ini_parse
12611148Smitch.hayenga@arm.com    @classmethod
12711148Smitch.hayenga@arm.com    def cxx_ini_predecls(cls, code):
12811148Smitch.hayenga@arm.com        pass
12911148Smitch.hayenga@arm.com
13011148Smitch.hayenga@arm.com    # parse a .ini file entry for this param from string expression
13111321Ssteve.reinhardt@amd.com    # src into lvalue dest (of the param's C++ type)
13211151Smitch.hayenga@arm.com    @classmethod
13311148Smitch.hayenga@arm.com    def cxx_ini_parse(cls, code, src, dest, ret):
13411148Smitch.hayenga@arm.com        code('// Unhandled param type: %s' % cls.__name__)
13511148Smitch.hayenga@arm.com        code('%s false;' % ret)
13611148Smitch.hayenga@arm.com
13711148Smitch.hayenga@arm.com    # allows us to blithely call unproxy() on things without checking
13811148Smitch.hayenga@arm.com    # if they're really proxies or not
13911148Smitch.hayenga@arm.com    def unproxy(self, base):
14011148Smitch.hayenga@arm.com        return self
14111148Smitch.hayenga@arm.com
1429342SAndreas.Sandberg@arm.com    # Produce a human readable version of the stored value
1432915Sktlim@umich.edu    def pretty_print(self, value):
1449448SAndreas.Sandberg@ARM.com        return str(value)
1459448SAndreas.Sandberg@ARM.com
1465220Ssaidi@eecs.umich.edu# Regular parameter description.
1475220Ssaidi@eecs.umich.educlass ParamDesc(object):
1484940Snate@binkert.org    def __init__(self, ptype_str, ptype, *args, **kwargs):
1499523SAndreas.Sandberg@ARM.com        self.ptype_str = ptype_str
1503324Shsul@eecs.umich.edu        # remember ptype only if it is provided
1519448SAndreas.Sandberg@ARM.com        if ptype != None:
1529448SAndreas.Sandberg@ARM.com            self.ptype = ptype
15311147Smitch.hayenga@arm.com
15411147Smitch.hayenga@arm.com        if args:
15511147Smitch.hayenga@arm.com            if len(args) == 1:
15611147Smitch.hayenga@arm.com                self.desc = args[0]
15711147Smitch.hayenga@arm.com            elif len(args) == 2:
15811147Smitch.hayenga@arm.com                self.default = args[0]
15911147Smitch.hayenga@arm.com                self.desc = args[1]
16011147Smitch.hayenga@arm.com            else:
16111147Smitch.hayenga@arm.com                raise TypeError('too many arguments')
16211147Smitch.hayenga@arm.com
16311147Smitch.hayenga@arm.com        if 'desc' in kwargs:
16411147Smitch.hayenga@arm.com            assert(not hasattr(self, 'desc'))
16511147Smitch.hayenga@arm.com            self.desc = kwargs['desc']
16611147Smitch.hayenga@arm.com            del kwargs['desc']
16711147Smitch.hayenga@arm.com
1689448SAndreas.Sandberg@ARM.com        if 'default' in kwargs:
16912276Sanouk.vanlaer@arm.com            assert(not hasattr(self, 'default'))
17012276Sanouk.vanlaer@arm.com            self.default = kwargs['default']
17112276Sanouk.vanlaer@arm.com            del kwargs['default']
1722623SN/A
1732623SN/A        if kwargs:
1749443SAndreas.Sandberg@ARM.com            raise TypeError('extra unknown kwargs %s' % kwargs)
1759443SAndreas.Sandberg@ARM.com
1769443SAndreas.Sandberg@ARM.com        if not hasattr(self, 'desc'):
17710913Sandreas.sandberg@arm.com            raise TypeError('desc attribute missing')
1789443SAndreas.Sandberg@ARM.com
1799443SAndreas.Sandberg@ARM.com    def __getattr__(self, attr):
18011147Smitch.hayenga@arm.com        if attr == 'ptype':
1819443SAndreas.Sandberg@ARM.com            from . import SimObject
1829443SAndreas.Sandberg@ARM.com            ptype = SimObject.allClasses[self.ptype_str]
1839443SAndreas.Sandberg@ARM.com            assert isSimObjectClass(ptype)
1849443SAndreas.Sandberg@ARM.com            self.ptype = ptype
18510913Sandreas.sandberg@arm.com            return ptype
1869443SAndreas.Sandberg@ARM.com
1879443SAndreas.Sandberg@ARM.com        raise AttributeError("'%s' object has no attribute '%s'" % \
1889443SAndreas.Sandberg@ARM.com              (type(self).__name__, attr))
1899443SAndreas.Sandberg@ARM.com
1909443SAndreas.Sandberg@ARM.com    def example_str(self):
1912623SN/A        if hasattr(self.ptype, "ex_str"):
1922798Sktlim@umich.edu            return self.ptype.ex_str
1932623SN/A        else:
1949429SAndreas.Sandberg@ARM.com            return self.ptype_str
1959429SAndreas.Sandberg@ARM.com
1969443SAndreas.Sandberg@ARM.com    # Is the param available to be exposed on the command line
1979342SAndreas.Sandberg@arm.com    def isCmdLineSettable(self):
1989443SAndreas.Sandberg@ARM.com        if hasattr(self.ptype, "cmd_line_settable"):
1992623SN/A            return self.ptype.cmd_line_settable
2002623SN/A        else:
2012623SN/A            return False
2022623SN/A
2032623SN/A    def convert(self, value):
2042623SN/A        if isinstance(value, proxy.BaseProxy):
2059429SAndreas.Sandberg@ARM.com            value.set_param_desc(self)
2062623SN/A            return value
2079443SAndreas.Sandberg@ARM.com        if 'ptype' not in self.__dict__ and isNullPointer(value):
2082623SN/A            # deferred evaluation of SimObject; continue to defer if
2092623SN/A            # we're just assigning a null pointer
2102623SN/A            return value
2119523SAndreas.Sandberg@ARM.com        if isinstance(value, self.ptype):
2129523SAndreas.Sandberg@ARM.com            return value
2139523SAndreas.Sandberg@ARM.com        if isNullPointer(value) and isSimObjectClass(self.ptype):
2149524SAndreas.Sandberg@ARM.com            return value
2159523SAndreas.Sandberg@ARM.com        return self.ptype(value)
2169523SAndreas.Sandberg@ARM.com
2179523SAndreas.Sandberg@ARM.com    def pretty_print(self, value):
2189523SAndreas.Sandberg@ARM.com        if isinstance(value, proxy.BaseProxy):
2192623SN/A           return str(value)
2202623SN/A        if isNullPointer(value):
22110407Smitch.hayenga@arm.com           return NULL
2222623SN/A        return self.ptype(value).pretty_print(value)
22310407Smitch.hayenga@arm.com
2244940Snate@binkert.org    def cxx_predecls(self, code):
22511147Smitch.hayenga@arm.com        code('#include <cstddef>')
2262623SN/A        self.ptype.cxx_predecls(code)
22711147Smitch.hayenga@arm.com
22811147Smitch.hayenga@arm.com    def pybind_predecls(self, code):
22911147Smitch.hayenga@arm.com        self.ptype.pybind_predecls(code)
23010464SAndreas.Sandberg@ARM.com
2313686Sktlim@umich.edu    def cxx_decl(self, code):
23211147Smitch.hayenga@arm.com        code('${{self.ptype.cxx_type}} ${{self.name}};')
23311147Smitch.hayenga@arm.com
23411147Smitch.hayenga@arm.com# Vector-valued parameter description.  Just like ParamDesc, except
23511147Smitch.hayenga@arm.com# that the value is a vector (list) of the specified type instead of a
2369342SAndreas.Sandberg@arm.com# single value.
23711147Smitch.hayenga@arm.com
23811147Smitch.hayenga@arm.comclass VectorParamValue(list):
23911147Smitch.hayenga@arm.com    __metaclass__ = MetaParamValue
24011147Smitch.hayenga@arm.com    def __setattr__(self, attr, value):
24111526Sdavid.guillen@arm.com        raise AttributeError("Not allowed to set %s on '%s'" % \
24211526Sdavid.guillen@arm.com                             (attr, type(self).__name__))
2432623SN/A
2442623SN/A    def config_value(self):
2452623SN/A        return [v.config_value() for v in self]
2462623SN/A
2478737Skoansin.tan@gmail.com    def ini_str(self):
2482623SN/A        return ' '.join([v.ini_str() for v in self])
2494940Snate@binkert.org
2504940Snate@binkert.org    def getValue(self):
25111147Smitch.hayenga@arm.com        return [ v.getValue() for v in self ]
25211147Smitch.hayenga@arm.com
2532623SN/A    def unproxy(self, base):
2546043Sgblack@eecs.umich.edu        if len(self) == 1 and isinstance(self[0], proxy.BaseProxy):
2556043Sgblack@eecs.umich.edu            # The value is a proxy (e.g. Parent.any, Parent.all or
2566043Sgblack@eecs.umich.edu            # Parent.x) therefore try resolve it
2579342SAndreas.Sandberg@arm.com            return self[0].unproxy(base)
2582626SN/A        else:
25911147Smitch.hayenga@arm.com            return [v.unproxy(base) for v in self]
2602623SN/A
26111147Smitch.hayenga@arm.comclass SimObjectVector(VectorParamValue):
26211147Smitch.hayenga@arm.com    # support clone operation
26311147Smitch.hayenga@arm.com    def __call__(self, **kwargs):
26411147Smitch.hayenga@arm.com        return SimObjectVector([v(**kwargs) for v in self])
26511147Smitch.hayenga@arm.com
26611147Smitch.hayenga@arm.com    def clear_parent(self, old_parent):
26711147Smitch.hayenga@arm.com        for v in self:
26811147Smitch.hayenga@arm.com            v.clear_parent(old_parent)
26911526Sdavid.guillen@arm.com
2702623SN/A    def set_parent(self, parent, name):
2712623SN/A        if len(self) == 1:
2722623SN/A            self[0].set_parent(parent, name)
27310030SAli.Saidi@ARM.com        else:
27410030SAli.Saidi@ARM.com            width = int(math.ceil(math.log(len(self))/math.log(10)))
27510030SAli.Saidi@ARM.com            for i,v in enumerate(self):
27610030SAli.Saidi@ARM.com                v.set_parent(parent, "%s%0*d" % (name, width, i))
27710030SAli.Saidi@ARM.com
27810030SAli.Saidi@ARM.com    def has_parent(self):
27910529Smorr@cs.wisc.edu        return any([e.has_parent() for e in self if not isNullPointer(e)])
28010529Smorr@cs.wisc.edu
28111148Smitch.hayenga@arm.com    # return 'cpu0 cpu1' etc. for print_ini()
28211148Smitch.hayenga@arm.com    def get_name(self):
28311148Smitch.hayenga@arm.com        return ' '.join([v._name for v in self])
28411151Smitch.hayenga@arm.com
28511148Smitch.hayenga@arm.com    # By iterating through the constituent members of the vector here
28610529Smorr@cs.wisc.edu    # we can nicely handle iterating over all a SimObject's children
28710529Smorr@cs.wisc.edu    # without having to provide lots of special functions on
28810030SAli.Saidi@ARM.com    # SimObjectVector directly.
28911356Skrinat01@arm.com    def descendants(self):
29011356Skrinat01@arm.com        for v in self:
29111356Skrinat01@arm.com            for obj in v.descendants():
29211356Skrinat01@arm.com                yield obj
29310030SAli.Saidi@ARM.com
29410030SAli.Saidi@ARM.com    def get_config_as_dict(self):
29511147Smitch.hayenga@arm.com        a = []
29611147Smitch.hayenga@arm.com        for v in self:
29711147Smitch.hayenga@arm.com            a.append(v.get_config_as_dict())
29810030SAli.Saidi@ARM.com        return a
29910030SAli.Saidi@ARM.com
30010030SAli.Saidi@ARM.com    # If we are replacing an item in the vector, make sure to set the
30110030SAli.Saidi@ARM.com    # parent reference of the new SimObject to be the same as the parent
30210030SAli.Saidi@ARM.com    # of the SimObject being replaced. Useful to have if we created
30310030SAli.Saidi@ARM.com    # a SimObjectVector of temporary objects that will be modified later in
30410030SAli.Saidi@ARM.com    # configuration scripts.
30510030SAli.Saidi@ARM.com    def __setitem__(self, key, value):
30610030SAli.Saidi@ARM.com        val = self[key]
30710030SAli.Saidi@ARM.com        if value.has_parent():
30810030SAli.Saidi@ARM.com            warn("SimObject %s already has a parent" % value.get_name() +\
30910529Smorr@cs.wisc.edu                 " that is being overwritten by a SimObjectVector")
31010529Smorr@cs.wisc.edu        value.set_parent(val.get_parent(), val._name)
31111148Smitch.hayenga@arm.com        super(SimObjectVector, self).__setitem__(key, value)
31211321Ssteve.reinhardt@amd.com
31311151Smitch.hayenga@arm.com    # Enumerate the params of each member of the SimObject vector. Creates
31411148Smitch.hayenga@arm.com    # strings that will allow indexing into the vector by the python code and
31510529Smorr@cs.wisc.edu    # allow it to be specified on the command line.
31610529Smorr@cs.wisc.edu    def enumerateParams(self, flags_dict = {},
31710030SAli.Saidi@ARM.com                        cmd_line_str = "",
31810030SAli.Saidi@ARM.com                        access_str = ""):
31910030SAli.Saidi@ARM.com        if hasattr(self, "_paramEnumed"):
32010030SAli.Saidi@ARM.com            print("Cycle detected enumerating params at %s?!" % (cmd_line_str))
32111147Smitch.hayenga@arm.com        else:
32211147Smitch.hayenga@arm.com            x = 0
32311147Smitch.hayenga@arm.com            for vals in self:
32410030SAli.Saidi@ARM.com                # Each entry in the SimObjectVector should be an
32510030SAli.Saidi@ARM.com                # instance of a SimObject
32610030SAli.Saidi@ARM.com                flags_dict = vals.enumerateParams(flags_dict,
3272623SN/A                                                  cmd_line_str + "%d." % x,
32811608Snikos.nikoleris@arm.com                                                  access_str + "[%d]." % x)
32911608Snikos.nikoleris@arm.com                x = x + 1
3302623SN/A
33111147Smitch.hayenga@arm.com        return flags_dict
33211147Smitch.hayenga@arm.com
33311147Smitch.hayenga@arm.comclass VectorParamDesc(ParamDesc):
3343169Sstever@eecs.umich.edu    # Convert assigned value to appropriate type.  If the RHS is not a
3354870Sstever@eecs.umich.edu    # list or tuple, it generates a single-element list.
3362623SN/A    def convert(self, value):
33710665SAli.Saidi@ARM.com        if isinstance(value, (list, tuple)):
33810665SAli.Saidi@ARM.com            # list: coerce each element into new list
3392623SN/A            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
3404999Sgblack@eecs.umich.edu        elif isinstance(value, str):
3417520Sgblack@eecs.umich.edu            # If input is a csv string
3422623SN/A            tmp_list = [ ParamDesc.convert(self, v) \
3434999Sgblack@eecs.umich.edu                         for v in value.strip('[').strip(']').split(',') ]
3444999Sgblack@eecs.umich.edu        else:
3459814Sandreas.hansson@arm.com            # singleton: coerce to a single-element list
3464999Sgblack@eecs.umich.edu            tmp_list = [ ParamDesc.convert(self, value) ]
3477520Sgblack@eecs.umich.edu
3487520Sgblack@eecs.umich.edu        if isSimObjectSequence(tmp_list):
3494999Sgblack@eecs.umich.edu            return SimObjectVector(tmp_list)
3504999Sgblack@eecs.umich.edu        else:
3514999Sgblack@eecs.umich.edu            return VectorParamValue(tmp_list)
35210024Sdam.sunwoo@arm.com
3537520Sgblack@eecs.umich.edu    # Produce a human readable example string that describes
3548832SAli.Saidi@ARM.com    # how to set this vector parameter in the absence of a default
3554999Sgblack@eecs.umich.edu    # value.
3564999Sgblack@eecs.umich.edu    def example_str(self):
35711147Smitch.hayenga@arm.com        s = super(VectorParamDesc, self).example_str()
35811147Smitch.hayenga@arm.com        help_str = "[" + s + "," + s + ", ...]"
3594999Sgblack@eecs.umich.edu        return help_str
3604999Sgblack@eecs.umich.edu
3616623Sgblack@eecs.umich.edu    # Produce a human readable representation of the value of this vector param.
36210739Ssteve.reinhardt@amd.com    def pretty_print(self, value):
3637520Sgblack@eecs.umich.edu        if isinstance(value, (list, tuple)):
3644999Sgblack@eecs.umich.edu            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ]
3658105Sgblack@eecs.umich.edu        elif isinstance(value, str):
3664999Sgblack@eecs.umich.edu            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ]
3674999Sgblack@eecs.umich.edu        else:
3688931Sandreas.hansson@arm.com            tmp_list = [ ParamDesc.pretty_print(self, value) ]
3698931Sandreas.hansson@arm.com
3704999Sgblack@eecs.umich.edu        return tmp_list
3714999Sgblack@eecs.umich.edu
3724999Sgblack@eecs.umich.edu    # This is a helper function for the new config system
3734999Sgblack@eecs.umich.edu    def __call__(self, value):
3745012Sgblack@eecs.umich.edu        if isinstance(value, (list, tuple)):
3754999Sgblack@eecs.umich.edu            # list: coerce each element into new list
3764999Sgblack@eecs.umich.edu            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
3776102Sgblack@eecs.umich.edu        elif isinstance(value, str):
3784999Sgblack@eecs.umich.edu            # If input is a csv string
3794999Sgblack@eecs.umich.edu            tmp_list = [ ParamDesc.convert(self, v) \
3804968Sacolyte@umich.edu                         for v in value.strip('[').strip(']').split(',') ]
3814986Ssaidi@eecs.umich.edu        else:
3824999Sgblack@eecs.umich.edu            # singleton: coerce to a single-element list
3836739Sgblack@eecs.umich.edu            tmp_list = [ ParamDesc.convert(self, value) ]
3846739Sgblack@eecs.umich.edu
3856739Sgblack@eecs.umich.edu        return VectorParamValue(tmp_list)
3866739Sgblack@eecs.umich.edu
3876739Sgblack@eecs.umich.edu    def cxx_predecls(self, code):
3886739Sgblack@eecs.umich.edu        code('#include <vector>')
3896739Sgblack@eecs.umich.edu        self.ptype.cxx_predecls(code)
3906739Sgblack@eecs.umich.edu
3914999Sgblack@eecs.umich.edu    def pybind_predecls(self, code):
3924999Sgblack@eecs.umich.edu        code('#include <vector>')
3934999Sgblack@eecs.umich.edu        self.ptype.pybind_predecls(code)
39410760Ssteve.reinhardt@amd.com
3956078Sgblack@eecs.umich.edu    def cxx_decl(self, code):
3966078Sgblack@eecs.umich.edu        code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
3976078Sgblack@eecs.umich.edu
39811147Smitch.hayenga@arm.comclass ParamFactory(object):
3994999Sgblack@eecs.umich.edu    def __init__(self, param_desc_class, ptype_str = None):
4004968Sacolyte@umich.edu        self.param_desc_class = param_desc_class
4013170Sstever@eecs.umich.edu        self.ptype_str = ptype_str
4024999Sgblack@eecs.umich.edu
4034999Sgblack@eecs.umich.edu    def __getattr__(self, attr):
4044999Sgblack@eecs.umich.edu        if self.ptype_str:
4054999Sgblack@eecs.umich.edu            attr = self.ptype_str + '.' + attr
4064999Sgblack@eecs.umich.edu        return ParamFactory(self.param_desc_class, attr)
4077520Sgblack@eecs.umich.edu
4084999Sgblack@eecs.umich.edu    # E.g., Param.Int(5, "number of widgets")
4097520Sgblack@eecs.umich.edu    def __call__(self, *args, **kwargs):
4104999Sgblack@eecs.umich.edu        ptype = None
4114999Sgblack@eecs.umich.edu        try:
4122623SN/A            ptype = allParams[self.ptype_str]
4132623SN/A        except KeyError:
4142623SN/A            # if name isn't defined yet, assume it's a SimObject, and
41511303Ssteve.reinhardt@amd.com            # try to resolve it later
41611608Snikos.nikoleris@arm.com            pass
41711608Snikos.nikoleris@arm.com        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
41811303Ssteve.reinhardt@amd.com
41911303Ssteve.reinhardt@amd.comParam = ParamFactory(ParamDesc)
42011303Ssteve.reinhardt@amd.comVectorParam = ParamFactory(VectorParamDesc)
42111303Ssteve.reinhardt@amd.com
4227520Sgblack@eecs.umich.edu#####################################################################
4232623SN/A#
42411608Snikos.nikoleris@arm.com# Parameter Types
42511608Snikos.nikoleris@arm.com#
4262623SN/A# Though native Python types could be used to specify parameter types
42711147Smitch.hayenga@arm.com# (the 'ptype' field of the Param and VectorParam classes), it's more
42811147Smitch.hayenga@arm.com# flexible to define our own set of types.  This gives us more control
42910031SAli.Saidi@ARM.com# over how Python expressions are converted to values (via the
43010031SAli.Saidi@ARM.com# __init__() constructor) and how these values are printed out (via
43110031SAli.Saidi@ARM.com# the __str__() conversion method).
43210031SAli.Saidi@ARM.com#
43310031SAli.Saidi@ARM.com#####################################################################
43410031SAli.Saidi@ARM.com
43510031SAli.Saidi@ARM.com# String-valued parameter.  Just mixin the ParamValue class with the
43610031SAli.Saidi@ARM.com# built-in str class.
43710031SAli.Saidi@ARM.comclass String(ParamValue,str):
4383169Sstever@eecs.umich.edu    cxx_type = 'std::string'
4394870Sstever@eecs.umich.edu    cmd_line_settable = True
4402623SN/A
44110665SAli.Saidi@ARM.com    @classmethod
44210665SAli.Saidi@ARM.com    def cxx_predecls(self, code):
4432623SN/A        code('#include <string>')
4444999Sgblack@eecs.umich.edu
4457520Sgblack@eecs.umich.edu    def __call__(self, value):
4462623SN/A        self = value
4474999Sgblack@eecs.umich.edu        return value
4484999Sgblack@eecs.umich.edu
4499814Sandreas.hansson@arm.com    @classmethod
4504999Sgblack@eecs.umich.edu    def cxx_ini_parse(self, code, src, dest, ret):
45111321Ssteve.reinhardt@amd.com        code('%s = %s;' % (dest, src))
4527520Sgblack@eecs.umich.edu        code('%s true;' % ret)
4534999Sgblack@eecs.umich.edu
4544999Sgblack@eecs.umich.edu    def getValue(self):
4554999Sgblack@eecs.umich.edu        return self
45610024Sdam.sunwoo@arm.com
45711321Ssteve.reinhardt@amd.com# superclass for "numeric" parameter values, to emulate math
4588832SAli.Saidi@ARM.com# operations in a type-safe way.  e.g., a Latency times an int returns
4594999Sgblack@eecs.umich.edu# a new Latency object.
4604999Sgblack@eecs.umich.educlass NumericParamValue(ParamValue):
46111147Smitch.hayenga@arm.com    @staticmethod
4624999Sgblack@eecs.umich.edu    def unwrap(v):
4634999Sgblack@eecs.umich.edu        return v.value if isinstance(v, NumericParamValue) else v
4644999Sgblack@eecs.umich.edu
4654999Sgblack@eecs.umich.edu    def __str__(self):
4664999Sgblack@eecs.umich.edu        return str(self.value)
4674999Sgblack@eecs.umich.edu
4686102Sgblack@eecs.umich.edu    def __float__(self):
4694999Sgblack@eecs.umich.edu        return float(self.value)
47010030SAli.Saidi@ARM.com
4714999Sgblack@eecs.umich.edu    def __long__(self):
4724999Sgblack@eecs.umich.edu        return long(self.value)
4734999Sgblack@eecs.umich.edu
4744999Sgblack@eecs.umich.edu    def __int__(self):
4754999Sgblack@eecs.umich.edu        return int(self.value)
4764999Sgblack@eecs.umich.edu
4774999Sgblack@eecs.umich.edu    # hook for bounds checking
4784999Sgblack@eecs.umich.edu    def _check(self):
4796623Sgblack@eecs.umich.edu        return
4808949Sandreas.hansson@arm.com
4817520Sgblack@eecs.umich.edu    def __mul__(self, other):
4824999Sgblack@eecs.umich.edu        newobj = self.__class__(self)
4838105Sgblack@eecs.umich.edu        newobj.value *= NumericParamValue.unwrap(other)
4844999Sgblack@eecs.umich.edu        newobj._check()
4854999Sgblack@eecs.umich.edu        return newobj
4864999Sgblack@eecs.umich.edu
4878931Sandreas.hansson@arm.com    __rmul__ = __mul__
4888931Sandreas.hansson@arm.com
4894999Sgblack@eecs.umich.edu    def __truediv__(self, other):
4904999Sgblack@eecs.umich.edu        newobj = self.__class__(self)
49111148Smitch.hayenga@arm.com        newobj.value /= NumericParamValue.unwrap(other)
49211148Smitch.hayenga@arm.com        newobj._check()
49311148Smitch.hayenga@arm.com        return newobj
4944999Sgblack@eecs.umich.edu
4954999Sgblack@eecs.umich.edu    def __floordiv__(self, other):
4964999Sgblack@eecs.umich.edu        newobj = self.__class__(self)
4974999Sgblack@eecs.umich.edu        newobj.value //= NumericParamValue.unwrap(other)
4984999Sgblack@eecs.umich.edu        newobj._check()
4994999Sgblack@eecs.umich.edu        return newobj
50010563Sandreas.hansson@arm.com
5014999Sgblack@eecs.umich.edu
5024999Sgblack@eecs.umich.edu    def __add__(self, other):
5034999Sgblack@eecs.umich.edu        newobj = self.__class__(self)
5044999Sgblack@eecs.umich.edu        newobj.value += NumericParamValue.unwrap(other)
5054999Sgblack@eecs.umich.edu        newobj._check()
5064878Sstever@eecs.umich.edu        return newobj
5074040Ssaidi@eecs.umich.edu
5084040Ssaidi@eecs.umich.edu    def __sub__(self, other):
5094999Sgblack@eecs.umich.edu        newobj = self.__class__(self)
5104999Sgblack@eecs.umich.edu        newobj.value -= NumericParamValue.unwrap(other)
5114999Sgblack@eecs.umich.edu        newobj._check()
5124999Sgblack@eecs.umich.edu        return newobj
51310760Ssteve.reinhardt@amd.com
5146078Sgblack@eecs.umich.edu    def __iadd__(self, other):
5156078Sgblack@eecs.umich.edu        self.value += NumericParamValue.unwrap(other)
5166078Sgblack@eecs.umich.edu        self._check()
51711147Smitch.hayenga@arm.com        return self
51811147Smitch.hayenga@arm.com
5196739Sgblack@eecs.umich.edu    def __isub__(self, other):
5206739Sgblack@eecs.umich.edu        self.value -= NumericParamValue.unwrap(other)
5216739Sgblack@eecs.umich.edu        self._check()
5226739Sgblack@eecs.umich.edu        return self
5236739Sgblack@eecs.umich.edu
5243170Sstever@eecs.umich.edu    def __imul__(self, other):
5253170Sstever@eecs.umich.edu        self.value *= NumericParamValue.unwrap(other)
5264999Sgblack@eecs.umich.edu        self._check()
5274999Sgblack@eecs.umich.edu        return self
5284999Sgblack@eecs.umich.edu
5294999Sgblack@eecs.umich.edu    def __itruediv__(self, other):
5304999Sgblack@eecs.umich.edu        self.value /= NumericParamValue.unwrap(other)
5317520Sgblack@eecs.umich.edu        self._check()
5324999Sgblack@eecs.umich.edu        return self
5337520Sgblack@eecs.umich.edu
5344999Sgblack@eecs.umich.edu    def __ifloordiv__(self, other):
5354999Sgblack@eecs.umich.edu        self.value //= NumericParamValue.unwrap(other)
5362623SN/A        self._check()
5372623SN/A        return self
5382623SN/A
5392623SN/A    def __lt__(self, other):
5402623SN/A        return self.value < NumericParamValue.unwrap(other)
5412623SN/A
5422623SN/A    # Python 2.7 pre __future__.division operators
5434940Snate@binkert.org    # TODO: Remove these when after "import division from __future__"
5444940Snate@binkert.org    __div__ =  __truediv__
54511147Smitch.hayenga@arm.com    __idiv__ = __itruediv__
54611147Smitch.hayenga@arm.com
54711147Smitch.hayenga@arm.com    def config_value(self):
54811147Smitch.hayenga@arm.com        return self.value
54911147Smitch.hayenga@arm.com
55011148Smitch.hayenga@arm.com    @classmethod
55111148Smitch.hayenga@arm.com    def cxx_ini_predecls(cls, code):
55211435Smitch.hayenga@arm.com        # Assume that base/str.hh will be included anyway
55311435Smitch.hayenga@arm.com        # code('#include "base/str.hh"')
55411435Smitch.hayenga@arm.com        pass
55511147Smitch.hayenga@arm.com
55611147Smitch.hayenga@arm.com    # The default for parsing PODs from an .ini entry is to extract from an
55711147Smitch.hayenga@arm.com    # istringstream and let overloading choose the right type according to
55811147Smitch.hayenga@arm.com    # the dest type.
55911147Smitch.hayenga@arm.com    @classmethod
5605487Snate@binkert.org    def cxx_ini_parse(self, code, src, dest, ret):
5612623SN/A        code('%s to_number(%s, %s);' % (ret, src, dest))
5626078Sgblack@eecs.umich.edu
5632623SN/A# Metaclass for bounds-checked integer parameters.  See CheckedInt.
56412284Sjose.marinho@arm.comclass CheckedIntType(MetaParamValue):
5652623SN/A    def __init__(cls, name, bases, dict):
56610596Sgabeblack@google.com        super(CheckedIntType, cls).__init__(name, bases, dict)
5673387Sgblack@eecs.umich.edu
56810596Sgabeblack@google.com        # CheckedInt is an abstract base class, so we actually don't
56910596Sgabeblack@google.com        # want to do any processing on it... the rest of this code is
5702626SN/A        # just for classes that derive from CheckedInt.
5718143SAli.Saidi@ARM.com        if name == 'CheckedInt':
5729443SAndreas.Sandberg@ARM.com            return
5739443SAndreas.Sandberg@ARM.com
5748143SAli.Saidi@ARM.com        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
5759443SAndreas.Sandberg@ARM.com            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
5765348Ssaidi@eecs.umich.edu                panic("CheckedInt subclass %s must define either\n" \
5775669Sgblack@eecs.umich.edu                      "    'min' and 'max' or 'size' and 'unsigned'\n",
5785669Sgblack@eecs.umich.edu                      name);
5797720Sgblack@eecs.umich.edu            if cls.unsigned:
5807720Sgblack@eecs.umich.edu                cls.min = 0
5817720Sgblack@eecs.umich.edu                cls.max = 2 ** cls.size - 1
5827720Sgblack@eecs.umich.edu            else:
5837720Sgblack@eecs.umich.edu                cls.min = -(2 ** (cls.size - 1))
58410024Sdam.sunwoo@arm.com                cls.max = (2 ** (cls.size - 1)) - 1
5855894Sgblack@eecs.umich.edu
58611147Smitch.hayenga@arm.com# Abstract superclass for bounds-checked integer parameters.  This
5876023Snate@binkert.org# class is subclassed to generate parameter classes with specific
5885894Sgblack@eecs.umich.edu# bounds.  Initialization of the min and max bounds is done in the
5892623SN/A# metaclass CheckedIntType.__init__.
5902623SN/Aclass CheckedInt(NumericParamValue):
5914182Sgblack@eecs.umich.edu    __metaclass__ = CheckedIntType
5924182Sgblack@eecs.umich.edu    cmd_line_settable = True
5934182Sgblack@eecs.umich.edu
5942662Sstever@eecs.umich.edu    def _check(self):
5957720Sgblack@eecs.umich.edu        if not self.min <= self.value <= self.max:
5969023Sgblack@eecs.umich.edu            raise TypeError('Integer param out of bounds %d < %d < %d' % \
5975694Sgblack@eecs.umich.edu                  (self.min, self.value, self.max))
5985694Sgblack@eecs.umich.edu
5995694Sgblack@eecs.umich.edu    def __init__(self, value):
6005669Sgblack@eecs.umich.edu        if isinstance(value, str):
60111321Ssteve.reinhardt@amd.com            self.value = convert.toInteger(value)
6025669Sgblack@eecs.umich.edu        elif isinstance(value, (int, long, float, NumericParamValue)):
6035669Sgblack@eecs.umich.edu            self.value = long(value)
6048949Sandreas.hansson@arm.com        else:
6055669Sgblack@eecs.umich.edu            raise TypeError("Can't convert object of type %s to CheckedInt" \
6062623SN/A                  % type(value).__name__)
6078931Sandreas.hansson@arm.com        self._check()
6088931Sandreas.hansson@arm.com
6095669Sgblack@eecs.umich.edu    def __call__(self, value):
6105669Sgblack@eecs.umich.edu        self.__init__(value)
6114968Sacolyte@umich.edu        return value
6125669Sgblack@eecs.umich.edu
6134968Sacolyte@umich.edu    def __index__(self):
6145669Sgblack@eecs.umich.edu        return int(self.value)
6155669Sgblack@eecs.umich.edu
6165669Sgblack@eecs.umich.edu    @classmethod
6175669Sgblack@eecs.umich.edu    def cxx_predecls(cls, code):
6184182Sgblack@eecs.umich.edu        # most derived types require this, so we just do it here once
6192623SN/A        code('#include "base/types.hh"')
6203814Ssaidi@eecs.umich.edu
62111877Sbrandon.potter@amd.com    def getValue(self):
6225001Sgblack@eecs.umich.edu        return long(self.value)
62311147Smitch.hayenga@arm.com
6244998Sgblack@eecs.umich.educlass Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
6254998Sgblack@eecs.umich.educlass Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
62610381Sdam.sunwoo@arm.com
6274998Sgblack@eecs.umich.educlass Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
62810651Snikos.nikoleris@gmail.comclass UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
62910381Sdam.sunwoo@arm.comclass Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
6307655Sali.saidi@arm.comclass UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
6315001Sgblack@eecs.umich.educlass Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
6325001Sgblack@eecs.umich.educlass UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
6335001Sgblack@eecs.umich.educlass Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
6344998Sgblack@eecs.umich.educlass UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
63511877Sbrandon.potter@amd.com
63611877Sbrandon.potter@amd.comclass Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
63711877Sbrandon.potter@amd.comclass Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
63811877Sbrandon.potter@amd.comclass TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
63911877Sbrandon.potter@amd.comclass UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
64011877Sbrandon.potter@amd.com
64111877Sbrandon.potter@amd.comclass Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
6424182Sgblack@eecs.umich.edu
6434182Sgblack@eecs.umich.educlass Cycles(CheckedInt):
6442623SN/A    cxx_type = 'Cycles'
6453814Ssaidi@eecs.umich.edu    size = 64
6464539Sgblack@eecs.umich.edu    unsigned = True
6474539Sgblack@eecs.umich.edu
6483814Ssaidi@eecs.umich.edu    def getValue(self):
6493814Ssaidi@eecs.umich.edu        from _m5.core import Cycles
6505487Snate@binkert.org        return Cycles(self.value)
6515487Snate@binkert.org
6525487Snate@binkert.org    @classmethod
6535487Snate@binkert.org    def cxx_ini_predecls(cls, code):
6545487Snate@binkert.org        # Assume that base/str.hh will be included anyway
6555487Snate@binkert.org        # code('#include "base/str.hh"')
6565487Snate@binkert.org        pass
6579180Sandreas.hansson@arm.com
6589180Sandreas.hansson@arm.com    @classmethod
6599180Sandreas.hansson@arm.com    def cxx_ini_parse(cls, code, src, dest, ret):
6609180Sandreas.hansson@arm.com        code('uint64_t _temp;')
6619180Sandreas.hansson@arm.com        code('bool _ret = to_number(%s, _temp);' % src)
6622623SN/A        code('if (_ret)')
6632623SN/A        code('    %s = Cycles(_temp);' % dest)
6642623SN/A        code('%s _ret;' % ret)
66511321Ssteve.reinhardt@amd.com
6664182Sgblack@eecs.umich.educlass Float(ParamValue, float):
6672623SN/A    cxx_type = 'double'
6682623SN/A    cmd_line_settable = True
6699443SAndreas.Sandberg@ARM.com
6709443SAndreas.Sandberg@ARM.com    def __init__(self, value):
6719443SAndreas.Sandberg@ARM.com        if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
6725487Snate@binkert.org            self.value = float(value)
6739179Sandreas.hansson@arm.com        else:
6749179Sandreas.hansson@arm.com            raise TypeError("Can't convert object of type %s to Float" \
6755487Snate@binkert.org                  % type(value).__name__)
6762626SN/A
67711147Smitch.hayenga@arm.com    def __call__(self, value):
6782623SN/A        self.__init__(value)
6792623SN/A        return value
68010381Sdam.sunwoo@arm.com
68110381Sdam.sunwoo@arm.com    def getValue(self):
68210381Sdam.sunwoo@arm.com        return float(self.value)
68310464SAndreas.Sandberg@ARM.com
68410464SAndreas.Sandberg@ARM.com    def config_value(self):
68510381Sdam.sunwoo@arm.com        return self
68610381Sdam.sunwoo@arm.com
68710381Sdam.sunwoo@arm.com    @classmethod
6882623SN/A    def cxx_ini_predecls(cls, code):
6895315Sstever@gmail.com        code('#include <sstream>')
6905315Sstever@gmail.com
6915315Sstever@gmail.com    @classmethod
6925315Sstever@gmail.com    def cxx_ini_parse(self, code, src, dest, ret):
6935315Sstever@gmail.com        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
6945315Sstever@gmail.com
6952623SN/Aclass MemorySize(CheckedInt):
6962623SN/A    cxx_type = 'uint64_t'
6972623SN/A    ex_str = '512MB'
6982623SN/A    size = 64
6994762Snate@binkert.org    unsigned = True
7004762Snate@binkert.org    def __init__(self, value):
7012623SN/A        if isinstance(value, MemorySize):
7025529Snate@binkert.org            self.value = value.value
7032623SN/A        else:
704            self.value = convert.toMemorySize(value)
705        self._check()
706
707class MemorySize32(CheckedInt):
708    cxx_type = 'uint32_t'
709    ex_str = '512MB'
710    size = 32
711    unsigned = True
712    def __init__(self, value):
713        if isinstance(value, MemorySize):
714            self.value = value.value
715        else:
716            self.value = convert.toMemorySize(value)
717        self._check()
718
719class Addr(CheckedInt):
720    cxx_type = 'Addr'
721    size = 64
722    unsigned = True
723    def __init__(self, value):
724        if isinstance(value, Addr):
725            self.value = value.value
726        else:
727            try:
728                # Often addresses are referred to with sizes. Ex: A device
729                # base address is at "512MB".  Use toMemorySize() to convert
730                # these into addresses. If the address is not specified with a
731                # "size", an exception will occur and numeric translation will
732                # proceed below.
733                self.value = convert.toMemorySize(value)
734            except (TypeError, ValueError):
735                # Convert number to string and use long() to do automatic
736                # base conversion (requires base=0 for auto-conversion)
737                self.value = long(str(value), base=0)
738
739        self._check()
740    def __add__(self, other):
741        if isinstance(other, Addr):
742            return self.value + other.value
743        else:
744            return self.value + other
745    def pretty_print(self, value):
746        try:
747            val = convert.toMemorySize(value)
748        except TypeError:
749            val = long(value)
750        return "0x%x" % long(val)
751
752class AddrRange(ParamValue):
753    cxx_type = 'AddrRange'
754
755    def __init__(self, *args, **kwargs):
756        # Disable interleaving and hashing by default
757        self.intlvHighBit = 0
758        self.xorHighBit = 0
759        self.intlvBits = 0
760        self.intlvMatch = 0
761
762        def handle_kwargs(self, kwargs):
763            # An address range needs to have an upper limit, specified
764            # either explicitly with an end, or as an offset using the
765            # size keyword.
766            if 'end' in kwargs:
767                self.end = Addr(kwargs.pop('end'))
768            elif 'size' in kwargs:
769                self.end = self.start + Addr(kwargs.pop('size')) - 1
770            else:
771                raise TypeError("Either end or size must be specified")
772
773            # Now on to the optional bit
774            if 'intlvHighBit' in kwargs:
775                self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
776            if 'xorHighBit' in kwargs:
777                self.xorHighBit = int(kwargs.pop('xorHighBit'))
778            if 'intlvBits' in kwargs:
779                self.intlvBits = int(kwargs.pop('intlvBits'))
780            if 'intlvMatch' in kwargs:
781                self.intlvMatch = int(kwargs.pop('intlvMatch'))
782
783        if len(args) == 0:
784            self.start = Addr(kwargs.pop('start'))
785            handle_kwargs(self, kwargs)
786
787        elif len(args) == 1:
788            if kwargs:
789                self.start = Addr(args[0])
790                handle_kwargs(self, kwargs)
791            elif isinstance(args[0], (list, tuple)):
792                self.start = Addr(args[0][0])
793                self.end = Addr(args[0][1])
794            else:
795                self.start = Addr(0)
796                self.end = Addr(args[0]) - 1
797
798        elif len(args) == 2:
799            self.start = Addr(args[0])
800            self.end = Addr(args[1])
801        else:
802            raise TypeError("Too many arguments specified")
803
804        if kwargs:
805            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
806
807    def __str__(self):
808        return '%s:%s:%s:%s:%s:%s' \
809            % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\
810               self.intlvBits, self.intlvMatch)
811
812    def size(self):
813        # Divide the size by the size of the interleaving slice
814        return (long(self.end) - long(self.start) + 1) >> self.intlvBits
815
816    @classmethod
817    def cxx_predecls(cls, code):
818        Addr.cxx_predecls(code)
819        code('#include "base/addr_range.hh"')
820
821    @classmethod
822    def pybind_predecls(cls, code):
823        Addr.pybind_predecls(code)
824        code('#include "base/addr_range.hh"')
825
826    @classmethod
827    def cxx_ini_predecls(cls, code):
828        code('#include <sstream>')
829
830    @classmethod
831    def cxx_ini_parse(cls, code, src, dest, ret):
832        code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;')
833        code('uint64_t _intlvBits = 0, _intlvMatch = 0;')
834        code('char _sep;')
835        code('std::istringstream _stream(${src});')
836        code('_stream >> _start;')
837        code('_stream.get(_sep);')
838        code('_stream >> _end;')
839        code('if (!_stream.fail() && !_stream.eof()) {')
840        code('    _stream.get(_sep);')
841        code('    _stream >> _intlvHighBit;')
842        code('    _stream.get(_sep);')
843        code('    _stream >> _xorHighBit;')
844        code('    _stream.get(_sep);')
845        code('    _stream >> _intlvBits;')
846        code('    _stream.get(_sep);')
847        code('    _stream >> _intlvMatch;')
848        code('}')
849        code('bool _ret = !_stream.fail() &&'
850            '_stream.eof() && _sep == \':\';')
851        code('if (_ret)')
852        code('   ${dest} = AddrRange(_start, _end, _intlvHighBit, \
853                _xorHighBit, _intlvBits, _intlvMatch);')
854        code('${ret} _ret;')
855
856    def getValue(self):
857        # Go from the Python class to the wrapped C++ class
858        from _m5.range import AddrRange
859
860        return AddrRange(long(self.start), long(self.end),
861                         int(self.intlvHighBit), int(self.xorHighBit),
862                         int(self.intlvBits), int(self.intlvMatch))
863
864# Boolean parameter type.  Python doesn't let you subclass bool, since
865# it doesn't want to let you create multiple instances of True and
866# False.  Thus this is a little more complicated than String.
867class Bool(ParamValue):
868    cxx_type = 'bool'
869    cmd_line_settable = True
870
871    def __init__(self, value):
872        try:
873            self.value = convert.toBool(value)
874        except TypeError:
875            self.value = bool(value)
876
877    def __call__(self, value):
878        self.__init__(value)
879        return value
880
881    def getValue(self):
882        return bool(self.value)
883
884    def __str__(self):
885        return str(self.value)
886
887    # implement truth value testing for Bool parameters so that these params
888    # evaluate correctly during the python configuration phase
889    def __bool__(self):
890        return bool(self.value)
891
892    # Python 2.7 uses __nonzero__ instead of __bool__
893    __nonzero__ = __bool__
894
895    def ini_str(self):
896        if self.value:
897            return 'true'
898        return 'false'
899
900    def config_value(self):
901        return self.value
902
903    @classmethod
904    def cxx_ini_predecls(cls, code):
905        # Assume that base/str.hh will be included anyway
906        # code('#include "base/str.hh"')
907        pass
908
909    @classmethod
910    def cxx_ini_parse(cls, code, src, dest, ret):
911        code('%s to_bool(%s, %s);' % (ret, src, dest))
912
913def IncEthernetAddr(addr, val = 1):
914    bytes = [ int(x, 16) for x in addr.split(':') ]
915    bytes[5] += val
916    for i in (5, 4, 3, 2, 1):
917        val,rem = divmod(bytes[i], 256)
918        bytes[i] = rem
919        if val == 0:
920            break
921        bytes[i - 1] += val
922    assert(bytes[0] <= 255)
923    return ':'.join(map(lambda x: '%02x' % x, bytes))
924
925_NextEthernetAddr = "00:90:00:00:00:01"
926def NextEthernetAddr():
927    global _NextEthernetAddr
928
929    value = _NextEthernetAddr
930    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
931    return value
932
933class EthernetAddr(ParamValue):
934    cxx_type = 'Net::EthAddr'
935    ex_str = "00:90:00:00:00:01"
936    cmd_line_settable = True
937
938    @classmethod
939    def cxx_predecls(cls, code):
940        code('#include "base/inet.hh"')
941
942    def __init__(self, value):
943        if value == NextEthernetAddr:
944            self.value = value
945            return
946
947        if not isinstance(value, str):
948            raise TypeError("expected an ethernet address and didn't get one")
949
950        bytes = value.split(':')
951        if len(bytes) != 6:
952            raise TypeError('invalid ethernet address %s' % value)
953
954        for byte in bytes:
955            if not 0 <= int(byte, base=16) <= 0xff:
956                raise TypeError('invalid ethernet address %s' % value)
957
958        self.value = value
959
960    def __call__(self, value):
961        self.__init__(value)
962        return value
963
964    def unproxy(self, base):
965        if self.value == NextEthernetAddr:
966            return EthernetAddr(self.value())
967        return self
968
969    def getValue(self):
970        from _m5.net import EthAddr
971        return EthAddr(self.value)
972
973    def __str__(self):
974        return self.value
975
976    def ini_str(self):
977        return self.value
978
979    @classmethod
980    def cxx_ini_parse(self, code, src, dest, ret):
981        code('%s = Net::EthAddr(%s);' % (dest, src))
982        code('%s true;' % ret)
983
984# When initializing an IpAddress, pass in an existing IpAddress, a string of
985# the form "a.b.c.d", or an integer representing an IP.
986class IpAddress(ParamValue):
987    cxx_type = 'Net::IpAddress'
988    ex_str = "127.0.0.1"
989    cmd_line_settable = True
990
991    @classmethod
992    def cxx_predecls(cls, code):
993        code('#include "base/inet.hh"')
994
995    def __init__(self, value):
996        if isinstance(value, IpAddress):
997            self.ip = value.ip
998        else:
999            try:
1000                self.ip = convert.toIpAddress(value)
1001            except TypeError:
1002                self.ip = long(value)
1003        self.verifyIp()
1004
1005    def __call__(self, value):
1006        self.__init__(value)
1007        return value
1008
1009    def __str__(self):
1010        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
1011        return '%d.%d.%d.%d' % tuple(tup)
1012
1013    def __eq__(self, other):
1014        if isinstance(other, IpAddress):
1015            return self.ip == other.ip
1016        elif isinstance(other, str):
1017            try:
1018                return self.ip == convert.toIpAddress(other)
1019            except:
1020                return False
1021        else:
1022            return self.ip == other
1023
1024    def __ne__(self, other):
1025        return not (self == other)
1026
1027    def verifyIp(self):
1028        if self.ip < 0 or self.ip >= (1 << 32):
1029            raise TypeError("invalid ip address %#08x" % self.ip)
1030
1031    def getValue(self):
1032        from _m5.net import IpAddress
1033        return IpAddress(self.ip)
1034
1035# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
1036# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
1037# positional or keyword arguments.
1038class IpNetmask(IpAddress):
1039    cxx_type = 'Net::IpNetmask'
1040    ex_str = "127.0.0.0/24"
1041    cmd_line_settable = True
1042
1043    @classmethod
1044    def cxx_predecls(cls, code):
1045        code('#include "base/inet.hh"')
1046
1047    def __init__(self, *args, **kwargs):
1048        def handle_kwarg(self, kwargs, key, elseVal = None):
1049            if key in kwargs:
1050                setattr(self, key, kwargs.pop(key))
1051            elif elseVal:
1052                setattr(self, key, elseVal)
1053            else:
1054                raise TypeError("No value set for %s" % key)
1055
1056        if len(args) == 0:
1057            handle_kwarg(self, kwargs, 'ip')
1058            handle_kwarg(self, kwargs, 'netmask')
1059
1060        elif len(args) == 1:
1061            if kwargs:
1062                if not 'ip' in kwargs and not 'netmask' in kwargs:
1063                    raise TypeError("Invalid arguments")
1064                handle_kwarg(self, kwargs, 'ip', args[0])
1065                handle_kwarg(self, kwargs, 'netmask', args[0])
1066            elif isinstance(args[0], IpNetmask):
1067                self.ip = args[0].ip
1068                self.netmask = args[0].netmask
1069            else:
1070                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
1071
1072        elif len(args) == 2:
1073            self.ip = args[0]
1074            self.netmask = args[1]
1075        else:
1076            raise TypeError("Too many arguments specified")
1077
1078        if kwargs:
1079            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1080
1081        self.verify()
1082
1083    def __call__(self, value):
1084        self.__init__(value)
1085        return value
1086
1087    def __str__(self):
1088        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
1089
1090    def __eq__(self, other):
1091        if isinstance(other, IpNetmask):
1092            return self.ip == other.ip and self.netmask == other.netmask
1093        elif isinstance(other, str):
1094            try:
1095                return (self.ip, self.netmask) == convert.toIpNetmask(other)
1096            except:
1097                return False
1098        else:
1099            return False
1100
1101    def verify(self):
1102        self.verifyIp()
1103        if self.netmask < 0 or self.netmask > 32:
1104            raise TypeError("invalid netmask %d" % netmask)
1105
1106    def getValue(self):
1107        from _m5.net import IpNetmask
1108        return IpNetmask(self.ip, self.netmask)
1109
1110# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1111# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1112class IpWithPort(IpAddress):
1113    cxx_type = 'Net::IpWithPort'
1114    ex_str = "127.0.0.1:80"
1115    cmd_line_settable = True
1116
1117    @classmethod
1118    def cxx_predecls(cls, code):
1119        code('#include "base/inet.hh"')
1120
1121    def __init__(self, *args, **kwargs):
1122        def handle_kwarg(self, kwargs, key, elseVal = None):
1123            if key in kwargs:
1124                setattr(self, key, kwargs.pop(key))
1125            elif elseVal:
1126                setattr(self, key, elseVal)
1127            else:
1128                raise TypeError("No value set for %s" % key)
1129
1130        if len(args) == 0:
1131            handle_kwarg(self, kwargs, 'ip')
1132            handle_kwarg(self, kwargs, 'port')
1133
1134        elif len(args) == 1:
1135            if kwargs:
1136                if not 'ip' in kwargs and not 'port' in kwargs:
1137                    raise TypeError("Invalid arguments")
1138                handle_kwarg(self, kwargs, 'ip', args[0])
1139                handle_kwarg(self, kwargs, 'port', args[0])
1140            elif isinstance(args[0], IpWithPort):
1141                self.ip = args[0].ip
1142                self.port = args[0].port
1143            else:
1144                (self.ip, self.port) = convert.toIpWithPort(args[0])
1145
1146        elif len(args) == 2:
1147            self.ip = args[0]
1148            self.port = args[1]
1149        else:
1150            raise TypeError("Too many arguments specified")
1151
1152        if kwargs:
1153            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1154
1155        self.verify()
1156
1157    def __call__(self, value):
1158        self.__init__(value)
1159        return value
1160
1161    def __str__(self):
1162        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1163
1164    def __eq__(self, other):
1165        if isinstance(other, IpWithPort):
1166            return self.ip == other.ip and self.port == other.port
1167        elif isinstance(other, str):
1168            try:
1169                return (self.ip, self.port) == convert.toIpWithPort(other)
1170            except:
1171                return False
1172        else:
1173            return False
1174
1175    def verify(self):
1176        self.verifyIp()
1177        if self.port < 0 or self.port > 0xffff:
1178            raise TypeError("invalid port %d" % self.port)
1179
1180    def getValue(self):
1181        from _m5.net import IpWithPort
1182        return IpWithPort(self.ip, self.port)
1183
1184time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1185                 "%a %b %d %H:%M:%S %Y",
1186                 "%Y/%m/%d %H:%M:%S",
1187                 "%Y/%m/%d %H:%M",
1188                 "%Y/%m/%d",
1189                 "%m/%d/%Y %H:%M:%S",
1190                 "%m/%d/%Y %H:%M",
1191                 "%m/%d/%Y",
1192                 "%m/%d/%y %H:%M:%S",
1193                 "%m/%d/%y %H:%M",
1194                 "%m/%d/%y"]
1195
1196
1197def parse_time(value):
1198    from time import gmtime, strptime, struct_time, time
1199    from datetime import datetime, date
1200
1201    if isinstance(value, struct_time):
1202        return value
1203
1204    if isinstance(value, (int, long)):
1205        return gmtime(value)
1206
1207    if isinstance(value, (datetime, date)):
1208        return value.timetuple()
1209
1210    if isinstance(value, str):
1211        if value in ('Now', 'Today'):
1212            return time.gmtime(time.time())
1213
1214        for format in time_formats:
1215            try:
1216                return strptime(value, format)
1217            except ValueError:
1218                pass
1219
1220    raise ValueError("Could not parse '%s' as a time" % value)
1221
1222class Time(ParamValue):
1223    cxx_type = 'tm'
1224
1225    @classmethod
1226    def cxx_predecls(cls, code):
1227        code('#include <time.h>')
1228
1229    def __init__(self, value):
1230        self.value = parse_time(value)
1231
1232    def __call__(self, value):
1233        self.__init__(value)
1234        return value
1235
1236    def getValue(self):
1237        from _m5.core import tm
1238        import calendar
1239
1240        return tm.gmtime(calendar.timegm(self.value))
1241
1242    def __str__(self):
1243        return time.asctime(self.value)
1244
1245    def ini_str(self):
1246        return str(self)
1247
1248    def get_config_as_dict(self):
1249        assert false
1250        return str(self)
1251
1252    @classmethod
1253    def cxx_ini_predecls(cls, code):
1254        code('#include <time.h>')
1255
1256    @classmethod
1257    def cxx_ini_parse(cls, code, src, dest, ret):
1258        code('char *_parse_ret = strptime((${src}).c_str(),')
1259        code('    "%a %b %d %H:%M:%S %Y", &(${dest}));')
1260        code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1261
1262# Enumerated types are a little more complex.  The user specifies the
1263# type as Enum(foo) where foo is either a list or dictionary of
1264# alternatives (typically strings, but not necessarily so).  (In the
1265# long run, the integer value of the parameter will be the list index
1266# or the corresponding dictionary value.  For now, since we only check
1267# that the alternative is valid and then spit it into a .ini file,
1268# there's not much point in using the dictionary.)
1269
1270# What Enum() must do is generate a new type encapsulating the
1271# provided list/dictionary so that specific values of the parameter
1272# can be instances of that type.  We define two hidden internal
1273# classes (_ListEnum and _DictEnum) to serve as base classes, then
1274# derive the new type from the appropriate base class on the fly.
1275
1276allEnums = {}
1277# Metaclass for Enum types
1278class MetaEnum(MetaParamValue):
1279    def __new__(mcls, name, bases, dict):
1280        assert name not in allEnums
1281
1282        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1283        allEnums[name] = cls
1284        return cls
1285
1286    def __init__(cls, name, bases, init_dict):
1287        if 'map' in init_dict:
1288            if not isinstance(cls.map, dict):
1289                raise TypeError("Enum-derived class attribute 'map' " \
1290                      "must be of type dict")
1291            # build list of value strings from map
1292            cls.vals = list(cls.map.keys())
1293            cls.vals.sort()
1294        elif 'vals' in init_dict:
1295            if not isinstance(cls.vals, list):
1296                raise TypeError("Enum-derived class attribute 'vals' " \
1297                      "must be of type list")
1298            # build string->value map from vals sequence
1299            cls.map = {}
1300            for idx,val in enumerate(cls.vals):
1301                cls.map[val] = idx
1302        else:
1303            raise TypeError("Enum-derived class must define "\
1304                  "attribute 'map' or 'vals'")
1305
1306        if cls.is_class:
1307            cls.cxx_type = '%s' % name
1308        else:
1309            cls.cxx_type = 'Enums::%s' % name
1310
1311        super(MetaEnum, cls).__init__(name, bases, init_dict)
1312
1313    # Generate C++ class declaration for this enum type.
1314    # Note that we wrap the enum in a class/struct to act as a namespace,
1315    # so that the enum strings can be brief w/o worrying about collisions.
1316    def cxx_decl(cls, code):
1317        wrapper_name = cls.wrapper_name
1318        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1319        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1320        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1321
1322        code('''\
1323#ifndef $idem_macro
1324#define $idem_macro
1325
1326''')
1327        if cls.is_class:
1328            code('''\
1329enum class $name {
1330''')
1331        else:
1332            code('''\
1333$wrapper $wrapper_name {
1334    enum $name {
1335''')
1336            code.indent(1)
1337        code.indent(1)
1338        for val in cls.vals:
1339            code('$val = ${{cls.map[val]}},')
1340        code('Num_$name = ${{len(cls.vals)}}')
1341        code.dedent(1)
1342        code('};')
1343
1344        if cls.is_class:
1345            code('''\
1346extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
1347''')
1348        elif cls.wrapper_is_struct:
1349            code('static const char *${name}Strings[Num_${name}];')
1350        else:
1351            code('extern const char *${name}Strings[Num_${name}];')
1352
1353        if not cls.is_class:
1354            code.dedent(1)
1355            code('};')
1356
1357        code()
1358        code('#endif // $idem_macro')
1359
1360    def cxx_def(cls, code):
1361        wrapper_name = cls.wrapper_name
1362        file_name = cls.__name__
1363        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1364
1365        code('#include "enums/$file_name.hh"')
1366        if cls.wrapper_is_struct:
1367            code('const char *${wrapper_name}::${name}Strings'
1368                '[Num_${name}] =')
1369        else:
1370            if cls.is_class:
1371                code('''\
1372const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
1373''')
1374            else:
1375                code('namespace Enums {')
1376                code.indent(1)
1377                code('const char *${name}Strings[Num_${name}] =')
1378
1379        code('{')
1380        code.indent(1)
1381        for val in cls.vals:
1382            code('"$val",')
1383        code.dedent(1)
1384        code('};')
1385
1386        if not cls.wrapper_is_struct and not cls.is_class:
1387            code.dedent(1)
1388            code('} // namespace $wrapper_name')
1389
1390
1391    def pybind_def(cls, code):
1392        name = cls.__name__
1393        enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
1394        wrapper_name = enum_name if cls.is_class else cls.wrapper_name
1395
1396        code('''#include "pybind11/pybind11.h"
1397#include "pybind11/stl.h"
1398
1399#include <sim/init.hh>
1400
1401namespace py = pybind11;
1402
1403static void
1404module_init(py::module &m_internal)
1405{
1406    py::module m = m_internal.def_submodule("enum_${name}");
1407
1408''')
1409        if cls.is_class:
1410            code('py::enum_<${enum_name}>(m, "enum_${name}")')
1411        else:
1412            code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
1413
1414        code.indent()
1415        code.indent()
1416        for val in cls.vals:
1417            code('.value("${val}", ${wrapper_name}::${val})')
1418        code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1419        code('.export_values()')
1420        code(';')
1421        code.dedent()
1422
1423        code('}')
1424        code.dedent()
1425        code()
1426        code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1427
1428
1429# Base class for enum types.
1430class Enum(ParamValue):
1431    __metaclass__ = MetaEnum
1432    vals = []
1433    cmd_line_settable = True
1434
1435    # The name of the wrapping namespace or struct
1436    wrapper_name = 'Enums'
1437
1438    # If true, the enum is wrapped in a struct rather than a namespace
1439    wrapper_is_struct = False
1440
1441    is_class = False
1442
1443    # If not None, use this as the enum name rather than this class name
1444    enum_name = None
1445
1446    def __init__(self, value):
1447        if value not in self.map:
1448            raise TypeError("Enum param got bad value '%s' (not in %s)" \
1449                  % (value, self.vals))
1450        self.value = value
1451
1452    def __call__(self, value):
1453        self.__init__(value)
1454        return value
1455
1456    @classmethod
1457    def cxx_predecls(cls, code):
1458        code('#include "enums/$0.hh"', cls.__name__)
1459
1460    @classmethod
1461    def cxx_ini_parse(cls, code, src, dest, ret):
1462        code('if (false) {')
1463        for elem_name in cls.map.keys():
1464            code('} else if (%s == "%s") {' % (src, elem_name))
1465            code.indent()
1466            code('%s = Enums::%s;' % (dest, elem_name))
1467            code('%s true;' % ret)
1468            code.dedent()
1469        code('} else {')
1470        code('    %s false;' % ret)
1471        code('}')
1472
1473    def getValue(self):
1474        import m5.internal.params
1475        e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
1476        return e(self.map[self.value])
1477
1478    def __str__(self):
1479        return self.value
1480
1481# This param will generate a scoped c++ enum and its python bindings.
1482class ScopedEnum(Enum):
1483    __metaclass__ = MetaEnum
1484    vals = []
1485    cmd_line_settable = True
1486
1487    # The name of the wrapping namespace or struct
1488    wrapper_name = None
1489
1490    # If true, the enum is wrapped in a struct rather than a namespace
1491    wrapper_is_struct = False
1492
1493    # If true, the generated enum is a scoped enum
1494    is_class = True
1495
1496    # If not None, use this as the enum name rather than this class name
1497    enum_name = None
1498
1499# how big does a rounding error need to be before we warn about it?
1500frequency_tolerance = 0.001  # 0.1%
1501
1502class TickParamValue(NumericParamValue):
1503    cxx_type = 'Tick'
1504    ex_str = "1MHz"
1505    cmd_line_settable = True
1506
1507    @classmethod
1508    def cxx_predecls(cls, code):
1509        code('#include "base/types.hh"')
1510
1511    def __call__(self, value):
1512        self.__init__(value)
1513        return value
1514
1515    def getValue(self):
1516        return long(self.value)
1517
1518    @classmethod
1519    def cxx_ini_predecls(cls, code):
1520        code('#include <sstream>')
1521
1522    # Ticks are expressed in seconds in JSON files and in plain
1523    # Ticks in .ini files.  Switch based on a config flag
1524    @classmethod
1525    def cxx_ini_parse(self, code, src, dest, ret):
1526        code('${ret} to_number(${src}, ${dest});')
1527
1528class Latency(TickParamValue):
1529    ex_str = "100ns"
1530
1531    def __init__(self, value):
1532        if isinstance(value, (Latency, Clock)):
1533            self.ticks = value.ticks
1534            self.value = value.value
1535        elif isinstance(value, Frequency):
1536            self.ticks = value.ticks
1537            self.value = 1.0 / value.value
1538        elif value.endswith('t'):
1539            self.ticks = True
1540            self.value = int(value[:-1])
1541        else:
1542            self.ticks = False
1543            self.value = convert.toLatency(value)
1544
1545    def __call__(self, value):
1546        self.__init__(value)
1547        return value
1548
1549    def __getattr__(self, attr):
1550        if attr in ('latency', 'period'):
1551            return self
1552        if attr == 'frequency':
1553            return Frequency(self)
1554        raise AttributeError("Latency object has no attribute '%s'" % attr)
1555
1556    def getValue(self):
1557        if self.ticks or self.value == 0:
1558            value = self.value
1559        else:
1560            value = ticks.fromSeconds(self.value)
1561        return long(value)
1562
1563    def config_value(self):
1564        return self.getValue()
1565
1566    # convert latency to ticks
1567    def ini_str(self):
1568        return '%d' % self.getValue()
1569
1570class Frequency(TickParamValue):
1571    ex_str = "1GHz"
1572
1573    def __init__(self, value):
1574        if isinstance(value, (Latency, Clock)):
1575            if value.value == 0:
1576                self.value = 0
1577            else:
1578                self.value = 1.0 / value.value
1579            self.ticks = value.ticks
1580        elif isinstance(value, Frequency):
1581            self.value = value.value
1582            self.ticks = value.ticks
1583        else:
1584            self.ticks = False
1585            self.value = convert.toFrequency(value)
1586
1587    def __call__(self, value):
1588        self.__init__(value)
1589        return value
1590
1591    def __getattr__(self, attr):
1592        if attr == 'frequency':
1593            return self
1594        if attr in ('latency', 'period'):
1595            return Latency(self)
1596        raise AttributeError("Frequency object has no attribute '%s'" % attr)
1597
1598    # convert latency to ticks
1599    def getValue(self):
1600        if self.ticks or self.value == 0:
1601            value = self.value
1602        else:
1603            value = ticks.fromSeconds(1.0 / self.value)
1604        return long(value)
1605
1606    def config_value(self):
1607        return self.getValue()
1608
1609    def ini_str(self):
1610        return '%d' % self.getValue()
1611
1612# A generic Frequency and/or Latency value. Value is stored as a
1613# latency, just like Latency and Frequency.
1614class Clock(TickParamValue):
1615    def __init__(self, value):
1616        if isinstance(value, (Latency, Clock)):
1617            self.ticks = value.ticks
1618            self.value = value.value
1619        elif isinstance(value, Frequency):
1620            self.ticks = value.ticks
1621            self.value = 1.0 / value.value
1622        elif value.endswith('t'):
1623            self.ticks = True
1624            self.value = int(value[:-1])
1625        else:
1626            self.ticks = False
1627            self.value = convert.anyToLatency(value)
1628
1629    def __call__(self, value):
1630        self.__init__(value)
1631        return value
1632
1633    def __str__(self):
1634        return "%s" % Latency(self)
1635
1636    def __getattr__(self, attr):
1637        if attr == 'frequency':
1638            return Frequency(self)
1639        if attr in ('latency', 'period'):
1640            return Latency(self)
1641        raise AttributeError("Frequency object has no attribute '%s'" % attr)
1642
1643    def getValue(self):
1644        return self.period.getValue()
1645
1646    def config_value(self):
1647        return self.period.config_value()
1648
1649    def ini_str(self):
1650        return self.period.ini_str()
1651
1652class Voltage(Float):
1653    ex_str = "1V"
1654
1655    def __new__(cls, value):
1656        value = convert.toVoltage(value)
1657        return super(cls, Voltage).__new__(cls, value)
1658
1659    def __init__(self, value):
1660        value = convert.toVoltage(value)
1661        super(Voltage, self).__init__(value)
1662
1663class Current(Float):
1664    ex_str = "1mA"
1665
1666    def __new__(cls, value):
1667        value = convert.toCurrent(value)
1668        return super(cls, Current).__new__(cls, value)
1669
1670    def __init__(self, value):
1671        value = convert.toCurrent(value)
1672        super(Current, self).__init__(value)
1673
1674class Energy(Float):
1675    ex_str = "1pJ"
1676
1677    def __new__(cls, value):
1678        value = convert.toEnergy(value)
1679        return super(cls, Energy).__new__(cls, value)
1680
1681    def __init__(self, value):
1682        value = convert.toEnergy(value)
1683        super(Energy, self).__init__(value)
1684
1685class NetworkBandwidth(float,ParamValue):
1686    cxx_type = 'float'
1687    ex_str = "1Gbps"
1688    cmd_line_settable = True
1689
1690    def __new__(cls, value):
1691        # convert to bits per second
1692        val = convert.toNetworkBandwidth(value)
1693        return super(cls, NetworkBandwidth).__new__(cls, val)
1694
1695    def __str__(self):
1696        return str(self.val)
1697
1698    def __call__(self, value):
1699        val = convert.toNetworkBandwidth(value)
1700        self.__init__(val)
1701        return value
1702
1703    def getValue(self):
1704        # convert to seconds per byte
1705        value = 8.0 / float(self)
1706        # convert to ticks per byte
1707        value = ticks.fromSeconds(value)
1708        return float(value)
1709
1710    def ini_str(self):
1711        return '%f' % self.getValue()
1712
1713    def config_value(self):
1714        return '%f' % self.getValue()
1715
1716    @classmethod
1717    def cxx_ini_predecls(cls, code):
1718        code('#include <sstream>')
1719
1720    @classmethod
1721    def cxx_ini_parse(self, code, src, dest, ret):
1722        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1723
1724class MemoryBandwidth(float,ParamValue):
1725    cxx_type = 'float'
1726    ex_str = "1GB/s"
1727    cmd_line_settable = True
1728
1729    def __new__(cls, value):
1730        # convert to bytes per second
1731        val = convert.toMemoryBandwidth(value)
1732        return super(cls, MemoryBandwidth).__new__(cls, val)
1733
1734    def __call__(self, value):
1735        val = convert.toMemoryBandwidth(value)
1736        self.__init__(val)
1737        return value
1738
1739    def getValue(self):
1740        # convert to seconds per byte
1741        value = float(self)
1742        if value:
1743            value = 1.0 / float(self)
1744        # convert to ticks per byte
1745        value = ticks.fromSeconds(value)
1746        return float(value)
1747
1748    def ini_str(self):
1749        return '%f' % self.getValue()
1750
1751    def config_value(self):
1752        return '%f' % self.getValue()
1753
1754    @classmethod
1755    def cxx_ini_predecls(cls, code):
1756        code('#include <sstream>')
1757
1758    @classmethod
1759    def cxx_ini_parse(self, code, src, dest, ret):
1760        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1761
1762#
1763# "Constants"... handy aliases for various values.
1764#
1765
1766# Special class for NULL pointers.  Note the special check in
1767# make_param_value() above that lets these be assigned where a
1768# SimObject is required.
1769# only one copy of a particular node
1770class NullSimObject(object):
1771    __metaclass__ = Singleton
1772    _name = 'Null'
1773
1774    def __call__(cls):
1775        return cls
1776
1777    def _instantiate(self, parent = None, path = ''):
1778        pass
1779
1780    def ini_str(self):
1781        return 'Null'
1782
1783    def unproxy(self, base):
1784        return self
1785
1786    def set_path(self, parent, name):
1787        pass
1788
1789    def set_parent(self, parent, name):
1790        pass
1791
1792    def clear_parent(self, old_parent):
1793        pass
1794
1795    def descendants(self):
1796        return
1797        yield None
1798
1799    def get_config_as_dict(self):
1800        return {}
1801
1802    def __str__(self):
1803        return self._name
1804
1805    def config_value(self):
1806        return None
1807
1808    def getValue(self):
1809        return None
1810
1811# The only instance you'll ever need...
1812NULL = NullSimObject()
1813
1814def isNullPointer(value):
1815    return isinstance(value, NullSimObject)
1816
1817# Some memory range specifications use this as a default upper bound.
1818MaxAddr = Addr.max
1819MaxTick = Tick.max
1820AllMemory = AddrRange(0, MaxAddr)
1821
1822
1823#####################################################################
1824#
1825# Port objects
1826#
1827# Ports are used to interconnect objects in the memory system.
1828#
1829#####################################################################
1830
1831# Port reference: encapsulates a reference to a particular port on a
1832# particular SimObject.
1833class PortRef(object):
1834    def __init__(self, simobj, name, role):
1835        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1836        self.simobj = simobj
1837        self.name = name
1838        self.role = role
1839        self.peer = None   # not associated with another port yet
1840        self.ccConnected = False # C++ port connection done?
1841        self.index = -1  # always -1 for non-vector ports
1842
1843    def __str__(self):
1844        return '%s.%s' % (self.simobj, self.name)
1845
1846    def __len__(self):
1847        # Return the number of connected ports, i.e. 0 is we have no
1848        # peer and 1 if we do.
1849        return int(self.peer != None)
1850
1851    # for config.ini, print peer's name (not ours)
1852    def ini_str(self):
1853        return str(self.peer)
1854
1855    # for config.json
1856    def get_config_as_dict(self):
1857        return {'role' : self.role, 'peer' : str(self.peer)}
1858
1859    def __getattr__(self, attr):
1860        if attr == 'peerObj':
1861            # shorthand for proxies
1862            return self.peer.simobj
1863        raise AttributeError("'%s' object has no attribute '%s'" % \
1864              (self.__class__.__name__, attr))
1865
1866    # Full connection is symmetric (both ways).  Called via
1867    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1868    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1869    # e.g., "obj1.portA[3] = obj2.portB".
1870    def connect(self, other):
1871        if isinstance(other, VectorPortRef):
1872            # reference to plain VectorPort is implicit append
1873            other = other._get_next()
1874        if self.peer and not proxy.isproxy(self.peer):
1875            fatal("Port %s is already connected to %s, cannot connect %s\n",
1876                  self, self.peer, other);
1877        self.peer = other
1878        if proxy.isproxy(other):
1879            other.set_param_desc(PortParamDesc())
1880        elif isinstance(other, PortRef):
1881            if other.peer is not self:
1882                other.connect(self)
1883        else:
1884            raise TypeError("assigning non-port reference '%s' to port '%s'" \
1885                  % (other, self))
1886
1887    # Allow a master/slave port pair to be spliced between
1888    # a port and its connected peer. Useful operation for connecting
1889    # instrumentation structures into a system when it is necessary
1890    # to connect the instrumentation after the full system has been
1891    # constructed.
1892    def splice(self, new_master_peer, new_slave_peer):
1893        if not self.peer or proxy.isproxy(self.peer):
1894            fatal("Port %s not connected, cannot splice in new peers\n", self)
1895
1896        if not isinstance(new_master_peer, PortRef) or \
1897           not isinstance(new_slave_peer, PortRef):
1898            raise TypeError(
1899                  "Splicing non-port references '%s','%s' to port '%s'" % \
1900                  (new_master_peer, new_slave_peer, self))
1901
1902        old_peer = self.peer
1903        if self.role == 'SLAVE':
1904            self.peer = new_master_peer
1905            old_peer.peer = new_slave_peer
1906            new_master_peer.connect(self)
1907            new_slave_peer.connect(old_peer)
1908        elif self.role == 'MASTER':
1909            self.peer = new_slave_peer
1910            old_peer.peer = new_master_peer
1911            new_slave_peer.connect(self)
1912            new_master_peer.connect(old_peer)
1913        else:
1914            panic("Port %s has unknown role, "+\
1915                  "cannot splice in new peers\n", self)
1916
1917    def clone(self, simobj, memo):
1918        if self in memo:
1919            return memo[self]
1920        newRef = copy.copy(self)
1921        memo[self] = newRef
1922        newRef.simobj = simobj
1923        assert(isSimObject(newRef.simobj))
1924        if self.peer and not proxy.isproxy(self.peer):
1925            peerObj = self.peer.simobj(_memo=memo)
1926            newRef.peer = self.peer.clone(peerObj, memo)
1927            assert(not isinstance(newRef.peer, VectorPortRef))
1928        return newRef
1929
1930    def unproxy(self, simobj):
1931        assert(simobj is self.simobj)
1932        if proxy.isproxy(self.peer):
1933            try:
1934                realPeer = self.peer.unproxy(self.simobj)
1935            except:
1936                print("Error in unproxying port '%s' of %s" %
1937                      (self.name, self.simobj.path()))
1938                raise
1939            self.connect(realPeer)
1940
1941    # Call C++ to create corresponding port connection between C++ objects
1942    def ccConnect(self):
1943        from _m5.pyobject import connectPorts
1944
1945        if self.ccConnected: # already done this
1946            return
1947
1948        peer = self.peer
1949        if not self.peer: # nothing to connect to
1950            return
1951
1952        # check that we connect a master to a slave
1953        if self.role == peer.role:
1954            raise TypeError(
1955                "cannot connect '%s' and '%s' due to identical role '%s'" % \
1956                (peer, self, self.role))
1957
1958        if self.role == 'SLAVE':
1959            # do nothing and let the master take care of it
1960            return
1961
1962        try:
1963            # self is always the master and peer the slave
1964            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1965                         peer.simobj.getCCObject(), peer.name, peer.index)
1966        except:
1967            print("Error connecting port %s.%s to %s.%s" %
1968                  (self.simobj.path(), self.name,
1969                   peer.simobj.path(), peer.name))
1970            raise
1971        self.ccConnected = True
1972        peer.ccConnected = True
1973
1974# A reference to an individual element of a VectorPort... much like a
1975# PortRef, but has an index.
1976class VectorPortElementRef(PortRef):
1977    def __init__(self, simobj, name, role, index):
1978        PortRef.__init__(self, simobj, name, role)
1979        self.index = index
1980
1981    def __str__(self):
1982        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1983
1984# A reference to a complete vector-valued port (not just a single element).
1985# Can be indexed to retrieve individual VectorPortElementRef instances.
1986class VectorPortRef(object):
1987    def __init__(self, simobj, name, role):
1988        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1989        self.simobj = simobj
1990        self.name = name
1991        self.role = role
1992        self.elements = []
1993
1994    def __str__(self):
1995        return '%s.%s[:]' % (self.simobj, self.name)
1996
1997    def __len__(self):
1998        # Return the number of connected peers, corresponding the the
1999        # length of the elements.
2000        return len(self.elements)
2001
2002    # for config.ini, print peer's name (not ours)
2003    def ini_str(self):
2004        return ' '.join([el.ini_str() for el in self.elements])
2005
2006    # for config.json
2007    def get_config_as_dict(self):
2008        return {'role' : self.role,
2009                'peer' : [el.ini_str() for el in self.elements]}
2010
2011    def __getitem__(self, key):
2012        if not isinstance(key, int):
2013            raise TypeError("VectorPort index must be integer")
2014        if key >= len(self.elements):
2015            # need to extend list
2016            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
2017                   for i in range(len(self.elements), key+1)]
2018            self.elements.extend(ext)
2019        return self.elements[key]
2020
2021    def _get_next(self):
2022        return self[len(self.elements)]
2023
2024    def __setitem__(self, key, value):
2025        if not isinstance(key, int):
2026            raise TypeError("VectorPort index must be integer")
2027        self[key].connect(value)
2028
2029    def connect(self, other):
2030        if isinstance(other, (list, tuple)):
2031            # Assign list of port refs to vector port.
2032            # For now, append them... not sure if that's the right semantics
2033            # or if it should replace the current vector.
2034            for ref in other:
2035                self._get_next().connect(ref)
2036        else:
2037            # scalar assignment to plain VectorPort is implicit append
2038            self._get_next().connect(other)
2039
2040    def clone(self, simobj, memo):
2041        if self in memo:
2042            return memo[self]
2043        newRef = copy.copy(self)
2044        memo[self] = newRef
2045        newRef.simobj = simobj
2046        assert(isSimObject(newRef.simobj))
2047        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
2048        return newRef
2049
2050    def unproxy(self, simobj):
2051        [el.unproxy(simobj) for el in self.elements]
2052
2053    def ccConnect(self):
2054        [el.ccConnect() for el in self.elements]
2055
2056# Port description object.  Like a ParamDesc object, this represents a
2057# logical port in the SimObject class, not a particular port on a
2058# SimObject instance.  The latter are represented by PortRef objects.
2059class Port(object):
2060    # Generate a PortRef for this port on the given SimObject with the
2061    # given name
2062    def makeRef(self, simobj):
2063        return PortRef(simobj, self.name, self.role)
2064
2065    # Connect an instance of this port (on the given SimObject with
2066    # the given name) with the port described by the supplied PortRef
2067    def connect(self, simobj, ref):
2068        self.makeRef(simobj).connect(ref)
2069
2070    # No need for any pre-declarations at the moment as we merely rely
2071    # on an unsigned int.
2072    def cxx_predecls(self, code):
2073        pass
2074
2075    def pybind_predecls(self, code):
2076        cls.cxx_predecls(self, code)
2077
2078    # Declare an unsigned int with the same name as the port, that
2079    # will eventually hold the number of connected ports (and thus the
2080    # number of elements for a VectorPort).
2081    def cxx_decl(self, code):
2082        code('unsigned int port_${{self.name}}_connection_count;')
2083
2084class MasterPort(Port):
2085    # MasterPort("description")
2086    def __init__(self, *args):
2087        if len(args) == 1:
2088            self.desc = args[0]
2089            self.role = 'MASTER'
2090        else:
2091            raise TypeError('wrong number of arguments')
2092
2093class SlavePort(Port):
2094    # SlavePort("description")
2095    def __init__(self, *args):
2096        if len(args) == 1:
2097            self.desc = args[0]
2098            self.role = 'SLAVE'
2099        else:
2100            raise TypeError('wrong number of arguments')
2101
2102# VectorPort description object.  Like Port, but represents a vector
2103# of connections (e.g., as on a XBar).
2104class VectorPort(Port):
2105    def __init__(self, *args):
2106        self.isVec = True
2107
2108    def makeRef(self, simobj):
2109        return VectorPortRef(simobj, self.name, self.role)
2110
2111class VectorMasterPort(VectorPort):
2112    # VectorMasterPort("description")
2113    def __init__(self, *args):
2114        if len(args) == 1:
2115            self.desc = args[0]
2116            self.role = 'MASTER'
2117            VectorPort.__init__(self, *args)
2118        else:
2119            raise TypeError('wrong number of arguments')
2120
2121class VectorSlavePort(VectorPort):
2122    # VectorSlavePort("description")
2123    def __init__(self, *args):
2124        if len(args) == 1:
2125            self.desc = args[0]
2126            self.role = 'SLAVE'
2127            VectorPort.__init__(self, *args)
2128        else:
2129            raise TypeError('wrong number of arguments')
2130
2131# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2132# proxy objects (via set_param_desc()) so that proxy error messages
2133# make sense.
2134class PortParamDesc(object):
2135    __metaclass__ = Singleton
2136
2137    ptype_str = 'Port'
2138    ptype = Port
2139
2140baseEnums = allEnums.copy()
2141baseParams = allParams.copy()
2142
2143def clear():
2144    global allEnums, allParams
2145
2146    allEnums = baseEnums.copy()
2147    allParams = baseParams.copy()
2148
2149__all__ = ['Param', 'VectorParam',
2150           'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
2151           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2152           'Int32', 'UInt32', 'Int64', 'UInt64',
2153           'Counter', 'Addr', 'Tick', 'Percent',
2154           'TcpPort', 'UdpPort', 'EthernetAddr',
2155           'IpAddress', 'IpNetmask', 'IpWithPort',
2156           'MemorySize', 'MemorySize32',
2157           'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2158           'NetworkBandwidth', 'MemoryBandwidth',
2159           'AddrRange',
2160           'MaxAddr', 'MaxTick', 'AllMemory',
2161           'Time',
2162           'NextEthernetAddr', 'NULL',
2163           'MasterPort', 'SlavePort',
2164           'VectorMasterPort', 'VectorSlavePort']
2165