SimObject.py revision 1587
12292SN/A# Copyright (c) 2004 The Regents of The University of Michigan
22727Sktlim@umich.edu# All rights reserved.
32292SN/A#
42292SN/A# Redistribution and use in source and binary forms, with or without
52292SN/A# modification, are permitted provided that the following conditions are
62292SN/A# met: redistributions of source code must retain the above copyright
72292SN/A# notice, this list of conditions and the following disclaimer;
82292SN/A# redistributions in binary form must reproduce the above copyright
92292SN/A# notice, this list of conditions and the following disclaimer in the
102292SN/A# documentation and/or other materials provided with the distribution;
112292SN/A# neither the name of the copyright holders nor the names of its
122292SN/A# contributors may be used to endorse or promote products derived from
132292SN/A# this software without specific prior written permission.
142292SN/A#
152292SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
162292SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
172292SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
182292SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
192292SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
202292SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
212292SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
222292SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
232292SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
242292SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
252292SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
262292SN/A
272689Sktlim@umich.edufrom __future__ import generators
282689Sktlim@umich.eduimport os, re, sys, types, inspect
292292SN/A
302292SN/Afrom m5 import panic
312329SN/Afrom convert import *
322980Sgblack@eecs.umich.edufrom multidict import multidict
332329SN/A
342329SN/AnoDot = False
352292SN/Atry:
368232Snate@binkert.org    import pydot
378232Snate@binkert.orgexcept:
388232Snate@binkert.org    noDot = True
396221Snate@binkert.org
402292SN/Aclass Singleton(type):
416221Snate@binkert.org    def __call__(cls, *args, **kwargs):
425529Snate@binkert.org        if hasattr(cls, '_instance'):
434192Sktlim@umich.edu            return cls._instance
444192Sktlim@umich.edu
454192Sktlim@umich.edu        cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
464192Sktlim@umich.edu        return cls._instance
474192Sktlim@umich.edu
484192Sktlim@umich.edu#####################################################################
494192Sktlim@umich.edu#
504192Sktlim@umich.edu# M5 Python Configuration Utility
514192Sktlim@umich.edu#
524192Sktlim@umich.edu# The basic idea is to write simple Python programs that build Python
534192Sktlim@umich.edu# objects corresponding to M5 SimObjects for the deisred simulation
544192Sktlim@umich.edu# configuration.  For now, the Python emits a .ini file that can be
554192Sktlim@umich.edu# parsed by M5.  In the future, some tighter integration between M5
562292SN/A# and the Python interpreter may allow bypassing the .ini file.
572907Sktlim@umich.edu#
582907Sktlim@umich.edu# Each SimObject class in M5 is represented by a Python class with the
592907Sktlim@umich.edu# same name.  The Python inheritance tree mirrors the M5 C++ tree
602907Sktlim@umich.edu# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
617823Ssteve.reinhardt@amd.com# SimObjects inherit from a single SimObject base class).  To specify
622907Sktlim@umich.edu# an instance of an M5 SimObject in a configuration, the user simply
632907Sktlim@umich.edu# instantiates the corresponding Python object.  The parameters for
642907Sktlim@umich.edu# that SimObject are given by assigning to attributes of the Python
652907Sktlim@umich.edu# object, either using keyword assignment in the constructor or in
662907Sktlim@umich.edu# separate assignment statements.  For example:
672907Sktlim@umich.edu#
688346Sksewell@umich.edu# cache = BaseCache('my_cache', root, size=64*K)
692907Sktlim@umich.edu# cache.hit_latency = 3
702907Sktlim@umich.edu# cache.assoc = 8
712907Sktlim@umich.edu#
722907Sktlim@umich.edu# (The first two constructor arguments specify the name of the created
732907Sktlim@umich.edu# cache and its parent node in the hierarchy.)
742907Sktlim@umich.edu#
753647Srdreslin@umich.edu# The magic lies in the mapping of the Python attributes for SimObject
763647Srdreslin@umich.edu# classes to the actual SimObject parameter specifications.  This
773647Srdreslin@umich.edu# allows parameter validity checking in the Python code.  Continuing
783647Srdreslin@umich.edu# the example above, the statements "cache.blurfl=3" or
793647Srdreslin@umich.edu# "cache.assoc='hello'" would both result in runtime errors in Python,
802907Sktlim@umich.edu# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
813647Srdreslin@umich.edu# parameter requires an integer, respectively.  This magic is done
822907Sktlim@umich.edu# primarily by overriding the special __setattr__ method that controls
832907Sktlim@umich.edu# assignment to object attributes.
842907Sktlim@umich.edu#
852907Sktlim@umich.edu# The Python module provides another class, ConfigNode, which is a
862907Sktlim@umich.edu# superclass of SimObject.  ConfigNode implements the parent/child
872907Sktlim@umich.edu# relationship for building the configuration hierarchy tree.
882907Sktlim@umich.edu# Concrete instances of ConfigNode can be used to group objects in the
894986Ssaidi@eecs.umich.edu# hierarchy, but do not correspond to SimObjects themselves (like a
904986Ssaidi@eecs.umich.edu# .ini section with "children=" but no "type=".
913310Srdreslin@umich.edu#
925714Shsul@eecs.umich.edu# Once a set of Python objects have been instantiated in a hierarchy,
933310Srdreslin@umich.edu# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
943310Srdreslin@umich.edu# will generate a .ini file.  See simple-4cpu.py for an example
954895Sstever@eecs.umich.edu# (corresponding to m5-test/simple-4cpu.ini).
964895Sstever@eecs.umich.edu#
974895Sstever@eecs.umich.edu#####################################################################
984895Sstever@eecs.umich.edu
993310Srdreslin@umich.edu#####################################################################
1002907Sktlim@umich.edu#
1012907Sktlim@umich.edu# ConfigNode/SimObject classes
1022907Sktlim@umich.edu#
1032907Sktlim@umich.edu# The Python class hierarchy rooted by ConfigNode (which is the base
1042907Sktlim@umich.edu# class of SimObject, which in turn is the base class of all other M5
1052907Sktlim@umich.edu# SimObject classes) has special attribute behavior.  In general, an
1062907Sktlim@umich.edu# object in this hierarchy has three categories of attribute-like
1073014Srdreslin@umich.edu# things:
1083014Srdreslin@umich.edu#
1093014Srdreslin@umich.edu# 1. Regular Python methods and variables.  These must start with an
1103014Srdreslin@umich.edu# underscore to be treated normally.
1113014Srdreslin@umich.edu#
1124985Sktlim@umich.edu# 2. SimObject parameters.  These values are stored as normal Python
1132907Sktlim@umich.edu# attributes, but all assignments to these attributes are checked
1142907Sktlim@umich.edu# against the pre-defined set of parameters stored in the class's
1152907Sktlim@umich.edu# _params dictionary.  Assignments to attributes that do not
1164985Sktlim@umich.edu# correspond to predefined parameters, or that are not of the correct
1172907Sktlim@umich.edu# type, incur runtime errors.
1182907Sktlim@umich.edu#
1192907Sktlim@umich.edu# 3. Hierarchy children.  The child nodes of a ConfigNode are stored
1205529Snate@binkert.org# in the node's _children dictionary, but can be accessed using the
1215494Sstever@gmail.com# Python attribute dot-notation (just as they are printed out by the
1224329Sktlim@umich.edu# simulator).  Children cannot be created using attribute assigment;
1234329Sktlim@umich.edu# they must be added by specifying the parent node in the child's
1245529Snate@binkert.org# constructor or using the '+=' operator.
1252907Sktlim@umich.edu
1262292SN/A# The SimObject parameters are the most complex, for a few reasons.
1273647Srdreslin@umich.edu# First, both parameter descriptions and parameter values are
1283647Srdreslin@umich.edu# inherited.  Thus parameter description lookup must go up the
1292292SN/A# inheritance chain like normal attribute lookup, but this behavior
1302292SN/A# must be explicitly coded since the lookup occurs in each class's
1312292SN/A# _params attribute.  Second, because parameter values can be set
1322980Sgblack@eecs.umich.edu# on SimObject classes (to implement default values), the parameter
1332292SN/A# checking behavior must be enforced on class attribute assignments as
1342292SN/A# well as instance attribute assignments.  Finally, because we allow
1352292SN/A# class specialization via inheritance (e.g., see the L1Cache class in
1362292SN/A# the simple-4cpu.py example), we must do parameter checking even on
1372292SN/A# class instantiation.  To provide all these features, we use a
1382292SN/A# metaclass to define most of the SimObject parameter behavior for
1392292SN/A# this class hierarchy.
1402292SN/A#
1412292SN/A#####################################################################
1422292SN/A
1432292SN/Aclass Proxy(object):
1444329Sktlim@umich.edu    def __init__(self, path):
1452292SN/A        self._object = None
1462292SN/A        if path == 'any':
1472292SN/A            self._path = None
1482292SN/A        else:
1492292SN/A            # path is a list of (attr,index) tuples
1502292SN/A            self._path = [(path,None)]
1512292SN/A        self._index = None
1524329Sktlim@umich.edu        self._multiplier = None
1532292SN/A
1548346Sksewell@umich.edu    def __getattr__(self, attr):
1552292SN/A        # python uses __bases__ internally for inheritance
1562292SN/A        if attr == '__bases__':
1572292SN/A            return super(Proxy, self).__getattr__(self, attr)
1582292SN/A        if (self._path == None):
1592292SN/A            panic("Can't add attributes to 'any' proxy")
1602292SN/A        self._path.append((attr,None))
1612292SN/A        return self
1622292SN/A
1632292SN/A    def __setattr__(self, attr, value):
1642292SN/A        if not attr.startswith('_'):
1652292SN/A            raise AttributeError, 'cannot set attribute %s' % attr
1662292SN/A        super(Proxy, self).__setattr__(attr, value)
1674329Sktlim@umich.edu
1682292SN/A    # support indexing on proxies (e.g., parent.cpu[0])
1698346Sksewell@umich.edu    def __getitem__(self, key):
1702292SN/A        if not isinstance(key, int):
1712292SN/A            raise TypeError, "Proxy object requires integer index"
1722292SN/A        if self._path == None:
1732292SN/A            raise IndexError, "Index applied to 'any' proxy"
1742292SN/A        # replace index portion of last path element with new index
1752292SN/A        self._path[-1] = (self._path[-1][0], key)
1762292SN/A        return self
1776221Snate@binkert.org
1784329Sktlim@umich.edu    # support multiplying proxies by constants
1794329Sktlim@umich.edu    def __mul__(self, other):
1802907Sktlim@umich.edu        if not isinstance(other, int):
1812292SN/A            raise TypeError, "Proxy multiplier must be integer"
1822292SN/A        if self._multiplier == None:
1832292SN/A            self._multiplier = other
1842292SN/A        else:
1852292SN/A            # support chained multipliers
1862292SN/A            self._multiplier *= other
1872292SN/A        return self
1882292SN/A
1892292SN/A    def _mulcheck(self, result):
1902292SN/A        if self._multiplier == None:
1912292SN/A            return result
1922292SN/A        if not isinstance(result, int):
1932292SN/A            raise TypeError, "Proxy with multiplier resolves to " \
1942727Sktlim@umich.edu                  "non-integer value"
1952727Sktlim@umich.edu        return result * self._multiplier
1962727Sktlim@umich.edu
1976221Snate@binkert.org    def unproxy(self, base, ptype):
1982727Sktlim@umich.edu        obj = base
1992727Sktlim@umich.edu        done = False
2002727Sktlim@umich.edu        while not done:
2012727Sktlim@umich.edu            if obj is None:
2022727Sktlim@umich.edu                raise AttributeError, \
2032727Sktlim@umich.edu                      'Parent of %s type %s not found at path %s' \
2046221Snate@binkert.org                      % (base.name, ptype, self._path)
2052292SN/A
2062292SN/A            result, done = obj.find(ptype, self._path)
2072292SN/A            obj = obj.parent
2082292SN/A
2092292SN/A        if isinstance(result, Proxy):
2102292SN/A            result = result.unproxy(obj, ptype)
2112307SN/A
2122307SN/A        return self._mulcheck(result)
2132307SN/A
2146221Snate@binkert.org    def getindex(obj, index):
2152307SN/A        if index == None:
2162307SN/A            return obj
2172307SN/A        try:
2182307SN/A            obj = obj[index]
2192307SN/A        except TypeError:
2202307SN/A            if index != 0:
2212307SN/A                raise
2222307SN/A            # if index is 0 and item is not subscriptable, just
2236221Snate@binkert.org            # use item itself (so cpu[0] works on uniprocessors)
2242307SN/A        return obj
2252307SN/A    getindex = staticmethod(getindex)
2262307SN/A
2272307SN/Aclass ProxyFactory(object):
2282307SN/A    def __getattr__(self, attr):
2292292SN/A        return Proxy(attr)
2306221Snate@binkert.org
2312292SN/A# global object for handling parent.foo proxies
2322292SN/Aparent = ProxyFactory()
2332292SN/A
2342292SN/Adef isSubClass(value, cls):
2352292SN/A    try:
2362292SN/A        return issubclass(value, cls)
2372292SN/A    except:
2382292SN/A        return False
2392292SN/A
2402292SN/Adef isConfigNode(value):
2412292SN/A    try:
2422292SN/A        return issubclass(value, ConfigNode)
2432292SN/A    except:
2443867Sbinkertn@umich.edu        return False
2452292SN/A
2462292SN/Adef isSimObject(value):
2472292SN/A    try:
2482292SN/A        return issubclass(value, SimObject)
2492292SN/A    except:
2502292SN/A        return False
2512292SN/A
2522292SN/Adef isSimObjSequence(value):
2532292SN/A    if not isinstance(value, (list, tuple)):
2542292SN/A        return False
2552292SN/A
2566221Snate@binkert.org    for val in value:
2576221Snate@binkert.org        if not isNullPointer(val) and not isConfigNode(val):
2583867Sbinkertn@umich.edu            return False
2593867Sbinkertn@umich.edu
2606221Snate@binkert.org    return True
2613867Sbinkertn@umich.edu
2623867Sbinkertn@umich.edudef isParamContext(value):
2632292SN/A    try:
2642292SN/A        return issubclass(value, ParamContext)
2652292SN/A    except:
2662292SN/A        return False
2672292SN/A
2682292SN/A
2696221Snate@binkert.orgclass_decorator = 'M5M5_SIMOBJECT_'
2702292SN/Aexpr_decorator = 'M5M5_EXPRESSION_'
2712292SN/Adot_decorator = '_M5M5_DOT_'
2722292SN/A
2732292SN/A# 'Global' map of legitimate types for SimObject parameters.
2742292SN/Aparam_types = {}
2752292SN/A
2762292SN/A# Dummy base class to identify types that are legitimate for SimObject
2776221Snate@binkert.org# parameters.
2782292SN/Aclass ParamType(object):
2792292SN/A    pass
2802292SN/A
2812292SN/A# Add types defined in given context (dict or module) that are derived
2822292SN/A# from ParamType to param_types map.
2832292SN/Adef add_param_types(ctx):
2842292SN/A    if isinstance(ctx, types.DictType):
2852292SN/A        source_dict = ctx
2862292SN/A    elif isinstance(ctx, types.ModuleType):
2876221Snate@binkert.org        source_dict = ctx.__dict__
2886221Snate@binkert.org    else:
2892292SN/A        raise TypeError, \
2903867Sbinkertn@umich.edu              "m5.config.add_param_types requires dict or module as arg"
2916221Snate@binkert.org    for key,val in source_dict.iteritems():
2922292SN/A        if isinstance(val, type) and issubclass(val, ParamType):
2932292SN/A            param_types[key] = val
2942292SN/A
2952292SN/A# The metaclass for ConfigNode (and thus for everything that derives
2962292SN/A# from ConfigNode, including SimObject).  This class controls how new
2972292SN/A# classes that derive from ConfigNode are instantiated, and provides
2982292SN/A# inherited class behavior (just like a class controls how instances
2992292SN/A# of that class are instantiated, and provides inherited instance
3002292SN/A# behavior).
3016221Snate@binkert.orgclass MetaConfigNode(type):
3022292SN/A    # Attributes that can be set only at initialization time
3032292SN/A    init_keywords = {}
3042292SN/A    # Attributes that can be set any time
3052292SN/A    keywords = { 'check' : types.FunctionType,
3062292SN/A                 'children' : types.ListType }
3072292SN/A
3082292SN/A    # __new__ is called before __init__, and is where the statements
3092292SN/A    # in the body of the class definition get loaded into the class's
3106221Snate@binkert.org    # __dict__.  We intercept this to filter out parameter assignments
3112292SN/A    # and only allow "private" attributes to be passed to the base
3122292SN/A    # __new__ (starting with underscore).
3132292SN/A    def __new__(mcls, name, bases, dict):
3142292SN/A        # Copy "private" attributes (including special methods such as __new__)
3152292SN/A        # to the official dict.  Everything else goes in _init_dict to be
3162292SN/A        # filtered in __init__.
3172292SN/A        cls_dict = {}
3182292SN/A        for key,val in dict.items():
3196221Snate@binkert.org            if key.startswith('_'):
3202292SN/A                cls_dict[key] = val
3212292SN/A                del dict[key]
3222292SN/A        cls_dict['_init_dict'] = dict
3232292SN/A        return super(MetaConfigNode, mcls).__new__(mcls, name, bases, cls_dict)
3242292SN/A
3252292SN/A    # initialization
3262292SN/A    def __init__(cls, name, bases, dict):
3272292SN/A        super(MetaConfigNode, cls).__init__(name, bases, dict)
3286221Snate@binkert.org
3292292SN/A        # initialize required attributes
3302292SN/A        cls._params = multidict()
3312292SN/A        cls._values = multidict()
3322292SN/A        cls._param_types = {}
3332292SN/A        cls._bases = [c for c in cls.__mro__ if isConfigNode(c)]
3342292SN/A        cls._anon_subclass_counter = 0
3352292SN/A
3362292SN/A        # We don't support multiple inheritence.  If you want to, you
3376221Snate@binkert.org        # must fix multidict to deal with it properly.
3386221Snate@binkert.org        sob = [ base for base in bases \
3392292SN/A                if issubclass(base, ParamType) and base != ParamType ]
3403867Sbinkertn@umich.edu
3416221Snate@binkert.org        if len(sob) == 1:
3422292SN/A            # If your parent has a value in it that's a config node, clone
3432292SN/A            # it.  Do this now so if we update any of the values'
3442329SN/A            # attributes we are updating the clone and not the original.
3452329SN/A            for key,val in sob[0]._values.iteritems():
3462292SN/A
3472292SN/A                # don't clone if (1) we're about to overwrite it with
3482292SN/A                # a local setting or (2) we've already cloned a copy
3492292SN/A                # from an earlier (more derived) base
3502292SN/A                if cls._init_dict.has_key(key) or cls._values.has_key(key):
3512292SN/A                    continue
3522292SN/A
3532292SN/A                if isConfigNode(val):
3542292SN/A                    cls._values[key] = val()
3552292SN/A                elif isSimObjSequence(val) and len(val):
3562292SN/A                    cls._values[key] = [ v() for v in val ]
3576221Snate@binkert.org
3586221Snate@binkert.org            cls._params.parent = sob[0]._params
3592292SN/A            cls._values.parent = sob[0]._values
3603867Sbinkertn@umich.edu
3616221Snate@binkert.org        elif len(sob) > 1:
3623867Sbinkertn@umich.edu            panic("""\
3632292SN/AThe config hierarchy only supports single inheritence of SimObject
3642292SN/Aclasses. You're trying to derive from:
3652292SN/A%s""" % str(sob))
3662292SN/A
3672292SN/A        # process param types from _init_dict, as these may be needed
3682292SN/A        # by param descriptions also in _init_dict
3692292SN/A        for key,val in cls._init_dict.items():
3702292SN/A            if isinstance(val, type) and issubclass(val, ParamType):
3712292SN/A                cls._param_types[key] = val
3722292SN/A                if not issubclass(val, ConfigNode):
3732292SN/A                    del cls._init_dict[key]
3742292SN/A
3752292SN/A        # now process remaining _init_dict items
3766221Snate@binkert.org        for key,val in cls._init_dict.items():
3776221Snate@binkert.org            # param descriptions
3782292SN/A            if isinstance(val, _Param):
3793867Sbinkertn@umich.edu                cls._new_param(key, val)
3806221Snate@binkert.org
3813867Sbinkertn@umich.edu            # init-time-only keywords
3822292SN/A            elif cls.init_keywords.has_key(key):
3832292SN/A                cls._set_keyword(key, val, cls.init_keywords[key])
3842292SN/A
3852292SN/A            # See description of decorators in the importer.py file.
3862292SN/A            # We just strip off the expr_decorator now since we don't
3872292SN/A            # need from this point on.
3882292SN/A            elif key.startswith(expr_decorator):
3892292SN/A                key = key[len(expr_decorator):]
3902292SN/A                # because it had dots into a list so that we can find the
3912292SN/A                # proper variable to modify.
3922292SN/A                key = key.split(dot_decorator)
3932292SN/A                c = cls
3946221Snate@binkert.org                for item in key[:-1]:
3956221Snate@binkert.org                    c = getattr(c, item)
3962292SN/A                setattr(c, key[-1], val)
3973867Sbinkertn@umich.edu
3986221Snate@binkert.org            # default: use normal path (ends up in __setattr__)
3993867Sbinkertn@umich.edu            else:
4002292SN/A                setattr(cls, key, val)
4012292SN/A
4022292SN/A    def _set_keyword(cls, keyword, val, kwtype):
4032292SN/A        if not isinstance(val, kwtype):
4042292SN/A            raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
4052292SN/A                  (keyword, type(val), kwtype)
4062292SN/A        if isinstance(val, types.FunctionType):
4072292SN/A            val = classmethod(val)
4082292SN/A        type.__setattr__(cls, keyword, val)
4092292SN/A
4102292SN/A    def _new_param(cls, name, value):
4112292SN/A        cls._params[name] = value
4126221Snate@binkert.org        if hasattr(value, 'default'):
4136221Snate@binkert.org            cls._values[name] = value.default
4142292SN/A        # try to resolve local param types in local param_types scope
4153867Sbinkertn@umich.edu        value.maybe_resolve_type(cls._param_types)
4166221Snate@binkert.org
4173867Sbinkertn@umich.edu    # Set attribute (called on foo.attr = value when foo is an
4182292SN/A    # instance of class cls).
4192292SN/A    def __setattr__(cls, attr, value):
4202292SN/A        # normal processing for private attributes
4212292SN/A        if attr.startswith('_'):
4222292SN/A            type.__setattr__(cls, attr, value)
4232292SN/A            return
4242292SN/A
4252292SN/A        if cls.keywords.has_key(attr):
4262292SN/A            cls._set_keyword(attr, value, cls.keywords[attr])
4272292SN/A            return
4282292SN/A
4292292SN/A        # must be SimObject param
4306221Snate@binkert.org        param = cls._params.get(attr, None)
4316221Snate@binkert.org        if param:
4322292SN/A            # It's ok: set attribute by delegating to 'object' class.
4333867Sbinkertn@umich.edu            # Note the use of param.make_value() to verify/canonicalize
4346221Snate@binkert.org            # the assigned value
4353867Sbinkertn@umich.edu            try:
4362292SN/A                param.valid(value)
4372292SN/A            except Exception, e:
4382292SN/A                panic("Exception: %s\nError setting param %s.%s to %s\n" % \
4392292SN/A                      (e, cls.__name__, attr, value))
4402292SN/A            cls._values[attr] = value
4412292SN/A        elif isConfigNode(value) or isSimObjSequence(value):
4422292SN/A            cls._values[attr] = value
4432292SN/A        else:
4442292SN/A            raise AttributeError, \
4452292SN/A                  "Class %s has no parameter %s" % (cls.__name__, attr)
4462292SN/A
4472292SN/A    def __getattr__(cls, attr):
4486221Snate@binkert.org        if cls._params.has_key(attr) or cls._values.has_key(attr):
4496221Snate@binkert.org            return Value(cls, attr)
4502292SN/A
4513867Sbinkertn@umich.edu        if attr == '_cpp_param_decl' and hasattr(cls, 'type'):
4526221Snate@binkert.org            return cls.type + '*'
4533867Sbinkertn@umich.edu
4542292SN/A        raise AttributeError, \
4552292SN/A              "object '%s' has no attribute '%s'" % (cls.__name__, attr)
4562292SN/A
4572292SN/A    def add_child(cls, instance, name, child):
4582292SN/A        if isNullPointer(child) or instance.top_child_names.has_key(name):
4592292SN/A            return
4602292SN/A
4612292SN/A        if isinstance(child, (list, tuple)):
4626221Snate@binkert.org            kid = []
4632292SN/A            for i,c in enumerate(child):
4643870Sbinkertn@umich.edu                n = '%s%d' % (name, i)
4652292SN/A                k = c.instantiate(n, instance)
4662292SN/A
4672292SN/A                instance.children.append(k)
4682292SN/A                instance.child_names[n] = k
4692292SN/A                instance.child_objects[c] = k
4702292SN/A                kid.append(k)
4712292SN/A        else:
4722292SN/A            kid = child.instantiate(name, instance)
4732292SN/A            instance.children.append(kid)
4746221Snate@binkert.org            instance.child_names[name] = kid
4756221Snate@binkert.org            instance.child_objects[child] = kid
4762292SN/A
4773867Sbinkertn@umich.edu        instance.top_child_names[name] = kid
4786221Snate@binkert.org
4793867Sbinkertn@umich.edu    # Print instance info to .ini file.
4803867Sbinkertn@umich.edu    def instantiate(cls, name, parent = None):
4812292SN/A        instance = Node(name, cls, parent, isParamContext(cls))
4822292SN/A
4832292SN/A        if hasattr(cls, 'check'):
4842292SN/A            cls.check()
4852292SN/A
4862292SN/A        for key,value in cls._values.iteritems():
4872292SN/A            if isConfigNode(value):
4882292SN/A                cls.add_child(instance, key, value)
4896221Snate@binkert.org            if isinstance(value, (list, tuple)):
4902292SN/A                vals = [ v for v in value if isConfigNode(v) ]
4912292SN/A                if len(vals):
4922292SN/A                    cls.add_child(instance, key, vals)
4933867Sbinkertn@umich.edu
4942292SN/A        for pname,param in cls._params.iteritems():
4952292SN/A            value = cls._values.get(pname, None)
4962292SN/A            if value is None:
4972292SN/A                panic('Error getting %s from %s' % (pname, name))
4982292SN/A
4992292SN/A            try:
5002292SN/A                if isConfigNode(value):
5012292SN/A                    value = instance.child_objects[value]
5022292SN/A                elif isinstance(value, (list, tuple)):
5036221Snate@binkert.org                    v = []
5046221Snate@binkert.org                    for val in value:
5052292SN/A                        if isConfigNode(val):
5063867Sbinkertn@umich.edu                            v.append(instance.child_objects[val])
5076221Snate@binkert.org                        else:
5083867Sbinkertn@umich.edu                            v.append(val)
5092292SN/A                    value = v
5102292SN/A
5112292SN/A                p = NodeParam(pname, param, value)
5122292SN/A                instance.params.append(p)
5132292SN/A                instance.param_names[pname] = p
5142292SN/A            except Exception, e:
5152292SN/A                raise e.__class__, 'Exception while evaluating %s.%s\n%s' % \
5162292SN/A                      (instance.path, pname, e)
5172292SN/A
5186221Snate@binkert.org        return instance
5192292SN/A
5202292SN/A    def _convert(cls, value):
5212292SN/A        realvalue = value
5223870Sbinkertn@umich.edu        if isinstance(value, Node):
5232292SN/A            realvalue = value.realtype
5242292SN/A
5252292SN/A        if isinstance(realvalue, Proxy):
5262292SN/A            return value
5272292SN/A
5282292SN/A        if realvalue == None or isNullPointer(realvalue):
5292292SN/A            return value
5302292SN/A
5312292SN/A        if isSubClass(realvalue, cls):
5326221Snate@binkert.org            return value
5336221Snate@binkert.org
5342292SN/A        raise TypeError, 'object %s type %s wrong type, should be %s' % \
5353867Sbinkertn@umich.edu              (repr(realvalue), realvalue, cls)
5366221Snate@binkert.org
5373867Sbinkertn@umich.edu    def _string(cls, value):
5382292SN/A        if isNullPointer(value):
5392292SN/A            return 'Null'
5402292SN/A        return Node._string(value)
5412292SN/A
5422292SN/A# The ConfigNode class is the root of the special hierarchy.  Most of
5432292SN/A# the code in this class deals with the configuration hierarchy itself
5442292SN/A# (parent/child node relationships).
5452292SN/Aclass ConfigNode(object):
5462292SN/A    # Specify metaclass.  Any class inheriting from ConfigNode will
5476221Snate@binkert.org    # get this metaclass.
5482292SN/A    __metaclass__ = MetaConfigNode
5492292SN/A
5502292SN/A    def __new__(cls, **kwargs):
5513870Sbinkertn@umich.edu        name = cls.__name__ + ("_%d" % cls._anon_subclass_counter)
5522292SN/A        cls._anon_subclass_counter += 1
5532292SN/A        return cls.__metaclass__(name, (cls, ), kwargs)
5542292SN/A
5552292SN/Aclass ParamContext(ConfigNode,ParamType):
5562292SN/A    pass
5572292SN/A
5582292SN/Aclass MetaSimObject(MetaConfigNode):
5592292SN/A    # init_keywords and keywords are inherited from MetaConfigNode,
5602292SN/A    # with overrides/additions
5616221Snate@binkert.org    init_keywords = MetaConfigNode.init_keywords
5626221Snate@binkert.org    init_keywords.update({ 'abstract' : types.BooleanType,
5632292SN/A                           'type' : types.StringType })
5643867Sbinkertn@umich.edu
5656221Snate@binkert.org    keywords = MetaConfigNode.keywords
5663867Sbinkertn@umich.edu    # no additional keywords
5672292SN/A
5682292SN/A    cpp_classes = []
5692292SN/A
5702292SN/A    # initialization
5712292SN/A    def __init__(cls, name, bases, dict):
5722292SN/A        super(MetaSimObject, cls).__init__(name, bases, dict)
5732292SN/A
5742292SN/A        if hasattr(cls, 'type'):
5752292SN/A            if name == 'SimObject':
5766221Snate@binkert.org                cls._cpp_base = None
5772292SN/A            elif hasattr(cls._bases[1], 'type'):
5783870Sbinkertn@umich.edu                cls._cpp_base = cls._bases[1].type
5792292SN/A            else:
5802292SN/A                panic("SimObject %s derives from a non-C++ SimObject %s "\
5812292SN/A                      "(no 'type')" % (cls, cls_bases[1].__name__))
5822292SN/A
5832292SN/A            # This class corresponds to a C++ class: put it on the global
5842292SN/A            # list of C++ objects to generate param structs, etc.
5852292SN/A            MetaSimObject.cpp_classes.append(cls)
5862292SN/A
5872292SN/A    def _cpp_decl(cls):
5886221Snate@binkert.org        name = cls.__name__
5896221Snate@binkert.org        code = ""
5902292SN/A        code += "\n".join([e.cpp_declare() for e in cls._param_types.values()])
5913867Sbinkertn@umich.edu        code += "\n"
5926221Snate@binkert.org        param_names = cls._params.keys()
5933867Sbinkertn@umich.edu        param_names.sort()
5945557Sktlim@umich.edu        code += "struct Params"
5955557Sktlim@umich.edu        if cls._cpp_base:
5962292SN/A            code += " : public %s::Params" % cls._cpp_base
5972292SN/A        code += " {\n    "
5985557Sktlim@umich.edu        code += "\n    ".join([cls._params[pname].cpp_decl(pname) \
5992292SN/A                               for pname in param_names])
6002292SN/A        code += "\n};\n"
6012292SN/A        return code
6022292SN/A
6032292SN/Aclass NodeParam(object):
6042292SN/A    def __init__(self, name, param, value):
6056221Snate@binkert.org        self.name = name
6066221Snate@binkert.org        self.param = param
6072292SN/A        self.ptype = param.ptype
6083867Sbinkertn@umich.edu        self.convert = param.convert
6096221Snate@binkert.org        self.string = param.string
6103867Sbinkertn@umich.edu        self.value = value
6115557Sktlim@umich.edu
6125557Sktlim@umich.educlass Node(object):
6132292SN/A    all = {}
6142292SN/A    def __init__(self, name, realtype, parent, paramcontext):
6155557Sktlim@umich.edu        self.name = name
6162292SN/A        self.realtype = realtype
6172292SN/A        if isSimObject(realtype):
6182292SN/A            self.type = realtype.type
6192292SN/A        else:
6202292SN/A            self.type = None
6212292SN/A        self.parent = parent
6226221Snate@binkert.org        self.children = []
6236221Snate@binkert.org        self.child_names = {}
6242292SN/A        self.child_objects = {}
6253867Sbinkertn@umich.edu        self.top_child_names = {}
6266221Snate@binkert.org        self.params = []
6273867Sbinkertn@umich.edu        self.param_names = {}
6282292SN/A        self.paramcontext = paramcontext
6292292SN/A
6302292SN/A        path = [ self.name ]
631        node = self.parent
632        while node is not None:
633            if node.name != 'root':
634                path.insert(0, node.name)
635            else:
636                assert(node.parent is None)
637            node = node.parent
638        self.path = '.'.join(path)
639
640    def find(self, realtype, path):
641        if not path:
642            if issubclass(self.realtype, realtype):
643                return self, True
644
645            obj = None
646            for child in self.children:
647                if issubclass(child.realtype, realtype):
648                    if obj is not None:
649                        raise AttributeError, \
650                              'parent.any matched more than one: %s %s' % \
651                              (obj.path, child.path)
652                    obj = child
653            return obj, obj is not None
654
655        try:
656            obj = self
657            for (node,index) in path[:-1]:
658                if obj.child_names.has_key(node):
659                    obj = obj.child_names[node]
660                else:
661                    obj = obj.top_child_names[node]
662                obj = Proxy.getindex(obj, index)
663
664            (last,index) = path[-1]
665            if obj.child_names.has_key(last):
666                value = obj.child_names[last]
667                return Proxy.getindex(value, index), True
668            elif obj.top_child_names.has_key(last):
669                value = obj.top_child_names[last]
670                return Proxy.getindex(value, index), True
671            elif obj.param_names.has_key(last):
672                value = obj.param_names[last]
673                realtype._convert(value.value)
674                return Proxy.getindex(value.value, index), True
675        except KeyError:
676            pass
677
678        return None, False
679
680    def unproxy(self, param, ptype):
681        if not isinstance(param, Proxy):
682            return param
683        return param.unproxy(self, ptype)
684
685    def fixup(self):
686        self.all[self.path] = self
687
688        for param in self.params:
689            ptype = param.ptype
690            pval = param.value
691
692            try:
693                if isinstance(pval, (list, tuple)):
694                    param.value = [ self.unproxy(pv, ptype) for pv in pval ]
695                else:
696                    param.value = self.unproxy(pval, ptype)
697            except Exception, e:
698                raise e.__class__, 'Error while fixing up %s:%s\n%s' % \
699                      (self.path, param.name, e)
700
701        for child in self.children:
702            assert(child != self)
703            child.fixup()
704
705    # print type and parameter values to .ini file
706    def display(self):
707        print '[' + self.path + ']'	# .ini section header
708
709        if isSimObject(self.realtype):
710            print 'type = %s' % self.type
711
712        if self.children:
713            # instantiate children in same order they were added for
714            # backward compatibility (else we can end up with cpu1
715            # before cpu0).  Changing ordering can also influence timing
716            # in the current memory system, as caches get added to a bus
717            # in different orders which affects their priority in the
718            # case of simulataneous requests.
719            self.children.sort(lambda x,y: cmp(x.name, y.name))
720            children = [ c.name for c in self.children if not c.paramcontext]
721            print 'children =', ' '.join(children)
722
723        self.params.sort(lambda x,y: cmp(x.name, y.name))
724        for param in self.params:
725            try:
726                if param.value is None:
727                    raise AttributeError, 'Parameter with no value'
728
729                value = param.convert(param.value)
730                string = param.string(value)
731            except Exception, e:
732                raise e.__class__, 'exception in %s:%s\n%s' % \
733                      (self.path, param.name, e)
734
735            print '%s = %s' % (param.name, string)
736
737        print
738
739        # recursively dump out children
740        for c in self.children:
741            c.display()
742
743    # print type and parameter values to .ini file
744    def outputDot(self, dot):
745
746
747        label = "{%s|" % self.path
748        if isSimObject(self.realtype):
749            label +=  '%s|' % self.type
750
751        if self.children:
752            # instantiate children in same order they were added for
753            # backward compatibility (else we can end up with cpu1
754            # before cpu0).
755            for c in self.children:
756                dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
757
758        simobjs = []
759        for param in self.params:
760            try:
761                if param.value is None:
762                    raise AttributeError, 'Parameter with no value'
763
764                value = param.convert(param.value)
765                string = param.string(value)
766            except Exception, e:
767                raise e.__class__, 'exception in %s:%s\n%s' % \
768                      (self.name, param.name, e)
769                raise
770            if isConfigNode(param.ptype) and string != "Null":
771                simobjs.append(string)
772            else:
773                label += '%s = %s\\n' % (param.name, string)
774
775        for so in simobjs:
776            label += "|<%s> %s" % (so, so)
777            dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
778                                    tailport="w"))
779        label += '}'
780        dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
781
782        # recursively dump out children
783        for c in self.children:
784            c.outputDot(dot)
785
786    def _string(cls, value):
787        if not isinstance(value, Node):
788            raise AttributeError, 'expecting %s got %s' % (Node, value)
789        return value.path
790    _string = classmethod(_string)
791
792#####################################################################
793#
794# Parameter description classes
795#
796# The _params dictionary in each class maps parameter names to
797# either a Param or a VectorParam object.  These objects contain the
798# parameter description string, the parameter type, and the default
799# value (loaded from the PARAM section of the .odesc files).  The
800# _convert() method on these objects is used to force whatever value
801# is assigned to the parameter to the appropriate type.
802#
803# Note that the default values are loaded into the class's attribute
804# space when the parameter dictionary is initialized (in
805# MetaConfigNode._setparams()); after that point they aren't used.
806#
807#####################################################################
808
809def isNullPointer(value):
810    return isinstance(value, NullSimObject)
811
812class Value(object):
813    def __init__(self, obj, attr):
814        super(Value, self).__setattr__('attr', attr)
815        super(Value, self).__setattr__('obj', obj)
816
817    def _getattr(self):
818        return self.obj._values.get(self.attr)
819
820    def __setattr__(self, attr, value):
821        setattr(self._getattr(), attr, value)
822
823    def __getattr__(self, attr):
824        return getattr(self._getattr(), attr)
825
826    def __getitem__(self, index):
827        return self._getattr().__getitem__(index)
828
829    def __call__(self, *args, **kwargs):
830        return self._getattr().__call__(*args, **kwargs)
831
832    def __nonzero__(self):
833        return bool(self._getattr())
834
835    def __str__(self):
836        return str(self._getattr())
837
838    def __len__(self):
839        return len(self._getattr())
840
841# Regular parameter.
842class _Param(object):
843    def __init__(self, ptype, *args, **kwargs):
844        if isinstance(ptype, types.StringType):
845            self.ptype_string = ptype
846        elif isinstance(ptype, type):
847            self.ptype = ptype
848        else:
849            raise TypeError, "Param type is not a type (%s)" % ptype
850
851        if args:
852            if len(args) == 1:
853                self.desc = args[0]
854            elif len(args) == 2:
855                self.default = args[0]
856                self.desc = args[1]
857            else:
858                raise TypeError, 'too many arguments'
859
860        if kwargs.has_key('desc'):
861            assert(not hasattr(self, 'desc'))
862            self.desc = kwargs['desc']
863            del kwargs['desc']
864
865        if kwargs.has_key('default'):
866            assert(not hasattr(self, 'default'))
867            self.default = kwargs['default']
868            del kwargs['default']
869
870        if kwargs:
871            raise TypeError, 'extra unknown kwargs %s' % kwargs
872
873        if not hasattr(self, 'desc'):
874            raise TypeError, 'desc attribute missing'
875
876    def maybe_resolve_type(self, context):
877        # check if already resolved... don't use hasattr(),
878        # as that calls __getattr__()
879        if self.__dict__.has_key('ptype'):
880            return
881        try:
882            self.ptype = context[self.ptype_string]
883        except KeyError:
884            # no harm in trying... we'll try again later using global scope
885            pass
886
887    def __getattr__(self, attr):
888        if attr == 'ptype':
889            try:
890                self.ptype = param_types[self.ptype_string]
891                return self.ptype
892            except:
893                panic("undefined Param type %s" % self.ptype_string)
894        else:
895            raise AttributeError, "'%s' object has no attribute '%s'" % \
896                  (type(self).__name__, attr)
897
898    def valid(self, value):
899        if not isinstance(value, Proxy):
900            self.ptype._convert(value)
901
902    def convert(self, value):
903        return self.ptype._convert(value)
904
905    def string(self, value):
906        return self.ptype._string(value)
907
908    def set(self, name, instance, value):
909        instance.__dict__[name] = value
910
911    def cpp_decl(self, name):
912        return '%s %s;' % (self.ptype._cpp_param_decl, name)
913
914class _ParamProxy(object):
915    def __init__(self, type):
916        self.ptype = type
917
918    # E.g., Param.Int(5, "number of widgets")
919    def __call__(self, *args, **kwargs):
920        return _Param(self.ptype, *args, **kwargs)
921
922    # Strange magic to theoretically allow dotted names as Param classes,
923    # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
924    def __getattr__(self, attr):
925        if attr == '__bases__':
926            raise AttributeError, ''
927        cls = type(self)
928        return cls(attr)
929
930    def __setattr__(self, attr, value):
931        if attr != 'ptype':
932            raise AttributeError, \
933                  'Attribute %s not available in %s' % (attr, self.__class__)
934        super(_ParamProxy, self).__setattr__(attr, value)
935
936
937Param = _ParamProxy(None)
938
939# Vector-valued parameter description.  Just like Param, except that
940# the value is a vector (list) of the specified type instead of a
941# single value.
942class _VectorParam(_Param):
943    def __init__(self, type, *args, **kwargs):
944        _Param.__init__(self, type, *args, **kwargs)
945
946    def valid(self, value):
947        if value == None:
948            return True
949
950        if isinstance(value, (list, tuple)):
951            for val in value:
952                if not isinstance(val, Proxy):
953                    self.ptype._convert(val)
954        elif not isinstance(value, Proxy):
955            self.ptype._convert(value)
956
957    # Convert assigned value to appropriate type.  If the RHS is not a
958    # list or tuple, it generates a single-element list.
959    def convert(self, value):
960        if value == None:
961            return []
962
963        if isinstance(value, (list, tuple)):
964            # list: coerce each element into new list
965            return [ self.ptype._convert(v) for v in value ]
966        else:
967            # singleton: coerce & wrap in a list
968            return self.ptype._convert(value)
969
970    def string(self, value):
971        if isinstance(value, (list, tuple)):
972            return ' '.join([ self.ptype._string(v) for v in value])
973        else:
974            return self.ptype._string(value)
975
976    def cpp_decl(self, name):
977        return 'std::vector<%s> %s;' % (self.ptype._cpp_param_decl, name)
978
979class _VectorParamProxy(_ParamProxy):
980    # E.g., VectorParam.Int(5, "number of widgets")
981    def __call__(self, *args, **kwargs):
982        return _VectorParam(self.ptype, *args, **kwargs)
983
984VectorParam = _VectorParamProxy(None)
985
986#####################################################################
987#
988# Parameter Types
989#
990# Though native Python types could be used to specify parameter types
991# (the 'ptype' field of the Param and VectorParam classes), it's more
992# flexible to define our own set of types.  This gives us more control
993# over how Python expressions are converted to values (via the
994# __init__() constructor) and how these values are printed out (via
995# the __str__() conversion method).  Eventually we'll need these types
996# to correspond to distinct C++ types as well.
997#
998#####################################################################
999
1000
1001# Metaclass for bounds-checked integer parameters.  See CheckedInt.
1002class CheckedIntType(type):
1003    def __init__(cls, name, bases, dict):
1004        super(CheckedIntType, cls).__init__(name, bases, dict)
1005
1006        # CheckedInt is an abstract base class, so we actually don't
1007        # want to do any processing on it... the rest of this code is
1008        # just for classes that derive from CheckedInt.
1009        if name == 'CheckedInt':
1010            return
1011
1012        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1013            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1014                panic("CheckedInt subclass %s must define either\n" \
1015                      "    'min' and 'max' or 'size' and 'unsigned'\n" \
1016                      % name);
1017            if cls.unsigned:
1018                cls.min = 0
1019                cls.max = 2 ** cls.size - 1
1020            else:
1021                cls.min = -(2 ** (cls.size - 1))
1022                cls.max = (2 ** (cls.size - 1)) - 1
1023
1024        cls._cpp_param_decl = cls.cppname
1025
1026    def _convert(cls, value):
1027        if isinstance(value, bool):
1028            return int(value)
1029
1030        if not isinstance(value, (int, long, float, str)):
1031            raise TypeError, 'Integer param of invalid type %s' % type(value)
1032
1033        if isinstance(value, (str, float)):
1034            value = long(float(value))
1035
1036        if not cls.min <= value <= cls.max:
1037            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1038                  (cls.min, value, cls.max)
1039
1040        return value
1041
1042    def _string(cls, value):
1043        return str(value)
1044
1045# Abstract superclass for bounds-checked integer parameters.  This
1046# class is subclassed to generate parameter classes with specific
1047# bounds.  Initialization of the min and max bounds is done in the
1048# metaclass CheckedIntType.__init__.
1049class CheckedInt(ParamType):
1050    __metaclass__ = CheckedIntType
1051
1052class Int(CheckedInt):      cppname = 'int';      size = 32; unsigned = False
1053class Unsigned(CheckedInt): cppname = 'unsigned'; size = 32; unsigned = True
1054
1055class Int8(CheckedInt):     cppname =  'int8_t';  size =  8; unsigned = False
1056class UInt8(CheckedInt):    cppname = 'uint8_t';  size =  8; unsigned = True
1057class Int16(CheckedInt):    cppname =  'int16_t'; size = 16; unsigned = False
1058class UInt16(CheckedInt):   cppname = 'uint16_t'; size = 16; unsigned = True
1059class Int32(CheckedInt):    cppname =  'int32_t'; size = 32; unsigned = False
1060class UInt32(CheckedInt):   cppname = 'uint32_t'; size = 32; unsigned = True
1061class Int64(CheckedInt):    cppname =  'int64_t'; size = 64; unsigned = False
1062class UInt64(CheckedInt):   cppname = 'uint64_t'; size = 64; unsigned = True
1063
1064class Counter(CheckedInt): cppname = 'Counter'; size = 64; unsigned = True
1065class Addr(CheckedInt):    cppname = 'Addr';    size = 64; unsigned = True
1066class Tick(CheckedInt):    cppname = 'Tick';    size = 64; unsigned = True
1067
1068class Percent(CheckedInt): cppname = 'int'; min = 0; max = 100
1069
1070class Pair(object):
1071    def __init__(self, first, second):
1072        self.first = first
1073        self.second = second
1074
1075class MetaRange(type):
1076    def __init__(cls, name, bases, dict):
1077        super(MetaRange, cls).__init__(name, bases, dict)
1078        if name == 'Range':
1079            return
1080        cls._cpp_param_decl = 'Range<%s>' % cls.type._cpp_param_decl
1081
1082    def _convert(cls, value):
1083        if not isinstance(value, Pair):
1084            raise TypeError, 'value %s is not a Pair' % value
1085        return Pair(cls.type._convert(value.first),
1086                    cls.type._convert(value.second))
1087
1088    def _string(cls, value):
1089        return '%s:%s' % (cls.type._string(value.first),
1090                          cls.type._string(value.second))
1091
1092class Range(ParamType):
1093    __metaclass__ = MetaRange
1094
1095def RangeSize(start, size):
1096    return Pair(start, start + size - 1)
1097
1098class AddrRange(Range): type = Addr
1099
1100# Boolean parameter type.
1101class Bool(ParamType):
1102    _cpp_param_decl = 'bool'
1103    def _convert(value):
1104        t = type(value)
1105        if t == bool:
1106            return value
1107
1108        if t == int or t == long:
1109            return bool(value)
1110
1111        if t == str:
1112            v = value.lower()
1113            if v == "true" or v == "t" or v == "yes" or v == "y":
1114                return True
1115            elif v == "false" or v == "f" or v == "no" or v == "n":
1116                return False
1117
1118        raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v, t)
1119    _convert = staticmethod(_convert)
1120
1121    def _string(value):
1122        if value:
1123            return "true"
1124        else:
1125            return "false"
1126    _string = staticmethod(_string)
1127
1128# String-valued parameter.
1129class String(ParamType):
1130    _cpp_param_decl = 'string'
1131
1132    # Constructor.  Value must be Python string.
1133    def _convert(cls,value):
1134        if value is None:
1135            return ''
1136        if isinstance(value, str):
1137            return value
1138
1139        raise TypeError, \
1140              "String param got value %s %s" % (repr(value), type(value))
1141    _convert = classmethod(_convert)
1142
1143    # Generate printable string version.  Not too tricky.
1144    def _string(cls, value):
1145        return value
1146    _string = classmethod(_string)
1147
1148
1149def IncEthernetAddr(addr, val = 1):
1150    bytes = map(lambda x: int(x, 16), addr.split(':'))
1151    bytes[5] += val
1152    for i in (5, 4, 3, 2, 1):
1153        val,rem = divmod(bytes[i], 256)
1154        bytes[i] = rem
1155        if val == 0:
1156            break
1157        bytes[i - 1] += val
1158    assert(bytes[0] <= 255)
1159    return ':'.join(map(lambda x: '%02x' % x, bytes))
1160
1161class NextEthernetAddr(object):
1162    __metaclass__ = Singleton
1163    addr = "00:90:00:00:00:01"
1164
1165    def __init__(self, inc = 1):
1166        self.value = self.addr
1167        self.addr = IncEthernetAddr(self.addr, inc)
1168
1169class EthernetAddr(ParamType):
1170    _cpp_param_decl = 'EthAddr'
1171
1172    def _convert(cls, value):
1173        if value == NextEthernetAddr:
1174            return value
1175
1176        if not isinstance(value, str):
1177            raise TypeError, "expected an ethernet address and didn't get one"
1178
1179        bytes = value.split(':')
1180        if len(bytes) != 6:
1181            raise TypeError, 'invalid ethernet address %s' % value
1182
1183        for byte in bytes:
1184            if not 0 <= int(byte) <= 256:
1185                raise TypeError, 'invalid ethernet address %s' % value
1186
1187        return value
1188    _convert = classmethod(_convert)
1189
1190    def _string(cls, value):
1191        if value == NextEthernetAddr:
1192            value = value().value
1193        return value
1194    _string = classmethod(_string)
1195
1196# Special class for NULL pointers.  Note the special check in
1197# make_param_value() above that lets these be assigned where a
1198# SimObject is required.
1199# only one copy of a particular node
1200class NullSimObject(object):
1201    __metaclass__ = Singleton
1202
1203    def __call__(cls):
1204        return cls
1205
1206    def _instantiate(self, parent = None, path = ''):
1207        pass
1208
1209    def _convert(cls, value):
1210        if value == Nxone:
1211            return
1212
1213        if isinstance(value, cls):
1214            return value
1215
1216        raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1217              (repr(value), type(value), cls)
1218    _convert = classmethod(_convert)
1219
1220    def _string():
1221        return 'NULL'
1222    _string = staticmethod(_string)
1223
1224# The only instance you'll ever need...
1225Null = NULL = NullSimObject()
1226
1227# Enumerated types are a little more complex.  The user specifies the
1228# type as Enum(foo) where foo is either a list or dictionary of
1229# alternatives (typically strings, but not necessarily so).  (In the
1230# long run, the integer value of the parameter will be the list index
1231# or the corresponding dictionary value.  For now, since we only check
1232# that the alternative is valid and then spit it into a .ini file,
1233# there's not much point in using the dictionary.)
1234
1235# What Enum() must do is generate a new type encapsulating the
1236# provided list/dictionary so that specific values of the parameter
1237# can be instances of that type.  We define two hidden internal
1238# classes (_ListEnum and _DictEnum) to serve as base classes, then
1239# derive the new type from the appropriate base class on the fly.
1240
1241
1242# Metaclass for Enum types
1243class MetaEnum(type):
1244
1245    def __init__(cls, name, bases, init_dict):
1246        if init_dict.has_key('map'):
1247            if not isinstance(cls.map, dict):
1248                raise TypeError, "Enum-derived class attribute 'map' " \
1249                      "must be of type dict"
1250            # build list of value strings from map
1251            cls.vals = cls.map.keys()
1252            cls.vals.sort()
1253        elif init_dict.has_key('vals'):
1254            if not isinstance(cls.vals, list):
1255                raise TypeError, "Enum-derived class attribute 'vals' " \
1256                      "must be of type list"
1257            # build string->value map from vals sequence
1258            cls.map = {}
1259            for idx,val in enumerate(cls.vals):
1260                cls.map[val] = idx
1261        else:
1262            raise TypeError, "Enum-derived class must define "\
1263                  "attribute 'map' or 'vals'"
1264
1265        cls._cpp_param_decl = name
1266
1267        super(MetaEnum, cls).__init__(name, bases, init_dict)
1268
1269    def cpp_declare(cls):
1270        s = 'enum %s {\n    ' % cls.__name__
1271        s += ',\n    '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1272        s += '\n};\n'
1273        return s
1274
1275# Base class for enum types.
1276class Enum(ParamType):
1277    __metaclass__ = MetaEnum
1278    vals = []
1279
1280    def _convert(self, value):
1281        if value not in self.map:
1282            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1283                  % (value, self.vals)
1284        return value
1285    _convert = classmethod(_convert)
1286
1287    # Generate printable string version of value.
1288    def _string(self, value):
1289        return str(value)
1290    _string = classmethod(_string)
1291#
1292# "Constants"... handy aliases for various values.
1293#
1294
1295# Some memory range specifications use this as a default upper bound.
1296MAX_ADDR = Addr.max
1297MaxTick = Tick.max
1298
1299# For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1300K = 1024
1301M = K*K
1302G = K*M
1303
1304#####################################################################
1305
1306# The final hook to generate .ini files.  Called from configuration
1307# script once config is built.
1308def instantiate(root):
1309    instance = root.instantiate('root')
1310    instance.fixup()
1311    instance.display()
1312    if not noDot:
1313       dot = pydot.Dot()
1314       instance.outputDot(dot)
1315       dot.orientation = "portrait"
1316       dot.size = "8.5,11"
1317       dot.ranksep="equally"
1318       dot.rank="samerank"
1319       dot.write("config.dot")
1320       dot.write_ps("config.ps")
1321
1322# SimObject is a minimal extension of ConfigNode, implementing a
1323# hierarchy node that corresponds to an M5 SimObject.  It prints out a
1324# "type=" line to indicate its SimObject class, prints out the
1325# assigned parameters corresponding to its class, and allows
1326# parameters to be set by keyword in the constructor.  Note that most
1327# of the heavy lifting for the SimObject param handling is done in the
1328# MetaConfigNode metaclass.
1329class SimObject(ConfigNode, ParamType):
1330    __metaclass__ = MetaSimObject
1331    type = 'SimObject'
1332
1333
1334# __all__ defines the list of symbols that get exported when
1335# 'from config import *' is invoked.  Try to keep this reasonably
1336# short to avoid polluting other namespaces.
1337__all__ = ['ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam',
1338           'parent', 'Enum',
1339           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1340           'Int32', 'UInt32', 'Int64', 'UInt64',
1341           'Counter', 'Addr', 'Tick', 'Percent',
1342           'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M',
1343           'NextEthernetAddr',
1344           'instantiate']
1345