params.py revision 10364
12SN/A# Copyright (c) 2012-2014 ARM Limited
21762SN/A# All rights reserved.
37534Ssteve.reinhardt@amd.com#
42SN/A# The license below extends only to copyright in the software and shall
52SN/A# not be construed as granting a license to any other intellectual
62SN/A# property including but not limited to intellectual property relating
72SN/A# to a hardware implementation of the functionality of the software
82SN/A# licensed hereunder.  You may use the software subject to the license
92SN/A# terms below provided that you ensure that this notice is replicated
102SN/A# unmodified and in its entirety in all distributions of the software,
112SN/A# modified or unmodified, in source code or in binary form.
122SN/A#
132SN/A# Copyright (c) 2004-2006 The Regents of The University of Michigan
142SN/A# Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
152SN/A# All rights reserved.
162SN/A#
172SN/A# Redistribution and use in source and binary forms, with or without
182SN/A# modification, are permitted provided that the following conditions are
192SN/A# met: redistributions of source code must retain the above copyright
202SN/A# notice, this list of conditions and the following disclaimer;
212SN/A# redistributions in binary form must reproduce the above copyright
222SN/A# notice, this list of conditions and the following disclaimer in the
232SN/A# documentation and/or other materials provided with the distribution;
242SN/A# neither the name of the copyright holders nor the names of its
252SN/A# contributors may be used to endorse or promote products derived from
262SN/A# this software without specific prior written permission.
272SN/A#
282665Ssaidi@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
292665Ssaidi@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
302665Ssaidi@eecs.umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
312SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
322SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
332SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
342SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
352SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
362SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
372SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
382SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
392SN/A#
405491Sgblack@eecs.umich.edu# Authors: Steve Reinhardt
415491Sgblack@eecs.umich.edu#          Nathan Binkert
422SN/A#          Gabe Black
435491Sgblack@eecs.umich.edu#          Andreas Hansson
442SN/A
452SN/A#####################################################################
468737Skoansin.tan@gmail.com#
474762Snate@binkert.org# Parameter description classes
485605Snate@binkert.org#
4956SN/A# The _params dictionary in each class maps parameter names to either
502SN/A# a Param or a VectorParam object.  These objects contain the
512797Sktlim@umich.edu# parameter description string, the parameter type, and the default
522797Sktlim@umich.edu# value (if any).  The convert() method on these objects is used to
532609SN/A# force whatever value is assigned to the parameter to the appropriate
549196SAndreas.Sandberg@arm.com# type.
552SN/A#
562SN/A# Note that the default values are loaded into the class's attribute
572SN/A# space when the parameter dictionary is initialized (in
589196SAndreas.Sandberg@arm.com# MetaSimObject._new_param()); after that point they aren't used.
599196SAndreas.Sandberg@arm.com#
609196SAndreas.Sandberg@arm.com#####################################################################
619196SAndreas.Sandberg@arm.com
629196SAndreas.Sandberg@arm.comimport copy
639196SAndreas.Sandberg@arm.comimport datetime
649196SAndreas.Sandberg@arm.comimport re
659196SAndreas.Sandberg@arm.comimport sys
669196SAndreas.Sandberg@arm.comimport time
679196SAndreas.Sandberg@arm.comimport math
689196SAndreas.Sandberg@arm.com
699196SAndreas.Sandberg@arm.comimport proxy
709196SAndreas.Sandberg@arm.comimport ticks
719196SAndreas.Sandberg@arm.comfrom util import *
729196SAndreas.Sandberg@arm.com
739196SAndreas.Sandberg@arm.comdef isSimObject(*args, **kwargs):
749196SAndreas.Sandberg@arm.com    return SimObject.isSimObject(*args, **kwargs)
759196SAndreas.Sandberg@arm.com
769196SAndreas.Sandberg@arm.comdef isSimObjectSequence(*args, **kwargs):
779196SAndreas.Sandberg@arm.com    return SimObject.isSimObjectSequence(*args, **kwargs)
789196SAndreas.Sandberg@arm.com
799196SAndreas.Sandberg@arm.comdef isSimObjectClass(*args, **kwargs):
809196SAndreas.Sandberg@arm.com    return SimObject.isSimObjectClass(*args, **kwargs)
819196SAndreas.Sandberg@arm.com
829196SAndreas.Sandberg@arm.comallParams = {}
839196SAndreas.Sandberg@arm.com
849196SAndreas.Sandberg@arm.comclass MetaParamValue(type):
859196SAndreas.Sandberg@arm.com    def __new__(mcls, name, bases, dct):
869196SAndreas.Sandberg@arm.com        cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
879196SAndreas.Sandberg@arm.com        assert name not in allParams
889196SAndreas.Sandberg@arm.com        allParams[name] = cls
899196SAndreas.Sandberg@arm.com        return cls
909196SAndreas.Sandberg@arm.com
919196SAndreas.Sandberg@arm.com
929196SAndreas.Sandberg@arm.com# Dummy base class to identify types that are legitimate for SimObject
939196SAndreas.Sandberg@arm.com# parameters.
949196SAndreas.Sandberg@arm.comclass ParamValue(object):
959196SAndreas.Sandberg@arm.com    __metaclass__ = MetaParamValue
969196SAndreas.Sandberg@arm.com    cmd_line_settable = False
979196SAndreas.Sandberg@arm.com
989196SAndreas.Sandberg@arm.com    # Generate the code needed as a prerequisite for declaring a C++
999196SAndreas.Sandberg@arm.com    # object of this type.  Typically generates one or more #include
1009196SAndreas.Sandberg@arm.com    # statements.  Used when declaring parameters of this type.
1019196SAndreas.Sandberg@arm.com    @classmethod
1029196SAndreas.Sandberg@arm.com    def cxx_predecls(cls, code):
1039196SAndreas.Sandberg@arm.com        pass
1049196SAndreas.Sandberg@arm.com
1059196SAndreas.Sandberg@arm.com    # Generate the code needed as a prerequisite for including a
1069196SAndreas.Sandberg@arm.com    # reference to a C++ object of this type in a SWIG .i file.
1079196SAndreas.Sandberg@arm.com    # Typically generates one or more %import or %include statements.
1089196SAndreas.Sandberg@arm.com    @classmethod
1099196SAndreas.Sandberg@arm.com    def swig_predecls(cls, code):
1109196SAndreas.Sandberg@arm.com        pass
1119196SAndreas.Sandberg@arm.com
1129196SAndreas.Sandberg@arm.com    # default for printing to .ini file is regular string conversion.
1139196SAndreas.Sandberg@arm.com    # will be overridden in some cases
1149196SAndreas.Sandberg@arm.com    def ini_str(self):
1159196SAndreas.Sandberg@arm.com        return str(self)
1162SN/A
1177492Ssteve.reinhardt@amd.com    # allows us to blithely call unproxy() on things without checking
1182SN/A    # if they're really proxies or not
1191553SN/A    def unproxy(self, base):
1209196SAndreas.Sandberg@arm.com        return self
1219196SAndreas.Sandberg@arm.com
1229196SAndreas.Sandberg@arm.com    # Produce a human readable version of the stored value
1239196SAndreas.Sandberg@arm.com    def pretty_print(self, value):
1249196SAndreas.Sandberg@arm.com        return str(value)
1259196SAndreas.Sandberg@arm.com
1269196SAndreas.Sandberg@arm.com# Regular parameter description.
1279196SAndreas.Sandberg@arm.comclass ParamDesc(object):
1289196SAndreas.Sandberg@arm.com    def __init__(self, ptype_str, ptype, *args, **kwargs):
1299196SAndreas.Sandberg@arm.com        self.ptype_str = ptype_str
1309196SAndreas.Sandberg@arm.com        # remember ptype only if it is provided
1319196SAndreas.Sandberg@arm.com        if ptype != None:
1329196SAndreas.Sandberg@arm.com            self.ptype = ptype
1339196SAndreas.Sandberg@arm.com
1349196SAndreas.Sandberg@arm.com        if args:
1359196SAndreas.Sandberg@arm.com            if len(args) == 1:
1369196SAndreas.Sandberg@arm.com                self.desc = args[0]
1379196SAndreas.Sandberg@arm.com            elif len(args) == 2:
1382797Sktlim@umich.edu                self.default = args[0]
1399196SAndreas.Sandberg@arm.com                self.desc = args[1]
1409196SAndreas.Sandberg@arm.com            else:
1419196SAndreas.Sandberg@arm.com                raise TypeError, 'too many arguments'
1422797Sktlim@umich.edu
1433202Shsul@eecs.umich.edu        if kwargs.has_key('desc'):
1442901Ssaidi@eecs.umich.edu            assert(not hasattr(self, 'desc'))
1452901Ssaidi@eecs.umich.edu            self.desc = kwargs['desc']
1462797Sktlim@umich.edu            del kwargs['desc']
147265SN/A
1482797Sktlim@umich.edu        if kwargs.has_key('default'):
1491553SN/A            assert(not hasattr(self, 'default'))
1501553SN/A            self.default = kwargs['default']
1512797Sktlim@umich.edu            del kwargs['default']
1522797Sktlim@umich.edu
1532SN/A        if kwargs:
1542SN/A            raise TypeError, 'extra unknown kwargs %s' % kwargs
1552SN/A
1569196SAndreas.Sandberg@arm.com        if not hasattr(self, 'desc'):
1572SN/A            raise TypeError, 'desc attribute missing'
1582SN/A
1594762Snate@binkert.org    def __getattr__(self, attr):
1609196SAndreas.Sandberg@arm.com        if attr == 'ptype':
1614762Snate@binkert.org            ptype = SimObject.allClasses[self.ptype_str]
1624762Snate@binkert.org            assert isSimObjectClass(ptype)
1632SN/A            self.ptype = ptype
1644762Snate@binkert.org            return ptype
1654762Snate@binkert.org
1664762Snate@binkert.org        raise AttributeError, "'%s' object has no attribute '%s'" % \
1672SN/A              (type(self).__name__, attr)
1682SN/A
1695034Smilesck@eecs.umich.edu    def example_str(self):
1705034Smilesck@eecs.umich.edu        if hasattr(self.ptype, "ex_str"):
1711553SN/A            return self.ptype.ex_str
172265SN/A        else:
1737532Ssteve.reinhardt@amd.com            return self.ptype_str
1747532Ssteve.reinhardt@amd.com
1757532Ssteve.reinhardt@amd.com    # Is the param available to be exposed on the command line
1767532Ssteve.reinhardt@amd.com    def isCmdLineSettable(self):
1777532Ssteve.reinhardt@amd.com        if hasattr(self.ptype, "cmd_line_settable"):
1787532Ssteve.reinhardt@amd.com            return self.ptype.cmd_line_settable
179465SN/A        else:
180465SN/A            return False
1817532Ssteve.reinhardt@amd.com
1827532Ssteve.reinhardt@amd.com    def convert(self, value):
1837532Ssteve.reinhardt@amd.com        if isinstance(value, proxy.BaseProxy):
1847532Ssteve.reinhardt@amd.com            value.set_param_desc(self)
1857532Ssteve.reinhardt@amd.com            return value
1867532Ssteve.reinhardt@amd.com        if not hasattr(self, 'ptype') and isNullPointer(value):
1877532Ssteve.reinhardt@amd.com            # deferred evaluation of SimObject; continue to defer if
1887532Ssteve.reinhardt@amd.com            # we're just assigning a null pointer
1899196SAndreas.Sandberg@arm.com            return value
1909196SAndreas.Sandberg@arm.com        if isinstance(value, self.ptype):
1917532Ssteve.reinhardt@amd.com            return value
1927532Ssteve.reinhardt@amd.com        if isNullPointer(value) and isSimObjectClass(self.ptype):
1937532Ssteve.reinhardt@amd.com            return value
1947532Ssteve.reinhardt@amd.com        return self.ptype(value)
1957532Ssteve.reinhardt@amd.com
1967532Ssteve.reinhardt@amd.com    def pretty_print(self, value):
1977532Ssteve.reinhardt@amd.com        if isinstance(value, proxy.BaseProxy):
1987532Ssteve.reinhardt@amd.com           return str(value)
1997532Ssteve.reinhardt@amd.com        if isNullPointer(value):
2007532Ssteve.reinhardt@amd.com           return NULL
2019196SAndreas.Sandberg@arm.com        return self.ptype(value).pretty_print(value)
2029196SAndreas.Sandberg@arm.com
2039196SAndreas.Sandberg@arm.com    def cxx_predecls(self, code):
2042SN/A        code('#include <cstddef>')
2059196SAndreas.Sandberg@arm.com        self.ptype.cxx_predecls(code)
2069196SAndreas.Sandberg@arm.com
2079196SAndreas.Sandberg@arm.com    def swig_predecls(self, code):
2089196SAndreas.Sandberg@arm.com        self.ptype.swig_predecls(code)
209330SN/A
2102SN/A    def cxx_decl(self, code):
2117532Ssteve.reinhardt@amd.com        code('${{self.ptype.cxx_type}} ${{self.name}};')
2127532Ssteve.reinhardt@amd.com
2137532Ssteve.reinhardt@amd.com# Vector-valued parameter description.  Just like ParamDesc, except
2147823Ssteve.reinhardt@amd.com# that the value is a vector (list) of the specified type instead of a
2157532Ssteve.reinhardt@amd.com# single value.
2167532Ssteve.reinhardt@amd.com
2177492Ssteve.reinhardt@amd.comclass VectorParamValue(list):
218330SN/A    __metaclass__ = MetaParamValue
2199196SAndreas.Sandberg@arm.com    def __setattr__(self, attr, value):
2209196SAndreas.Sandberg@arm.com        raise AttributeError, \
2219196SAndreas.Sandberg@arm.com              "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
2229196SAndreas.Sandberg@arm.com
223938SN/A    def ini_str(self):
2249196SAndreas.Sandberg@arm.com        return ' '.join([v.ini_str() for v in self])
2259196SAndreas.Sandberg@arm.com
2269196SAndreas.Sandberg@arm.com    def getValue(self):
2279196SAndreas.Sandberg@arm.com        return [ v.getValue() for v in self ]
2289196SAndreas.Sandberg@arm.com
2299196SAndreas.Sandberg@arm.com    def unproxy(self, base):
2309196SAndreas.Sandberg@arm.com        if len(self) == 1 and isinstance(self[0], proxy.AllProxy):
2319196SAndreas.Sandberg@arm.com            return self[0].unproxy(base)
2329196SAndreas.Sandberg@arm.com        else:
2339196SAndreas.Sandberg@arm.com             return [v.unproxy(base) for v in self]
2349196SAndreas.Sandberg@arm.com
2359196SAndreas.Sandberg@arm.comclass SimObjectVector(VectorParamValue):
2369196SAndreas.Sandberg@arm.com    # support clone operation
2379196SAndreas.Sandberg@arm.com    def __call__(self, **kwargs):
2389196SAndreas.Sandberg@arm.com        return SimObjectVector([v(**kwargs) for v in self])
2399196SAndreas.Sandberg@arm.com
2409196SAndreas.Sandberg@arm.com    def clear_parent(self, old_parent):
2419196SAndreas.Sandberg@arm.com        for v in self:
2429196SAndreas.Sandberg@arm.com            v.clear_parent(old_parent)
2439196SAndreas.Sandberg@arm.com
2449196SAndreas.Sandberg@arm.com    def set_parent(self, parent, name):
2459196SAndreas.Sandberg@arm.com        if len(self) == 1:
2469196SAndreas.Sandberg@arm.com            self[0].set_parent(parent, name)
2479196SAndreas.Sandberg@arm.com        else:
2489196SAndreas.Sandberg@arm.com            width = int(math.ceil(math.log(len(self))/math.log(10)))
2499196SAndreas.Sandberg@arm.com            for i,v in enumerate(self):
2509196SAndreas.Sandberg@arm.com                v.set_parent(parent, "%s%0*d" % (name, width, i))
2512901Ssaidi@eecs.umich.edu
2529196SAndreas.Sandberg@arm.com    def has_parent(self):
2539196SAndreas.Sandberg@arm.com        return reduce(lambda x,y: x and y, [v.has_parent() for v in self])
2549196SAndreas.Sandberg@arm.com
2559196SAndreas.Sandberg@arm.com    # return 'cpu0 cpu1' etc. for print_ini()
2562797Sktlim@umich.edu    def get_name(self):
2579196SAndreas.Sandberg@arm.com        return ' '.join([v._name for v in self])
2589196SAndreas.Sandberg@arm.com
2599196SAndreas.Sandberg@arm.com    # By iterating through the constituent members of the vector here
2609196SAndreas.Sandberg@arm.com    # we can nicely handle iterating over all a SimObject's children
2619196SAndreas.Sandberg@arm.com    # without having to provide lots of special functions on
2629196SAndreas.Sandberg@arm.com    # SimObjectVector directly.
2638737Skoansin.tan@gmail.com    def descendants(self):
2649196SAndreas.Sandberg@arm.com        for v in self:
2659196SAndreas.Sandberg@arm.com            for obj in v.descendants():
2669196SAndreas.Sandberg@arm.com                yield obj
2679196SAndreas.Sandberg@arm.com
2689196SAndreas.Sandberg@arm.com    def get_config_as_dict(self):
2699196SAndreas.Sandberg@arm.com        a = []
2709196SAndreas.Sandberg@arm.com        for v in self:
2712797Sktlim@umich.edu            a.append(v.get_config_as_dict())
2729196SAndreas.Sandberg@arm.com        return a
2739196SAndreas.Sandberg@arm.com
2749196SAndreas.Sandberg@arm.com    # If we are replacing an item in the vector, make sure to set the
2759196SAndreas.Sandberg@arm.com    # parent reference of the new SimObject to be the same as the parent
2769196SAndreas.Sandberg@arm.com    # of the SimObject being replaced. Useful to have if we created
2779196SAndreas.Sandberg@arm.com    # a SimObjectVector of temporary objects that will be modified later in
2789196SAndreas.Sandberg@arm.com    # configuration scripts.
2799196SAndreas.Sandberg@arm.com    def __setitem__(self, key, value):
2809196SAndreas.Sandberg@arm.com        val = self[key]
2819196SAndreas.Sandberg@arm.com        if value.has_parent():
2829196SAndreas.Sandberg@arm.com            warn("SimObject %s already has a parent" % value.get_name() +\
2839196SAndreas.Sandberg@arm.com                 " that is being overwritten by a SimObjectVector")
2849196SAndreas.Sandberg@arm.com        value.set_parent(val.get_parent(), val._name)
2859196SAndreas.Sandberg@arm.com        super(SimObjectVector, self).__setitem__(key, value)
2862797Sktlim@umich.edu
2872609SN/A    # Enumerate the params of each member of the SimObject vector. Creates
2881031SN/A    # strings that will allow indexing into the vector by the python code and
2891031SN/A    # allow it to be specified on the command line.
2901031SN/A    def enumerateParams(self, flags_dict = {},
2911031SN/A                        cmd_line_str = "",
2921031SN/A                        access_str = ""):
2931031SN/A        if hasattr(self, "_paramEnumed"):
2945314Sstever@gmail.com            print "Cycle detected enumerating params at %s?!" % (cmd_line_str)
2955314Sstever@gmail.com        else:
2965315Sstever@gmail.com            x = 0
2975314Sstever@gmail.com            for vals in self:
2985314Sstever@gmail.com                # Each entry in the SimObjectVector should be an
2995314Sstever@gmail.com                # instance of a SimObject
3002SN/A                flags_dict = vals.enumerateParams(flags_dict,
3012SN/A                                                  cmd_line_str + "%d." % x,
3022SN/A                                                  access_str + "[%d]." % x)
303                x = x + 1
304
305        return flags_dict
306
307class VectorParamDesc(ParamDesc):
308    # Convert assigned value to appropriate type.  If the RHS is not a
309    # list or tuple, it generates a single-element list.
310    def convert(self, value):
311        if isinstance(value, (list, tuple)):
312            # list: coerce each element into new list
313            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
314        elif isinstance(value, str):
315            # If input is a csv string
316            tmp_list = [ ParamDesc.convert(self, v) \
317                         for v in value.strip('[').strip(']').split(',') ]
318        else:
319            # singleton: coerce to a single-element list
320            tmp_list = [ ParamDesc.convert(self, value) ]
321
322        if isSimObjectSequence(tmp_list):
323            return SimObjectVector(tmp_list)
324        else:
325            return VectorParamValue(tmp_list)
326
327    # Produce a human readable example string that describes
328    # how to set this vector parameter in the absence of a default
329    # value.
330    def example_str(self):
331        s = super(VectorParamDesc, self).example_str()
332        help_str = "[" + s + "," + s + ", ...]"
333        return help_str
334
335    # Produce a human readable representation of the value of this vector param.
336    def pretty_print(self, value):
337        if isinstance(value, (list, tuple)):
338            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ]
339        elif isinstance(value, str):
340            tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ]
341        else:
342            tmp_list = [ ParamDesc.pretty_print(self, value) ]
343
344        return tmp_list
345
346    # This is a helper function for the new config system
347    def __call__(self, value):
348        if isinstance(value, (list, tuple)):
349            # list: coerce each element into new list
350            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
351        elif isinstance(value, str):
352            # If input is a csv string
353            tmp_list = [ ParamDesc.convert(self, v) \
354                         for v in value.strip('[').strip(']').split(',') ]
355        else:
356            # singleton: coerce to a single-element list
357            tmp_list = [ ParamDesc.convert(self, value) ]
358
359        return VectorParamValue(tmp_list)
360
361    def swig_module_name(self):
362        return "%s_vector" % self.ptype_str
363
364    def swig_predecls(self, code):
365        code('%import "${{self.swig_module_name()}}.i"')
366
367    def swig_decl(self, code):
368        code('%module(package="m5.internal") ${{self.swig_module_name()}}')
369        code('%{')
370        self.ptype.cxx_predecls(code)
371        code('%}')
372        code()
373        # Make sure the SWIGPY_SLICE_ARG is defined through this inclusion
374        code('%include "std_container.i"')
375        code()
376        self.ptype.swig_predecls(code)
377        code()
378        code('%include "std_vector.i"')
379        code()
380
381        ptype = self.ptype_str
382        cxx_type = self.ptype.cxx_type
383
384        code('%template(vector_$ptype) std::vector< $cxx_type >;')
385
386    def cxx_predecls(self, code):
387        code('#include <vector>')
388        self.ptype.cxx_predecls(code)
389
390    def cxx_decl(self, code):
391        code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
392
393class ParamFactory(object):
394    def __init__(self, param_desc_class, ptype_str = None):
395        self.param_desc_class = param_desc_class
396        self.ptype_str = ptype_str
397
398    def __getattr__(self, attr):
399        if self.ptype_str:
400            attr = self.ptype_str + '.' + attr
401        return ParamFactory(self.param_desc_class, attr)
402
403    # E.g., Param.Int(5, "number of widgets")
404    def __call__(self, *args, **kwargs):
405        ptype = None
406        try:
407            ptype = allParams[self.ptype_str]
408        except KeyError:
409            # if name isn't defined yet, assume it's a SimObject, and
410            # try to resolve it later
411            pass
412        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
413
414Param = ParamFactory(ParamDesc)
415VectorParam = ParamFactory(VectorParamDesc)
416
417#####################################################################
418#
419# Parameter Types
420#
421# Though native Python types could be used to specify parameter types
422# (the 'ptype' field of the Param and VectorParam classes), it's more
423# flexible to define our own set of types.  This gives us more control
424# over how Python expressions are converted to values (via the
425# __init__() constructor) and how these values are printed out (via
426# the __str__() conversion method).
427#
428#####################################################################
429
430# String-valued parameter.  Just mixin the ParamValue class with the
431# built-in str class.
432class String(ParamValue,str):
433    cxx_type = 'std::string'
434    cmd_line_settable = True
435
436    @classmethod
437    def cxx_predecls(self, code):
438        code('#include <string>')
439
440    @classmethod
441    def swig_predecls(cls, code):
442        code('%include "std_string.i"')
443
444    def __call__(self, value):
445        self = value
446        return value
447
448    def getValue(self):
449        return self
450
451# superclass for "numeric" parameter values, to emulate math
452# operations in a type-safe way.  e.g., a Latency times an int returns
453# a new Latency object.
454class NumericParamValue(ParamValue):
455    def __str__(self):
456        return str(self.value)
457
458    def __float__(self):
459        return float(self.value)
460
461    def __long__(self):
462        return long(self.value)
463
464    def __int__(self):
465        return int(self.value)
466
467    # hook for bounds checking
468    def _check(self):
469        return
470
471    def __mul__(self, other):
472        newobj = self.__class__(self)
473        newobj.value *= other
474        newobj._check()
475        return newobj
476
477    __rmul__ = __mul__
478
479    def __div__(self, other):
480        newobj = self.__class__(self)
481        newobj.value /= other
482        newobj._check()
483        return newobj
484
485    def __sub__(self, other):
486        newobj = self.__class__(self)
487        newobj.value -= other
488        newobj._check()
489        return newobj
490
491# Metaclass for bounds-checked integer parameters.  See CheckedInt.
492class CheckedIntType(MetaParamValue):
493    def __init__(cls, name, bases, dict):
494        super(CheckedIntType, cls).__init__(name, bases, dict)
495
496        # CheckedInt is an abstract base class, so we actually don't
497        # want to do any processing on it... the rest of this code is
498        # just for classes that derive from CheckedInt.
499        if name == 'CheckedInt':
500            return
501
502        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
503            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
504                panic("CheckedInt subclass %s must define either\n" \
505                      "    'min' and 'max' or 'size' and 'unsigned'\n",
506                      name);
507            if cls.unsigned:
508                cls.min = 0
509                cls.max = 2 ** cls.size - 1
510            else:
511                cls.min = -(2 ** (cls.size - 1))
512                cls.max = (2 ** (cls.size - 1)) - 1
513
514# Abstract superclass for bounds-checked integer parameters.  This
515# class is subclassed to generate parameter classes with specific
516# bounds.  Initialization of the min and max bounds is done in the
517# metaclass CheckedIntType.__init__.
518class CheckedInt(NumericParamValue):
519    __metaclass__ = CheckedIntType
520    cmd_line_settable = True
521
522    def _check(self):
523        if not self.min <= self.value <= self.max:
524            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
525                  (self.min, self.value, self.max)
526
527    def __init__(self, value):
528        if isinstance(value, str):
529            self.value = convert.toInteger(value)
530        elif isinstance(value, (int, long, float, NumericParamValue)):
531            self.value = long(value)
532        else:
533            raise TypeError, "Can't convert object of type %s to CheckedInt" \
534                  % type(value).__name__
535        self._check()
536
537    def __call__(self, value):
538        self.__init__(value)
539        return value
540
541    @classmethod
542    def cxx_predecls(cls, code):
543        # most derived types require this, so we just do it here once
544        code('#include "base/types.hh"')
545
546    @classmethod
547    def swig_predecls(cls, code):
548        # most derived types require this, so we just do it here once
549        code('%import "stdint.i"')
550        code('%import "base/types.hh"')
551
552    def getValue(self):
553        return long(self.value)
554
555class Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
556class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
557
558class Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
559class UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
560class Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
561class UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
562class Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
563class UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
564class Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
565class UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
566
567class Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
568class Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
569class TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
570class UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
571
572class Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
573
574class Cycles(CheckedInt):
575    cxx_type = 'Cycles'
576    size = 64
577    unsigned = True
578
579    def getValue(self):
580        from m5.internal.core import Cycles
581        return Cycles(self.value)
582
583class Float(ParamValue, float):
584    cxx_type = 'double'
585    cmdLineSettable = True
586
587    def __init__(self, value):
588        if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
589            self.value = float(value)
590        else:
591            raise TypeError, "Can't convert object of type %s to Float" \
592                  % type(value).__name__
593
594    def __call__(self, value):
595        self.__init__(value)
596        return value
597
598    def getValue(self):
599        return float(self.value)
600
601class MemorySize(CheckedInt):
602    cxx_type = 'uint64_t'
603    ex_str = '512MB'
604    size = 64
605    unsigned = True
606    def __init__(self, value):
607        if isinstance(value, MemorySize):
608            self.value = value.value
609        else:
610            self.value = convert.toMemorySize(value)
611        self._check()
612
613class MemorySize32(CheckedInt):
614    cxx_type = 'uint32_t'
615    ex_str = '512MB'
616    size = 32
617    unsigned = True
618    def __init__(self, value):
619        if isinstance(value, MemorySize):
620            self.value = value.value
621        else:
622            self.value = convert.toMemorySize(value)
623        self._check()
624
625class Addr(CheckedInt):
626    cxx_type = 'Addr'
627    size = 64
628    unsigned = True
629    def __init__(self, value):
630        if isinstance(value, Addr):
631            self.value = value.value
632        else:
633            try:
634                # Often addresses are referred to with sizes. Ex: A device
635                # base address is at "512MB".  Use toMemorySize() to convert
636                # these into addresses. If the address is not specified with a
637                # "size", an exception will occur and numeric translation will
638                # proceed below.
639                self.value = convert.toMemorySize(value)
640            except (TypeError, ValueError):
641                # Convert number to string and use long() to do automatic
642                # base conversion (requires base=0 for auto-conversion)
643                self.value = long(str(value), base=0)
644
645        self._check()
646    def __add__(self, other):
647        if isinstance(other, Addr):
648            return self.value + other.value
649        else:
650            return self.value + other
651    def pretty_print(self, value):
652        try:
653            val = convert.toMemorySize(value)
654        except TypeError:
655            val = long(value)
656        return "0x%x" % long(val)
657
658class AddrRange(ParamValue):
659    cxx_type = 'AddrRange'
660
661    def __init__(self, *args, **kwargs):
662        # Disable interleaving by default
663        self.intlvHighBit = 0
664        self.intlvBits = 0
665        self.intlvMatch = 0
666
667        def handle_kwargs(self, kwargs):
668            # An address range needs to have an upper limit, specified
669            # either explicitly with an end, or as an offset using the
670            # size keyword.
671            if 'end' in kwargs:
672                self.end = Addr(kwargs.pop('end'))
673            elif 'size' in kwargs:
674                self.end = self.start + Addr(kwargs.pop('size')) - 1
675            else:
676                raise TypeError, "Either end or size must be specified"
677
678            # Now on to the optional bit
679            if 'intlvHighBit' in kwargs:
680                self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
681            if 'intlvBits' in kwargs:
682                self.intlvBits = int(kwargs.pop('intlvBits'))
683            if 'intlvMatch' in kwargs:
684                self.intlvMatch = int(kwargs.pop('intlvMatch'))
685
686        if len(args) == 0:
687            self.start = Addr(kwargs.pop('start'))
688            handle_kwargs(self, kwargs)
689
690        elif len(args) == 1:
691            if kwargs:
692                self.start = Addr(args[0])
693                handle_kwargs(self, kwargs)
694            elif isinstance(args[0], (list, tuple)):
695                self.start = Addr(args[0][0])
696                self.end = Addr(args[0][1])
697            else:
698                self.start = Addr(0)
699                self.end = Addr(args[0]) - 1
700
701        elif len(args) == 2:
702            self.start = Addr(args[0])
703            self.end = Addr(args[1])
704        else:
705            raise TypeError, "Too many arguments specified"
706
707        if kwargs:
708            raise TypeError, "Too many keywords: %s" % kwargs.keys()
709
710    def __str__(self):
711        return '%s:%s' % (self.start, self.end)
712
713    def size(self):
714        # Divide the size by the size of the interleaving slice
715        return (long(self.end) - long(self.start) + 1) >> self.intlvBits
716
717    @classmethod
718    def cxx_predecls(cls, code):
719        Addr.cxx_predecls(code)
720        code('#include "base/addr_range.hh"')
721
722    @classmethod
723    def swig_predecls(cls, code):
724        Addr.swig_predecls(code)
725
726    def getValue(self):
727        # Go from the Python class to the wrapped C++ class generated
728        # by swig
729        from m5.internal.range import AddrRange
730
731        return AddrRange(long(self.start), long(self.end),
732                         int(self.intlvHighBit), int(self.intlvBits),
733                         int(self.intlvMatch))
734
735# Boolean parameter type.  Python doesn't let you subclass bool, since
736# it doesn't want to let you create multiple instances of True and
737# False.  Thus this is a little more complicated than String.
738class Bool(ParamValue):
739    cxx_type = 'bool'
740    cmd_line_settable = True
741
742    def __init__(self, value):
743        try:
744            self.value = convert.toBool(value)
745        except TypeError:
746            self.value = bool(value)
747
748    def __call__(self, value):
749        self.__init__(value)
750        return value
751
752    def getValue(self):
753        return bool(self.value)
754
755    def __str__(self):
756        return str(self.value)
757
758    # implement truth value testing for Bool parameters so that these params
759    # evaluate correctly during the python configuration phase
760    def __nonzero__(self):
761        return bool(self.value)
762
763    def ini_str(self):
764        if self.value:
765            return 'true'
766        return 'false'
767
768def IncEthernetAddr(addr, val = 1):
769    bytes = map(lambda x: int(x, 16), addr.split(':'))
770    bytes[5] += val
771    for i in (5, 4, 3, 2, 1):
772        val,rem = divmod(bytes[i], 256)
773        bytes[i] = rem
774        if val == 0:
775            break
776        bytes[i - 1] += val
777    assert(bytes[0] <= 255)
778    return ':'.join(map(lambda x: '%02x' % x, bytes))
779
780_NextEthernetAddr = "00:90:00:00:00:01"
781def NextEthernetAddr():
782    global _NextEthernetAddr
783
784    value = _NextEthernetAddr
785    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
786    return value
787
788class EthernetAddr(ParamValue):
789    cxx_type = 'Net::EthAddr'
790    ex_str = "00:90:00:00:00:01"
791    cmd_line_settable = True
792
793    @classmethod
794    def cxx_predecls(cls, code):
795        code('#include "base/inet.hh"')
796
797    @classmethod
798    def swig_predecls(cls, code):
799        code('%include "python/swig/inet.i"')
800
801    def __init__(self, value):
802        if value == NextEthernetAddr:
803            self.value = value
804            return
805
806        if not isinstance(value, str):
807            raise TypeError, "expected an ethernet address and didn't get one"
808
809        bytes = value.split(':')
810        if len(bytes) != 6:
811            raise TypeError, 'invalid ethernet address %s' % value
812
813        for byte in bytes:
814            if not 0 <= int(byte, base=16) <= 0xff:
815                raise TypeError, 'invalid ethernet address %s' % value
816
817        self.value = value
818
819    def __call__(self, value):
820        self.__init__(value)
821        return value
822
823    def unproxy(self, base):
824        if self.value == NextEthernetAddr:
825            return EthernetAddr(self.value())
826        return self
827
828    def getValue(self):
829        from m5.internal.params import EthAddr
830        return EthAddr(self.value)
831
832    def ini_str(self):
833        return self.value
834
835# When initializing an IpAddress, pass in an existing IpAddress, a string of
836# the form "a.b.c.d", or an integer representing an IP.
837class IpAddress(ParamValue):
838    cxx_type = 'Net::IpAddress'
839    ex_str = "127.0.0.1"
840    cmd_line_settable = True
841
842    @classmethod
843    def cxx_predecls(cls, code):
844        code('#include "base/inet.hh"')
845
846    @classmethod
847    def swig_predecls(cls, code):
848        code('%include "python/swig/inet.i"')
849
850    def __init__(self, value):
851        if isinstance(value, IpAddress):
852            self.ip = value.ip
853        else:
854            try:
855                self.ip = convert.toIpAddress(value)
856            except TypeError:
857                self.ip = long(value)
858        self.verifyIp()
859
860    def __call__(self, value):
861        self.__init__(value)
862        return value
863
864    def __str__(self):
865        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
866        return '%d.%d.%d.%d' % tuple(tup)
867
868    def __eq__(self, other):
869        if isinstance(other, IpAddress):
870            return self.ip == other.ip
871        elif isinstance(other, str):
872            try:
873                return self.ip == convert.toIpAddress(other)
874            except:
875                return False
876        else:
877            return self.ip == other
878
879    def __ne__(self, other):
880        return not (self == other)
881
882    def verifyIp(self):
883        if self.ip < 0 or self.ip >= (1 << 32):
884            raise TypeError, "invalid ip address %#08x" % self.ip
885
886    def getValue(self):
887        from m5.internal.params import IpAddress
888        return IpAddress(self.ip)
889
890# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
891# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
892# positional or keyword arguments.
893class IpNetmask(IpAddress):
894    cxx_type = 'Net::IpNetmask'
895    ex_str = "127.0.0.0/24"
896    cmd_line_settable = True
897
898    @classmethod
899    def cxx_predecls(cls, code):
900        code('#include "base/inet.hh"')
901
902    @classmethod
903    def swig_predecls(cls, code):
904        code('%include "python/swig/inet.i"')
905
906    def __init__(self, *args, **kwargs):
907        def handle_kwarg(self, kwargs, key, elseVal = None):
908            if key in kwargs:
909                setattr(self, key, kwargs.pop(key))
910            elif elseVal:
911                setattr(self, key, elseVal)
912            else:
913                raise TypeError, "No value set for %s" % key
914
915        if len(args) == 0:
916            handle_kwarg(self, kwargs, 'ip')
917            handle_kwarg(self, kwargs, 'netmask')
918
919        elif len(args) == 1:
920            if kwargs:
921                if not 'ip' in kwargs and not 'netmask' in kwargs:
922                    raise TypeError, "Invalid arguments"
923                handle_kwarg(self, kwargs, 'ip', args[0])
924                handle_kwarg(self, kwargs, 'netmask', args[0])
925            elif isinstance(args[0], IpNetmask):
926                self.ip = args[0].ip
927                self.netmask = args[0].netmask
928            else:
929                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
930
931        elif len(args) == 2:
932            self.ip = args[0]
933            self.netmask = args[1]
934        else:
935            raise TypeError, "Too many arguments specified"
936
937        if kwargs:
938            raise TypeError, "Too many keywords: %s" % kwargs.keys()
939
940        self.verify()
941
942    def __call__(self, value):
943        self.__init__(value)
944        return value
945
946    def __str__(self):
947        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
948
949    def __eq__(self, other):
950        if isinstance(other, IpNetmask):
951            return self.ip == other.ip and self.netmask == other.netmask
952        elif isinstance(other, str):
953            try:
954                return (self.ip, self.netmask) == convert.toIpNetmask(other)
955            except:
956                return False
957        else:
958            return False
959
960    def verify(self):
961        self.verifyIp()
962        if self.netmask < 0 or self.netmask > 32:
963            raise TypeError, "invalid netmask %d" % netmask
964
965    def getValue(self):
966        from m5.internal.params import IpNetmask
967        return IpNetmask(self.ip, self.netmask)
968
969# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
970# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
971class IpWithPort(IpAddress):
972    cxx_type = 'Net::IpWithPort'
973    ex_str = "127.0.0.1:80"
974    cmd_line_settable = True
975
976    @classmethod
977    def cxx_predecls(cls, code):
978        code('#include "base/inet.hh"')
979
980    @classmethod
981    def swig_predecls(cls, code):
982        code('%include "python/swig/inet.i"')
983
984    def __init__(self, *args, **kwargs):
985        def handle_kwarg(self, kwargs, key, elseVal = None):
986            if key in kwargs:
987                setattr(self, key, kwargs.pop(key))
988            elif elseVal:
989                setattr(self, key, elseVal)
990            else:
991                raise TypeError, "No value set for %s" % key
992
993        if len(args) == 0:
994            handle_kwarg(self, kwargs, 'ip')
995            handle_kwarg(self, kwargs, 'port')
996
997        elif len(args) == 1:
998            if kwargs:
999                if not 'ip' in kwargs and not 'port' in kwargs:
1000                    raise TypeError, "Invalid arguments"
1001                handle_kwarg(self, kwargs, 'ip', args[0])
1002                handle_kwarg(self, kwargs, 'port', args[0])
1003            elif isinstance(args[0], IpWithPort):
1004                self.ip = args[0].ip
1005                self.port = args[0].port
1006            else:
1007                (self.ip, self.port) = convert.toIpWithPort(args[0])
1008
1009        elif len(args) == 2:
1010            self.ip = args[0]
1011            self.port = args[1]
1012        else:
1013            raise TypeError, "Too many arguments specified"
1014
1015        if kwargs:
1016            raise TypeError, "Too many keywords: %s" % kwargs.keys()
1017
1018        self.verify()
1019
1020    def __call__(self, value):
1021        self.__init__(value)
1022        return value
1023
1024    def __str__(self):
1025        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1026
1027    def __eq__(self, other):
1028        if isinstance(other, IpWithPort):
1029            return self.ip == other.ip and self.port == other.port
1030        elif isinstance(other, str):
1031            try:
1032                return (self.ip, self.port) == convert.toIpWithPort(other)
1033            except:
1034                return False
1035        else:
1036            return False
1037
1038    def verify(self):
1039        self.verifyIp()
1040        if self.port < 0 or self.port > 0xffff:
1041            raise TypeError, "invalid port %d" % self.port
1042
1043    def getValue(self):
1044        from m5.internal.params import IpWithPort
1045        return IpWithPort(self.ip, self.port)
1046
1047time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1048                 "%a %b %d %H:%M:%S %Z %Y",
1049                 "%Y/%m/%d %H:%M:%S",
1050                 "%Y/%m/%d %H:%M",
1051                 "%Y/%m/%d",
1052                 "%m/%d/%Y %H:%M:%S",
1053                 "%m/%d/%Y %H:%M",
1054                 "%m/%d/%Y",
1055                 "%m/%d/%y %H:%M:%S",
1056                 "%m/%d/%y %H:%M",
1057                 "%m/%d/%y"]
1058
1059
1060def parse_time(value):
1061    from time import gmtime, strptime, struct_time, time
1062    from datetime import datetime, date
1063
1064    if isinstance(value, struct_time):
1065        return value
1066
1067    if isinstance(value, (int, long)):
1068        return gmtime(value)
1069
1070    if isinstance(value, (datetime, date)):
1071        return value.timetuple()
1072
1073    if isinstance(value, str):
1074        if value in ('Now', 'Today'):
1075            return time.gmtime(time.time())
1076
1077        for format in time_formats:
1078            try:
1079                return strptime(value, format)
1080            except ValueError:
1081                pass
1082
1083    raise ValueError, "Could not parse '%s' as a time" % value
1084
1085class Time(ParamValue):
1086    cxx_type = 'tm'
1087
1088    @classmethod
1089    def cxx_predecls(cls, code):
1090        code('#include <time.h>')
1091
1092    @classmethod
1093    def swig_predecls(cls, code):
1094        code('%include "python/swig/time.i"')
1095
1096    def __init__(self, value):
1097        self.value = parse_time(value)
1098
1099    def __call__(self, value):
1100        self.__init__(value)
1101        return value
1102
1103    def getValue(self):
1104        from m5.internal.params import tm
1105
1106        c_time = tm()
1107        py_time = self.value
1108
1109        # UNIX is years since 1900
1110        c_time.tm_year = py_time.tm_year - 1900;
1111
1112        # Python starts at 1, UNIX starts at 0
1113        c_time.tm_mon =  py_time.tm_mon - 1;
1114        c_time.tm_mday = py_time.tm_mday;
1115        c_time.tm_hour = py_time.tm_hour;
1116        c_time.tm_min = py_time.tm_min;
1117        c_time.tm_sec = py_time.tm_sec;
1118
1119        # Python has 0 as Monday, UNIX is 0 as sunday
1120        c_time.tm_wday = py_time.tm_wday + 1
1121        if c_time.tm_wday > 6:
1122            c_time.tm_wday -= 7;
1123
1124        # Python starts at 1, Unix starts at 0
1125        c_time.tm_yday = py_time.tm_yday - 1;
1126
1127        return c_time
1128
1129    def __str__(self):
1130        return time.asctime(self.value)
1131
1132    def ini_str(self):
1133        return str(self)
1134
1135    def get_config_as_dict(self):
1136        return str(self)
1137
1138# Enumerated types are a little more complex.  The user specifies the
1139# type as Enum(foo) where foo is either a list or dictionary of
1140# alternatives (typically strings, but not necessarily so).  (In the
1141# long run, the integer value of the parameter will be the list index
1142# or the corresponding dictionary value.  For now, since we only check
1143# that the alternative is valid and then spit it into a .ini file,
1144# there's not much point in using the dictionary.)
1145
1146# What Enum() must do is generate a new type encapsulating the
1147# provided list/dictionary so that specific values of the parameter
1148# can be instances of that type.  We define two hidden internal
1149# classes (_ListEnum and _DictEnum) to serve as base classes, then
1150# derive the new type from the appropriate base class on the fly.
1151
1152allEnums = {}
1153# Metaclass for Enum types
1154class MetaEnum(MetaParamValue):
1155    def __new__(mcls, name, bases, dict):
1156        assert name not in allEnums
1157
1158        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1159        allEnums[name] = cls
1160        return cls
1161
1162    def __init__(cls, name, bases, init_dict):
1163        if init_dict.has_key('map'):
1164            if not isinstance(cls.map, dict):
1165                raise TypeError, "Enum-derived class attribute 'map' " \
1166                      "must be of type dict"
1167            # build list of value strings from map
1168            cls.vals = cls.map.keys()
1169            cls.vals.sort()
1170        elif init_dict.has_key('vals'):
1171            if not isinstance(cls.vals, list):
1172                raise TypeError, "Enum-derived class attribute 'vals' " \
1173                      "must be of type list"
1174            # build string->value map from vals sequence
1175            cls.map = {}
1176            for idx,val in enumerate(cls.vals):
1177                cls.map[val] = idx
1178        else:
1179            raise TypeError, "Enum-derived class must define "\
1180                  "attribute 'map' or 'vals'"
1181
1182        cls.cxx_type = 'Enums::%s' % name
1183
1184        super(MetaEnum, cls).__init__(name, bases, init_dict)
1185
1186    # Generate C++ class declaration for this enum type.
1187    # Note that we wrap the enum in a class/struct to act as a namespace,
1188    # so that the enum strings can be brief w/o worrying about collisions.
1189    def cxx_decl(cls, code):
1190        wrapper_name = cls.wrapper_name
1191        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1192        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1193        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1194
1195        code('''\
1196#ifndef $idem_macro
1197#define $idem_macro
1198
1199$wrapper $wrapper_name {
1200    enum $name {
1201''')
1202        code.indent(2)
1203        for val in cls.vals:
1204            code('$val = ${{cls.map[val]}},')
1205        code('Num_$name = ${{len(cls.vals)}}')
1206        code.dedent(2)
1207        code('    };')
1208
1209        if cls.wrapper_is_struct:
1210            code('    static const char *${name}Strings[Num_${name}];')
1211            code('};')
1212        else:
1213            code('extern const char *${name}Strings[Num_${name}];')
1214            code('}')
1215
1216        code()
1217        code('#endif // $idem_macro')
1218
1219    def cxx_def(cls, code):
1220        wrapper_name = cls.wrapper_name
1221        file_name = cls.__name__
1222        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1223
1224        code('#include "enums/$file_name.hh"')
1225        if cls.wrapper_is_struct:
1226            code('const char *${wrapper_name}::${name}Strings'
1227                '[Num_${name}] =')
1228        else:
1229            code('namespace Enums {')
1230            code.indent(1)
1231            code(' const char *${name}Strings[Num_${name}] =')
1232
1233        code('{')
1234        code.indent(1)
1235        for val in cls.vals:
1236            code('"$val",')
1237        code.dedent(1)
1238        code('};')
1239
1240        if not cls.wrapper_is_struct:
1241            code('} // namespace $wrapper_name')
1242            code.dedent(1)
1243
1244    def swig_decl(cls, code):
1245        name = cls.__name__
1246        code('''\
1247%module(package="m5.internal") enum_$name
1248
1249%{
1250#include "enums/$name.hh"
1251%}
1252
1253%include "enums/$name.hh"
1254''')
1255
1256
1257# Base class for enum types.
1258class Enum(ParamValue):
1259    __metaclass__ = MetaEnum
1260    vals = []
1261    cmd_line_settable = True
1262
1263    # The name of the wrapping namespace or struct
1264    wrapper_name = 'Enums'
1265
1266    # If true, the enum is wrapped in a struct rather than a namespace
1267    wrapper_is_struct = False
1268
1269    # If not None, use this as the enum name rather than this class name
1270    enum_name = None
1271
1272    def __init__(self, value):
1273        if value not in self.map:
1274            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1275                  % (value, self.vals)
1276        self.value = value
1277
1278    def __call__(self, value):
1279        self.__init__(value)
1280        return value
1281
1282    @classmethod
1283    def cxx_predecls(cls, code):
1284        code('#include "enums/$0.hh"', cls.__name__)
1285
1286    @classmethod
1287    def swig_predecls(cls, code):
1288        code('%import "python/m5/internal/enum_$0.i"', cls.__name__)
1289
1290    def getValue(self):
1291        return int(self.map[self.value])
1292
1293    def __str__(self):
1294        return self.value
1295
1296# how big does a rounding error need to be before we warn about it?
1297frequency_tolerance = 0.001  # 0.1%
1298
1299class TickParamValue(NumericParamValue):
1300    cxx_type = 'Tick'
1301    ex_str = "1MHz"
1302    cmd_line_settable = True
1303
1304    @classmethod
1305    def cxx_predecls(cls, code):
1306        code('#include "base/types.hh"')
1307
1308    @classmethod
1309    def swig_predecls(cls, code):
1310        code('%import "stdint.i"')
1311        code('%import "base/types.hh"')
1312
1313    def __call__(self, value):
1314        self.__init__(value)
1315        return value
1316
1317    def getValue(self):
1318        return long(self.value)
1319
1320class Latency(TickParamValue):
1321    ex_str = "100ns"
1322
1323    def __init__(self, value):
1324        if isinstance(value, (Latency, Clock)):
1325            self.ticks = value.ticks
1326            self.value = value.value
1327        elif isinstance(value, Frequency):
1328            self.ticks = value.ticks
1329            self.value = 1.0 / value.value
1330        elif value.endswith('t'):
1331            self.ticks = True
1332            self.value = int(value[:-1])
1333        else:
1334            self.ticks = False
1335            self.value = convert.toLatency(value)
1336
1337    def __call__(self, value):
1338        self.__init__(value)
1339        return value
1340
1341    def __getattr__(self, attr):
1342        if attr in ('latency', 'period'):
1343            return self
1344        if attr == 'frequency':
1345            return Frequency(self)
1346        raise AttributeError, "Latency object has no attribute '%s'" % attr
1347
1348    def getValue(self):
1349        if self.ticks or self.value == 0:
1350            value = self.value
1351        else:
1352            value = ticks.fromSeconds(self.value)
1353        return long(value)
1354
1355    # convert latency to ticks
1356    def ini_str(self):
1357        return '%d' % self.getValue()
1358
1359class Frequency(TickParamValue):
1360    ex_str = "1GHz"
1361
1362    def __init__(self, value):
1363        if isinstance(value, (Latency, Clock)):
1364            if value.value == 0:
1365                self.value = 0
1366            else:
1367                self.value = 1.0 / value.value
1368            self.ticks = value.ticks
1369        elif isinstance(value, Frequency):
1370            self.value = value.value
1371            self.ticks = value.ticks
1372        else:
1373            self.ticks = False
1374            self.value = convert.toFrequency(value)
1375
1376    def __call__(self, value):
1377        self.__init__(value)
1378        return value
1379
1380    def __getattr__(self, attr):
1381        if attr == 'frequency':
1382            return self
1383        if attr in ('latency', 'period'):
1384            return Latency(self)
1385        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1386
1387    # convert latency to ticks
1388    def getValue(self):
1389        if self.ticks or self.value == 0:
1390            value = self.value
1391        else:
1392            value = ticks.fromSeconds(1.0 / self.value)
1393        return long(value)
1394
1395    def ini_str(self):
1396        return '%d' % self.getValue()
1397
1398# A generic Frequency and/or Latency value. Value is stored as a
1399# latency, just like Latency and Frequency.
1400class Clock(TickParamValue):
1401    def __init__(self, value):
1402        if isinstance(value, (Latency, Clock)):
1403            self.ticks = value.ticks
1404            self.value = value.value
1405        elif isinstance(value, Frequency):
1406            self.ticks = value.ticks
1407            self.value = 1.0 / value.value
1408        elif value.endswith('t'):
1409            self.ticks = True
1410            self.value = int(value[:-1])
1411        else:
1412            self.ticks = False
1413            self.value = convert.anyToLatency(value)
1414
1415    def __call__(self, value):
1416        self.__init__(value)
1417        return value
1418
1419    def __str__(self):
1420        return "%s" % Latency(self)
1421
1422    def __getattr__(self, attr):
1423        if attr == 'frequency':
1424            return Frequency(self)
1425        if attr in ('latency', 'period'):
1426            return Latency(self)
1427        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1428
1429    def getValue(self):
1430        return self.period.getValue()
1431
1432    def ini_str(self):
1433        return self.period.ini_str()
1434
1435class Voltage(float,ParamValue):
1436    cxx_type = 'double'
1437    ex_str = "1V"
1438    cmd_line_settable = False
1439
1440    def __new__(cls, value):
1441        # convert to voltage
1442        val = convert.toVoltage(value)
1443        return super(cls, Voltage).__new__(cls, val)
1444
1445    def __call__(self, value):
1446        val = convert.toVoltage(value)
1447        self.__init__(val)
1448        return value
1449
1450    def __str__(self):
1451        return str(self.getValue())
1452
1453    def getValue(self):
1454        value = float(self)
1455        return value
1456
1457    def ini_str(self):
1458        return '%f' % self.getValue()
1459
1460class NetworkBandwidth(float,ParamValue):
1461    cxx_type = 'float'
1462    ex_str = "1Gbps"
1463    cmd_line_settable = True
1464
1465    def __new__(cls, value):
1466        # convert to bits per second
1467        val = convert.toNetworkBandwidth(value)
1468        return super(cls, NetworkBandwidth).__new__(cls, val)
1469
1470    def __str__(self):
1471        return str(self.val)
1472
1473    def __call__(self, value):
1474        val = convert.toNetworkBandwidth(value)
1475        self.__init__(val)
1476        return value
1477
1478    def getValue(self):
1479        # convert to seconds per byte
1480        value = 8.0 / float(self)
1481        # convert to ticks per byte
1482        value = ticks.fromSeconds(value)
1483        return float(value)
1484
1485    def ini_str(self):
1486        return '%f' % self.getValue()
1487
1488class MemoryBandwidth(float,ParamValue):
1489    cxx_type = 'float'
1490    ex_str = "1GB/s"
1491    cmd_line_settable = True
1492
1493    def __new__(cls, value):
1494        # convert to bytes per second
1495        val = convert.toMemoryBandwidth(value)
1496        return super(cls, MemoryBandwidth).__new__(cls, val)
1497
1498    def __call__(self, value):
1499        val = convert.toMemoryBandwidth(value)
1500        self.__init__(val)
1501        return value
1502
1503    def getValue(self):
1504        # convert to seconds per byte
1505        value = float(self)
1506        if value:
1507            value = 1.0 / float(self)
1508        # convert to ticks per byte
1509        value = ticks.fromSeconds(value)
1510        return float(value)
1511
1512    def ini_str(self):
1513        return '%f' % self.getValue()
1514
1515#
1516# "Constants"... handy aliases for various values.
1517#
1518
1519# Special class for NULL pointers.  Note the special check in
1520# make_param_value() above that lets these be assigned where a
1521# SimObject is required.
1522# only one copy of a particular node
1523class NullSimObject(object):
1524    __metaclass__ = Singleton
1525
1526    def __call__(cls):
1527        return cls
1528
1529    def _instantiate(self, parent = None, path = ''):
1530        pass
1531
1532    def ini_str(self):
1533        return 'Null'
1534
1535    def unproxy(self, base):
1536        return self
1537
1538    def set_path(self, parent, name):
1539        pass
1540
1541    def __str__(self):
1542        return 'Null'
1543
1544    def getValue(self):
1545        return None
1546
1547# The only instance you'll ever need...
1548NULL = NullSimObject()
1549
1550def isNullPointer(value):
1551    return isinstance(value, NullSimObject)
1552
1553# Some memory range specifications use this as a default upper bound.
1554MaxAddr = Addr.max
1555MaxTick = Tick.max
1556AllMemory = AddrRange(0, MaxAddr)
1557
1558
1559#####################################################################
1560#
1561# Port objects
1562#
1563# Ports are used to interconnect objects in the memory system.
1564#
1565#####################################################################
1566
1567# Port reference: encapsulates a reference to a particular port on a
1568# particular SimObject.
1569class PortRef(object):
1570    def __init__(self, simobj, name, role):
1571        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1572        self.simobj = simobj
1573        self.name = name
1574        self.role = role
1575        self.peer = None   # not associated with another port yet
1576        self.ccConnected = False # C++ port connection done?
1577        self.index = -1  # always -1 for non-vector ports
1578
1579    def __str__(self):
1580        return '%s.%s' % (self.simobj, self.name)
1581
1582    def __len__(self):
1583        # Return the number of connected ports, i.e. 0 is we have no
1584        # peer and 1 if we do.
1585        return int(self.peer != None)
1586
1587    # for config.ini, print peer's name (not ours)
1588    def ini_str(self):
1589        return str(self.peer)
1590
1591    # for config.json
1592    def get_config_as_dict(self):
1593        return {'role' : self.role, 'peer' : str(self.peer)}
1594
1595    def __getattr__(self, attr):
1596        if attr == 'peerObj':
1597            # shorthand for proxies
1598            return self.peer.simobj
1599        raise AttributeError, "'%s' object has no attribute '%s'" % \
1600              (self.__class__.__name__, attr)
1601
1602    # Full connection is symmetric (both ways).  Called via
1603    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1604    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1605    # e.g., "obj1.portA[3] = obj2.portB".
1606    def connect(self, other):
1607        if isinstance(other, VectorPortRef):
1608            # reference to plain VectorPort is implicit append
1609            other = other._get_next()
1610        if self.peer and not proxy.isproxy(self.peer):
1611            fatal("Port %s is already connected to %s, cannot connect %s\n",
1612                  self, self.peer, other);
1613        self.peer = other
1614        if proxy.isproxy(other):
1615            other.set_param_desc(PortParamDesc())
1616        elif isinstance(other, PortRef):
1617            if other.peer is not self:
1618                other.connect(self)
1619        else:
1620            raise TypeError, \
1621                  "assigning non-port reference '%s' to port '%s'" \
1622                  % (other, self)
1623
1624    # Allow a master/slave port pair to be spliced between
1625    # a port and its connected peer. Useful operation for connecting
1626    # instrumentation structures into a system when it is necessary
1627    # to connect the instrumentation after the full system has been
1628    # constructed.
1629    def splice(self, new_master_peer, new_slave_peer):
1630        if self.peer and not proxy.isproxy(self.peer):
1631            if isinstance(new_master_peer, PortRef) and \
1632               isinstance(new_slave_peer, PortRef):
1633                 old_peer = self.peer
1634                 if self.role == 'SLAVE':
1635                     self.peer = new_master_peer
1636                     old_peer.peer = new_slave_peer
1637                     new_master_peer.connect(self)
1638                     new_slave_peer.connect(old_peer)
1639                 elif self.role == 'MASTER':
1640                     self.peer = new_slave_peer
1641                     old_peer.peer = new_master_peer
1642                     new_slave_peer.connect(self)
1643                     new_master_peer.connect(old_peer)
1644                 else:
1645                     panic("Port %s has unknown role, "+\
1646                           "cannot splice in new peers\n", self)
1647            else:
1648                raise TypeError, \
1649                      "Splicing non-port references '%s','%s' to port '%s'"\
1650                      % (new_peer, peers_new_peer, self)
1651        else:
1652            fatal("Port %s not connected, cannot splice in new peers\n", self)
1653
1654    def clone(self, simobj, memo):
1655        if memo.has_key(self):
1656            return memo[self]
1657        newRef = copy.copy(self)
1658        memo[self] = newRef
1659        newRef.simobj = simobj
1660        assert(isSimObject(newRef.simobj))
1661        if self.peer and not proxy.isproxy(self.peer):
1662            peerObj = self.peer.simobj(_memo=memo)
1663            newRef.peer = self.peer.clone(peerObj, memo)
1664            assert(not isinstance(newRef.peer, VectorPortRef))
1665        return newRef
1666
1667    def unproxy(self, simobj):
1668        assert(simobj is self.simobj)
1669        if proxy.isproxy(self.peer):
1670            try:
1671                realPeer = self.peer.unproxy(self.simobj)
1672            except:
1673                print "Error in unproxying port '%s' of %s" % \
1674                      (self.name, self.simobj.path())
1675                raise
1676            self.connect(realPeer)
1677
1678    # Call C++ to create corresponding port connection between C++ objects
1679    def ccConnect(self):
1680        from m5.internal.pyobject import connectPorts
1681
1682        if self.role == 'SLAVE':
1683            # do nothing and let the master take care of it
1684            return
1685
1686        if self.ccConnected: # already done this
1687            return
1688        peer = self.peer
1689        if not self.peer: # nothing to connect to
1690            return
1691
1692        # check that we connect a master to a slave
1693        if self.role == peer.role:
1694            raise TypeError, \
1695                "cannot connect '%s' and '%s' due to identical role '%s'" \
1696                % (peer, self, self.role)
1697
1698        try:
1699            # self is always the master and peer the slave
1700            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1701                         peer.simobj.getCCObject(), peer.name, peer.index)
1702        except:
1703            print "Error connecting port %s.%s to %s.%s" % \
1704                  (self.simobj.path(), self.name,
1705                   peer.simobj.path(), peer.name)
1706            raise
1707        self.ccConnected = True
1708        peer.ccConnected = True
1709
1710# A reference to an individual element of a VectorPort... much like a
1711# PortRef, but has an index.
1712class VectorPortElementRef(PortRef):
1713    def __init__(self, simobj, name, role, index):
1714        PortRef.__init__(self, simobj, name, role)
1715        self.index = index
1716
1717    def __str__(self):
1718        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1719
1720# A reference to a complete vector-valued port (not just a single element).
1721# Can be indexed to retrieve individual VectorPortElementRef instances.
1722class VectorPortRef(object):
1723    def __init__(self, simobj, name, role):
1724        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1725        self.simobj = simobj
1726        self.name = name
1727        self.role = role
1728        self.elements = []
1729
1730    def __str__(self):
1731        return '%s.%s[:]' % (self.simobj, self.name)
1732
1733    def __len__(self):
1734        # Return the number of connected peers, corresponding the the
1735        # length of the elements.
1736        return len(self.elements)
1737
1738    # for config.ini, print peer's name (not ours)
1739    def ini_str(self):
1740        return ' '.join([el.ini_str() for el in self.elements])
1741
1742    # for config.json
1743    def get_config_as_dict(self):
1744        return {'role' : self.role,
1745                'peer' : [el.ini_str() for el in self.elements]}
1746
1747    def __getitem__(self, key):
1748        if not isinstance(key, int):
1749            raise TypeError, "VectorPort index must be integer"
1750        if key >= len(self.elements):
1751            # need to extend list
1752            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1753                   for i in range(len(self.elements), key+1)]
1754            self.elements.extend(ext)
1755        return self.elements[key]
1756
1757    def _get_next(self):
1758        return self[len(self.elements)]
1759
1760    def __setitem__(self, key, value):
1761        if not isinstance(key, int):
1762            raise TypeError, "VectorPort index must be integer"
1763        self[key].connect(value)
1764
1765    def connect(self, other):
1766        if isinstance(other, (list, tuple)):
1767            # Assign list of port refs to vector port.
1768            # For now, append them... not sure if that's the right semantics
1769            # or if it should replace the current vector.
1770            for ref in other:
1771                self._get_next().connect(ref)
1772        else:
1773            # scalar assignment to plain VectorPort is implicit append
1774            self._get_next().connect(other)
1775
1776    def clone(self, simobj, memo):
1777        if memo.has_key(self):
1778            return memo[self]
1779        newRef = copy.copy(self)
1780        memo[self] = newRef
1781        newRef.simobj = simobj
1782        assert(isSimObject(newRef.simobj))
1783        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1784        return newRef
1785
1786    def unproxy(self, simobj):
1787        [el.unproxy(simobj) for el in self.elements]
1788
1789    def ccConnect(self):
1790        [el.ccConnect() for el in self.elements]
1791
1792# Port description object.  Like a ParamDesc object, this represents a
1793# logical port in the SimObject class, not a particular port on a
1794# SimObject instance.  The latter are represented by PortRef objects.
1795class Port(object):
1796    # Generate a PortRef for this port on the given SimObject with the
1797    # given name
1798    def makeRef(self, simobj):
1799        return PortRef(simobj, self.name, self.role)
1800
1801    # Connect an instance of this port (on the given SimObject with
1802    # the given name) with the port described by the supplied PortRef
1803    def connect(self, simobj, ref):
1804        self.makeRef(simobj).connect(ref)
1805
1806    # No need for any pre-declarations at the moment as we merely rely
1807    # on an unsigned int.
1808    def cxx_predecls(self, code):
1809        pass
1810
1811    # Declare an unsigned int with the same name as the port, that
1812    # will eventually hold the number of connected ports (and thus the
1813    # number of elements for a VectorPort).
1814    def cxx_decl(self, code):
1815        code('unsigned int port_${{self.name}}_connection_count;')
1816
1817class MasterPort(Port):
1818    # MasterPort("description")
1819    def __init__(self, *args):
1820        if len(args) == 1:
1821            self.desc = args[0]
1822            self.role = 'MASTER'
1823        else:
1824            raise TypeError, 'wrong number of arguments'
1825
1826class SlavePort(Port):
1827    # SlavePort("description")
1828    def __init__(self, *args):
1829        if len(args) == 1:
1830            self.desc = args[0]
1831            self.role = 'SLAVE'
1832        else:
1833            raise TypeError, 'wrong number of arguments'
1834
1835# VectorPort description object.  Like Port, but represents a vector
1836# of connections (e.g., as on a Bus).
1837class VectorPort(Port):
1838    def __init__(self, *args):
1839        self.isVec = True
1840
1841    def makeRef(self, simobj):
1842        return VectorPortRef(simobj, self.name, self.role)
1843
1844class VectorMasterPort(VectorPort):
1845    # VectorMasterPort("description")
1846    def __init__(self, *args):
1847        if len(args) == 1:
1848            self.desc = args[0]
1849            self.role = 'MASTER'
1850            VectorPort.__init__(self, *args)
1851        else:
1852            raise TypeError, 'wrong number of arguments'
1853
1854class VectorSlavePort(VectorPort):
1855    # VectorSlavePort("description")
1856    def __init__(self, *args):
1857        if len(args) == 1:
1858            self.desc = args[0]
1859            self.role = 'SLAVE'
1860            VectorPort.__init__(self, *args)
1861        else:
1862            raise TypeError, 'wrong number of arguments'
1863
1864# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1865# proxy objects (via set_param_desc()) so that proxy error messages
1866# make sense.
1867class PortParamDesc(object):
1868    __metaclass__ = Singleton
1869
1870    ptype_str = 'Port'
1871    ptype = Port
1872
1873baseEnums = allEnums.copy()
1874baseParams = allParams.copy()
1875
1876def clear():
1877    global allEnums, allParams
1878
1879    allEnums = baseEnums.copy()
1880    allParams = baseParams.copy()
1881
1882__all__ = ['Param', 'VectorParam',
1883           'Enum', 'Bool', 'String', 'Float',
1884           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1885           'Int32', 'UInt32', 'Int64', 'UInt64',
1886           'Counter', 'Addr', 'Tick', 'Percent',
1887           'TcpPort', 'UdpPort', 'EthernetAddr',
1888           'IpAddress', 'IpNetmask', 'IpWithPort',
1889           'MemorySize', 'MemorySize32',
1890           'Latency', 'Frequency', 'Clock', 'Voltage',
1891           'NetworkBandwidth', 'MemoryBandwidth',
1892           'AddrRange',
1893           'MaxAddr', 'MaxTick', 'AllMemory',
1894           'Time',
1895           'NextEthernetAddr', 'NULL',
1896           'MasterPort', 'SlavePort',
1897           'VectorMasterPort', 'VectorSlavePort']
1898
1899import SimObject
1900