SimObject.py revision 1605
110448Snilay@cs.wisc.edu# Copyright (c) 2004 The Regents of The University of Michigan
210448Snilay@cs.wisc.edu# All rights reserved.
310448Snilay@cs.wisc.edu#
410448Snilay@cs.wisc.edu# Redistribution and use in source and binary forms, with or without
510448Snilay@cs.wisc.edu# modification, are permitted provided that the following conditions are
610448Snilay@cs.wisc.edu# met: redistributions of source code must retain the above copyright
710448Snilay@cs.wisc.edu# notice, this list of conditions and the following disclaimer;
810448Snilay@cs.wisc.edu# redistributions in binary form must reproduce the above copyright
910448Snilay@cs.wisc.edu# notice, this list of conditions and the following disclaimer in the
1010448Snilay@cs.wisc.edu# documentation and/or other materials provided with the distribution;
1110448Snilay@cs.wisc.edu# neither the name of the copyright holders nor the names of its
1210448Snilay@cs.wisc.edu# contributors may be used to endorse or promote products derived from
1310448Snilay@cs.wisc.edu# this software without specific prior written permission.
1410448Snilay@cs.wisc.edu#
1510448Snilay@cs.wisc.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1610448Snilay@cs.wisc.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1710448Snilay@cs.wisc.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1810448Snilay@cs.wisc.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1910448Snilay@cs.wisc.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2010448Snilay@cs.wisc.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2110448Snilay@cs.wisc.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2210448Snilay@cs.wisc.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2310448Snilay@cs.wisc.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2410448Snilay@cs.wisc.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2510448Snilay@cs.wisc.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2610448Snilay@cs.wisc.edu
2710448Snilay@cs.wisc.edufrom __future__ import generators
2810448Snilay@cs.wisc.eduimport os, re, sys, types, inspect
2910448Snilay@cs.wisc.edu
3010448Snilay@cs.wisc.edufrom m5 import panic, env
3110448Snilay@cs.wisc.edufrom convert import *
3210448Snilay@cs.wisc.edufrom multidict import multidict
3310448Snilay@cs.wisc.edu
3410448Snilay@cs.wisc.edunoDot = False
3510448Snilay@cs.wisc.edutry:
3610448Snilay@cs.wisc.edu    import pydot
3710448Snilay@cs.wisc.eduexcept:
3810448Snilay@cs.wisc.edu    noDot = True
3910448Snilay@cs.wisc.edu
4010448Snilay@cs.wisc.educlass Singleton(type):
4110448Snilay@cs.wisc.edu    def __call__(cls, *args, **kwargs):
4210448Snilay@cs.wisc.edu        if hasattr(cls, '_instance'):
4310448Snilay@cs.wisc.edu            return cls._instance
4410448Snilay@cs.wisc.edu
4510448Snilay@cs.wisc.edu        cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
4610448Snilay@cs.wisc.edu        return cls._instance
4710448Snilay@cs.wisc.edu
4810448Snilay@cs.wisc.edu#####################################################################
4910448Snilay@cs.wisc.edu#
5010448Snilay@cs.wisc.edu# M5 Python Configuration Utility
5110448Snilay@cs.wisc.edu#
5210448Snilay@cs.wisc.edu# The basic idea is to write simple Python programs that build Python
5310448Snilay@cs.wisc.edu# objects corresponding to M5 SimObjects for the deisred simulation
5410448Snilay@cs.wisc.edu# configuration.  For now, the Python emits a .ini file that can be
5510448Snilay@cs.wisc.edu# parsed by M5.  In the future, some tighter integration between M5
5610448Snilay@cs.wisc.edu# and the Python interpreter may allow bypassing the .ini file.
5710448Snilay@cs.wisc.edu#
5810448Snilay@cs.wisc.edu# Each SimObject class in M5 is represented by a Python class with the
5910448Snilay@cs.wisc.edu# same name.  The Python inheritance tree mirrors the M5 C++ tree
6010448Snilay@cs.wisc.edu# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
6110448Snilay@cs.wisc.edu# SimObjects inherit from a single SimObject base class).  To specify
6210448Snilay@cs.wisc.edu# an instance of an M5 SimObject in a configuration, the user simply
6310448Snilay@cs.wisc.edu# instantiates the corresponding Python object.  The parameters for
6410448Snilay@cs.wisc.edu# that SimObject are given by assigning to attributes of the Python
6510448Snilay@cs.wisc.edu# object, either using keyword assignment in the constructor or in
6610448Snilay@cs.wisc.edu# separate assignment statements.  For example:
6710448Snilay@cs.wisc.edu#
6810448Snilay@cs.wisc.edu# cache = BaseCache('my_cache', root, size='64KB')
6910448Snilay@cs.wisc.edu# cache.hit_latency = 3
7010448Snilay@cs.wisc.edu# cache.assoc = 8
7110448Snilay@cs.wisc.edu#
7210448Snilay@cs.wisc.edu# (The first two constructor arguments specify the name of the created
7310448Snilay@cs.wisc.edu# cache and its parent node in the hierarchy.)
7410448Snilay@cs.wisc.edu#
7510448Snilay@cs.wisc.edu# The magic lies in the mapping of the Python attributes for SimObject
7610448Snilay@cs.wisc.edu# classes to the actual SimObject parameter specifications.  This
7710448Snilay@cs.wisc.edu# allows parameter validity checking in the Python code.  Continuing
7810448Snilay@cs.wisc.edu# the example above, the statements "cache.blurfl=3" or
7910448Snilay@cs.wisc.edu# "cache.assoc='hello'" would both result in runtime errors in Python,
8010448Snilay@cs.wisc.edu# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
8110448Snilay@cs.wisc.edu# parameter requires an integer, respectively.  This magic is done
8210448Snilay@cs.wisc.edu# primarily by overriding the special __setattr__ method that controls
8310448Snilay@cs.wisc.edu# assignment to object attributes.
8410448Snilay@cs.wisc.edu#
8510448Snilay@cs.wisc.edu# The Python module provides another class, ConfigNode, which is a
8610448Snilay@cs.wisc.edu# superclass of SimObject.  ConfigNode implements the parent/child
8710448Snilay@cs.wisc.edu# relationship for building the configuration hierarchy tree.
8810448Snilay@cs.wisc.edu# Concrete instances of ConfigNode can be used to group objects in the
8910448Snilay@cs.wisc.edu# hierarchy, but do not correspond to SimObjects themselves (like a
9010448Snilay@cs.wisc.edu# .ini section with "children=" but no "type=".
9110448Snilay@cs.wisc.edu#
9210448Snilay@cs.wisc.edu# Once a set of Python objects have been instantiated in a hierarchy,
9310448Snilay@cs.wisc.edu# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
9410448Snilay@cs.wisc.edu# will generate a .ini file.  See simple-4cpu.py for an example
9510448Snilay@cs.wisc.edu# (corresponding to m5-test/simple-4cpu.ini).
9610448Snilay@cs.wisc.edu#
9710448Snilay@cs.wisc.edu#####################################################################
9810448Snilay@cs.wisc.edu
9910448Snilay@cs.wisc.edu#####################################################################
10010448Snilay@cs.wisc.edu#
10110448Snilay@cs.wisc.edu# ConfigNode/SimObject classes
10210448Snilay@cs.wisc.edu#
10310448Snilay@cs.wisc.edu# The Python class hierarchy rooted by ConfigNode (which is the base
10410448Snilay@cs.wisc.edu# class of SimObject, which in turn is the base class of all other M5
10510448Snilay@cs.wisc.edu# SimObject classes) has special attribute behavior.  In general, an
10610448Snilay@cs.wisc.edu# object in this hierarchy has three categories of attribute-like
10710448Snilay@cs.wisc.edu# things:
10810448Snilay@cs.wisc.edu#
10910448Snilay@cs.wisc.edu# 1. Regular Python methods and variables.  These must start with an
11010448Snilay@cs.wisc.edu# underscore to be treated normally.
11110448Snilay@cs.wisc.edu#
11210448Snilay@cs.wisc.edu# 2. SimObject parameters.  These values are stored as normal Python
11310448Snilay@cs.wisc.edu# attributes, but all assignments to these attributes are checked
11410448Snilay@cs.wisc.edu# against the pre-defined set of parameters stored in the class's
11510448Snilay@cs.wisc.edu# _params dictionary.  Assignments to attributes that do not
11610448Snilay@cs.wisc.edu# correspond to predefined parameters, or that are not of the correct
11710448Snilay@cs.wisc.edu# type, incur runtime errors.
11810448Snilay@cs.wisc.edu#
11910448Snilay@cs.wisc.edu# 3. Hierarchy children.  The child nodes of a ConfigNode are stored
12010448Snilay@cs.wisc.edu# in the node's _children dictionary, but can be accessed using the
12110448Snilay@cs.wisc.edu# Python attribute dot-notation (just as they are printed out by the
12210448Snilay@cs.wisc.edu# simulator).  Children cannot be created using attribute assigment;
12310448Snilay@cs.wisc.edu# they must be added by specifying the parent node in the child's
12410448Snilay@cs.wisc.edu# constructor or using the '+=' operator.
12510448Snilay@cs.wisc.edu
12610448Snilay@cs.wisc.edu# The SimObject parameters are the most complex, for a few reasons.
12710448Snilay@cs.wisc.edu# First, both parameter descriptions and parameter values are
12810448Snilay@cs.wisc.edu# inherited.  Thus parameter description lookup must go up the
12910448Snilay@cs.wisc.edu# inheritance chain like normal attribute lookup, but this behavior
13010448Snilay@cs.wisc.edu# must be explicitly coded since the lookup occurs in each class's
13110448Snilay@cs.wisc.edu# _params attribute.  Second, because parameter values can be set
13210448Snilay@cs.wisc.edu# on SimObject classes (to implement default values), the parameter
13310448Snilay@cs.wisc.edu# checking behavior must be enforced on class attribute assignments as
13410448Snilay@cs.wisc.edu# well as instance attribute assignments.  Finally, because we allow
13510448Snilay@cs.wisc.edu# class specialization via inheritance (e.g., see the L1Cache class in
13610448Snilay@cs.wisc.edu# the simple-4cpu.py example), we must do parameter checking even on
13710448Snilay@cs.wisc.edu# class instantiation.  To provide all these features, we use a
13810448Snilay@cs.wisc.edu# metaclass to define most of the SimObject parameter behavior for
13910448Snilay@cs.wisc.edu# this class hierarchy.
14010448Snilay@cs.wisc.edu#
14110448Snilay@cs.wisc.edu#####################################################################
14210448Snilay@cs.wisc.edu
14310448Snilay@cs.wisc.educlass Proxy(object):
14410448Snilay@cs.wisc.edu    def __init__(self, path):
14510448Snilay@cs.wisc.edu        self._object = None
14610448Snilay@cs.wisc.edu        if path == 'any':
14710448Snilay@cs.wisc.edu            self._path = None
14810448Snilay@cs.wisc.edu        else:
14910448Snilay@cs.wisc.edu            # path is a list of (attr,index) tuples
15010448Snilay@cs.wisc.edu            self._path = [(path,None)]
15110448Snilay@cs.wisc.edu        self._index = None
15210448Snilay@cs.wisc.edu        self._multiplier = None
15310448Snilay@cs.wisc.edu
15410448Snilay@cs.wisc.edu    def __getattr__(self, attr):
15510448Snilay@cs.wisc.edu        # python uses __bases__ internally for inheritance
15610448Snilay@cs.wisc.edu        if attr == '__bases__':
15710448Snilay@cs.wisc.edu            return super(Proxy, self).__getattr__(self, attr)
15810448Snilay@cs.wisc.edu        if (self._path == None):
15910448Snilay@cs.wisc.edu            panic("Can't add attributes to 'any' proxy")
16010448Snilay@cs.wisc.edu        self._path.append((attr,None))
16110448Snilay@cs.wisc.edu        return self
16210448Snilay@cs.wisc.edu
16310448Snilay@cs.wisc.edu    def __setattr__(self, attr, value):
16410448Snilay@cs.wisc.edu        if not attr.startswith('_'):
16510448Snilay@cs.wisc.edu            raise AttributeError, 'cannot set attribute %s' % attr
16610448Snilay@cs.wisc.edu        super(Proxy, self).__setattr__(attr, value)
16710448Snilay@cs.wisc.edu
16810448Snilay@cs.wisc.edu    # support indexing on proxies (e.g., parent.cpu[0])
16910448Snilay@cs.wisc.edu    def __getitem__(self, key):
17010448Snilay@cs.wisc.edu        if not isinstance(key, int):
17110448Snilay@cs.wisc.edu            raise TypeError, "Proxy object requires integer index"
17210448Snilay@cs.wisc.edu        if self._path == None:
17310448Snilay@cs.wisc.edu            raise IndexError, "Index applied to 'any' proxy"
17410448Snilay@cs.wisc.edu        # replace index portion of last path element with new index
17510448Snilay@cs.wisc.edu        self._path[-1] = (self._path[-1][0], key)
17610448Snilay@cs.wisc.edu        return self
17710448Snilay@cs.wisc.edu
17810448Snilay@cs.wisc.edu    # support multiplying proxies by constants
17910448Snilay@cs.wisc.edu    def __mul__(self, other):
18010448Snilay@cs.wisc.edu        if not isinstance(other, int):
18110448Snilay@cs.wisc.edu            raise TypeError, "Proxy multiplier must be integer"
18210448Snilay@cs.wisc.edu        if self._multiplier == None:
18310448Snilay@cs.wisc.edu            self._multiplier = other
18410448Snilay@cs.wisc.edu        else:
18510448Snilay@cs.wisc.edu            # support chained multipliers
18610448Snilay@cs.wisc.edu            self._multiplier *= other
18710448Snilay@cs.wisc.edu        return self
18810448Snilay@cs.wisc.edu
18910448Snilay@cs.wisc.edu    def _mulcheck(self, result):
19010448Snilay@cs.wisc.edu        if self._multiplier == None:
19110448Snilay@cs.wisc.edu            return result
19210448Snilay@cs.wisc.edu        if not isinstance(result, int):
19310448Snilay@cs.wisc.edu            raise TypeError, "Proxy with multiplier resolves to " \
19410448Snilay@cs.wisc.edu                  "non-integer value"
19510448Snilay@cs.wisc.edu        return result * self._multiplier
19610448Snilay@cs.wisc.edu
19710448Snilay@cs.wisc.edu    def unproxy(self, base, ptype):
19810448Snilay@cs.wisc.edu        obj = base
19910448Snilay@cs.wisc.edu        done = False
20010448Snilay@cs.wisc.edu        while not done:
20110448Snilay@cs.wisc.edu            if obj is None:
20210448Snilay@cs.wisc.edu                raise AttributeError, \
20310448Snilay@cs.wisc.edu                      'Parent of %s type %s not found at path %s' \
20410448Snilay@cs.wisc.edu                      % (base.name, ptype, self._path)
20510448Snilay@cs.wisc.edu
20610448Snilay@cs.wisc.edu            result, done = obj.find(ptype, self._path)
20710448Snilay@cs.wisc.edu            obj = obj.parent
20810448Snilay@cs.wisc.edu
20910448Snilay@cs.wisc.edu        if isinstance(result, Proxy):
21010448Snilay@cs.wisc.edu            result = result.unproxy(obj, ptype)
21110448Snilay@cs.wisc.edu
21210448Snilay@cs.wisc.edu        return self._mulcheck(result)
21310448Snilay@cs.wisc.edu
214    def getindex(obj, index):
215        if index == None:
216            return obj
217        try:
218            obj = obj[index]
219        except TypeError:
220            if index != 0:
221                raise
222            # if index is 0 and item is not subscriptable, just
223            # use item itself (so cpu[0] works on uniprocessors)
224        return obj
225    getindex = staticmethod(getindex)
226
227class ProxyFactory(object):
228    def __getattr__(self, attr):
229        return Proxy(attr)
230
231# global object for handling parent.foo proxies
232parent = ProxyFactory()
233
234def isSubClass(value, cls):
235    try:
236        return issubclass(value, cls)
237    except:
238        return False
239
240def isConfigNode(value):
241    try:
242        return issubclass(value, ConfigNode)
243    except:
244        return False
245
246def isSimObject(value):
247    try:
248        return issubclass(value, SimObject)
249    except:
250        return False
251
252def isSimObjSequence(value):
253    if not isinstance(value, (list, tuple)):
254        return False
255
256    for val in value:
257        if not isNullPointer(val) and not isConfigNode(val):
258            return False
259
260    return True
261
262def isParamContext(value):
263    try:
264        return issubclass(value, ParamContext)
265    except:
266        return False
267
268
269class_decorator = 'M5M5_SIMOBJECT_'
270expr_decorator = 'M5M5_EXPRESSION_'
271dot_decorator = '_M5M5_DOT_'
272
273# 'Global' map of legitimate types for SimObject parameters.
274param_types = {}
275
276# Dummy base class to identify types that are legitimate for SimObject
277# parameters.
278class ParamType(object):
279    pass
280
281# Add types defined in given context (dict or module) that are derived
282# from ParamType to param_types map.
283def add_param_types(ctx):
284    if isinstance(ctx, types.DictType):
285        source_dict = ctx
286    elif isinstance(ctx, types.ModuleType):
287        source_dict = ctx.__dict__
288    else:
289        raise TypeError, \
290              "m5.config.add_param_types requires dict or module as arg"
291    for key,val in source_dict.iteritems():
292        if isinstance(val, type) and issubclass(val, ParamType):
293            param_types[key] = val
294
295# The metaclass for ConfigNode (and thus for everything that derives
296# from ConfigNode, including SimObject).  This class controls how new
297# classes that derive from ConfigNode are instantiated, and provides
298# inherited class behavior (just like a class controls how instances
299# of that class are instantiated, and provides inherited instance
300# behavior).
301class MetaConfigNode(type):
302    # Attributes that can be set only at initialization time
303    init_keywords = {}
304    # Attributes that can be set any time
305    keywords = { 'check' : types.FunctionType,
306                 'children' : types.ListType }
307
308    # __new__ is called before __init__, and is where the statements
309    # in the body of the class definition get loaded into the class's
310    # __dict__.  We intercept this to filter out parameter assignments
311    # and only allow "private" attributes to be passed to the base
312    # __new__ (starting with underscore).
313    def __new__(mcls, name, bases, dict):
314        # Copy "private" attributes (including special methods such as __new__)
315        # to the official dict.  Everything else goes in _init_dict to be
316        # filtered in __init__.
317        cls_dict = {}
318        for key,val in dict.items():
319            if key.startswith('_'):
320                cls_dict[key] = val
321                del dict[key]
322        cls_dict['_init_dict'] = dict
323        return super(MetaConfigNode, mcls).__new__(mcls, name, bases, cls_dict)
324
325    # initialization
326    def __init__(cls, name, bases, dict):
327        super(MetaConfigNode, cls).__init__(name, bases, dict)
328
329        # initialize required attributes
330        cls._params = multidict()
331        cls._values = multidict()
332        cls._param_types = {}
333        cls._bases = [c for c in cls.__mro__ if isConfigNode(c)]
334        cls._anon_subclass_counter = 0
335
336        # We don't support multiple inheritence.  If you want to, you
337        # must fix multidict to deal with it properly.
338        cnbase = [ base for base in bases if isConfigNode(base) ]
339        if len(cnbase) == 1:
340            # If your parent has a value in it that's a config node, clone
341            # it.  Do this now so if we update any of the values'
342            # attributes we are updating the clone and not the original.
343            for key,val in cnbase[0]._values.iteritems():
344
345                # don't clone if (1) we're about to overwrite it with
346                # a local setting or (2) we've already cloned a copy
347                # from an earlier (more derived) base
348                if cls._init_dict.has_key(key) or cls._values.has_key(key):
349                    continue
350
351                if isConfigNode(val):
352                    cls._values[key] = val()
353                elif isSimObjSequence(val) and len(val):
354                    cls._values[key] = [ v() for v in val ]
355
356            cls._params.parent = cnbase[0]._params
357            cls._values.parent = cnbase[0]._values
358
359        elif len(cnbase) > 1:
360            panic("""\
361The config hierarchy only supports single inheritence of SimObject
362classes. You're trying to derive from:
363%s""" % str(cnbase))
364
365        # process param types from _init_dict, as these may be needed
366        # by param descriptions also in _init_dict
367        for key,val in cls._init_dict.items():
368            if isinstance(val, type) and issubclass(val, ParamType):
369                cls._param_types[key] = val
370                if not issubclass(val, ConfigNode):
371                    del cls._init_dict[key]
372
373        # now process remaining _init_dict items
374        for key,val in cls._init_dict.items():
375            # param descriptions
376            if isinstance(val, ParamBase):
377                cls._new_param(key, val)
378
379            # init-time-only keywords
380            elif cls.init_keywords.has_key(key):
381                cls._set_keyword(key, val, cls.init_keywords[key])
382
383            # See description of decorators in the importer.py file.
384            # We just strip off the expr_decorator now since we don't
385            # need from this point on.
386            elif key.startswith(expr_decorator):
387                key = key[len(expr_decorator):]
388                # because it had dots into a list so that we can find the
389                # proper variable to modify.
390                key = key.split(dot_decorator)
391                c = cls
392                for item in key[:-1]:
393                    c = getattr(c, item)
394                setattr(c, key[-1], val)
395
396            # default: use normal path (ends up in __setattr__)
397            else:
398                setattr(cls, key, val)
399
400    def _set_keyword(cls, keyword, val, kwtype):
401        if not isinstance(val, kwtype):
402            raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
403                  (keyword, type(val), kwtype)
404        if isinstance(val, types.FunctionType):
405            val = classmethod(val)
406        type.__setattr__(cls, keyword, val)
407
408    def _new_param(cls, name, value):
409        cls._params[name] = value
410        if hasattr(value, 'default'):
411            cls._values[name] = value.default
412        # try to resolve local param types in local param_types scope
413        value.maybe_resolve_type(cls._param_types)
414
415    # Set attribute (called on foo.attr = value when foo is an
416    # instance of class cls).
417    def __setattr__(cls, attr, value):
418        # normal processing for private attributes
419        if attr.startswith('_'):
420            type.__setattr__(cls, attr, value)
421            return
422
423        if cls.keywords.has_key(attr):
424            cls._set_keyword(attr, value, cls.keywords[attr])
425            return
426
427        # must be SimObject param
428        param = cls._params.get(attr, None)
429        if param:
430            # It's ok: set attribute by delegating to 'object' class.
431            # Note the use of param.make_value() to verify/canonicalize
432            # the assigned value
433            try:
434                param.valid(value)
435            except Exception, e:
436                msg = "%s\nError setting param %s.%s to %s\n" % \
437                      (e, cls.__name__, attr, value)
438                e.args = (msg, )
439                raise
440            cls._values[attr] = value
441        elif isConfigNode(value) or isSimObjSequence(value):
442            cls._values[attr] = value
443        else:
444            raise AttributeError, \
445                  "Class %s has no parameter %s" % (cls.__name__, attr)
446
447    def __getattr__(cls, attr):
448        if cls._params.has_key(attr) or cls._values.has_key(attr):
449            return Value(cls, attr)
450
451        if attr == '_cpp_param_decl' and hasattr(cls, 'type'):
452            return cls.type + '*'
453
454        raise AttributeError, \
455              "object '%s' has no attribute '%s'" % (cls.__name__, attr)
456
457    def add_child(cls, instance, name, child):
458        if isNullPointer(child) or instance.top_child_names.has_key(name):
459            return
460
461        if isinstance(child, (list, tuple)):
462            kid = []
463            for i,c in enumerate(child):
464                n = '%s%d' % (name, i)
465                k = c.instantiate(n, instance)
466
467                instance.children.append(k)
468                instance.child_names[n] = k
469                instance.child_objects[c] = k
470                kid.append(k)
471        else:
472            kid = child.instantiate(name, instance)
473            instance.children.append(kid)
474            instance.child_names[name] = kid
475            instance.child_objects[child] = kid
476
477        instance.top_child_names[name] = kid
478
479    # Print instance info to .ini file.
480    def instantiate(cls, name, parent = None):
481        instance = Node(name, cls, parent, isParamContext(cls))
482
483        if hasattr(cls, 'check'):
484            cls.check()
485
486        for key,value in cls._values.iteritems():
487            if isConfigNode(value):
488                cls.add_child(instance, key, value)
489            if isinstance(value, (list, tuple)):
490                vals = [ v for v in value if isConfigNode(v) ]
491                if len(vals):
492                    cls.add_child(instance, key, vals)
493
494        for pname,param in cls._params.iteritems():
495            value = cls._values.get(pname, None)
496            if value is None:
497                panic('Error getting %s from %s' % (pname, name))
498
499            try:
500                if isConfigNode(value):
501                    value = instance.child_objects[value]
502                elif isinstance(value, (list, tuple)):
503                    v = []
504                    for val in value:
505                        if isConfigNode(val):
506                            v.append(instance.child_objects[val])
507                        else:
508                            v.append(val)
509                    value = v
510
511                p = NodeParam(pname, param, value)
512                instance.params.append(p)
513                instance.param_names[pname] = p
514            except Exception, e:
515                msg = 'Exception while evaluating %s.%s\n%s' % \
516                      (instance.path, pname, e)
517                e.args = (msg, )
518                raise
519
520        return instance
521
522    def _convert(cls, value):
523        realvalue = value
524        if isinstance(value, Node):
525            realvalue = value.realtype
526
527        if isinstance(realvalue, Proxy):
528            return value
529
530        if realvalue == None or isNullPointer(realvalue):
531            return value
532
533        if isSubClass(realvalue, cls):
534            return value
535
536        raise TypeError, 'object %s type %s wrong type, should be %s' % \
537              (repr(realvalue), realvalue, cls)
538
539    def _string(cls, value):
540        if isNullPointer(value):
541            return 'Null'
542        return Node._string(value)
543
544# The ConfigNode class is the root of the special hierarchy.  Most of
545# the code in this class deals with the configuration hierarchy itself
546# (parent/child node relationships).
547class ConfigNode(object):
548    # Specify metaclass.  Any class inheriting from ConfigNode will
549    # get this metaclass.
550    __metaclass__ = MetaConfigNode
551
552    def __new__(cls, **kwargs):
553        name = cls.__name__ + ("_%d" % cls._anon_subclass_counter)
554        cls._anon_subclass_counter += 1
555        return cls.__metaclass__(name, (cls, ), kwargs)
556
557class ParamContext(ConfigNode,ParamType):
558    pass
559
560class MetaSimObject(MetaConfigNode):
561    # init_keywords and keywords are inherited from MetaConfigNode,
562    # with overrides/additions
563    init_keywords = MetaConfigNode.init_keywords
564    init_keywords.update({ 'abstract' : types.BooleanType,
565                           'type' : types.StringType })
566
567    keywords = MetaConfigNode.keywords
568    # no additional keywords
569
570    cpp_classes = []
571
572    # initialization
573    def __init__(cls, name, bases, dict):
574        super(MetaSimObject, cls).__init__(name, bases, dict)
575
576        if hasattr(cls, 'type'):
577            if name == 'SimObject':
578                cls._cpp_base = None
579            elif hasattr(cls._bases[1], 'type'):
580                cls._cpp_base = cls._bases[1].type
581            else:
582                panic("SimObject %s derives from a non-C++ SimObject %s "\
583                      "(no 'type')" % (cls, cls_bases[1].__name__))
584
585            # This class corresponds to a C++ class: put it on the global
586            # list of C++ objects to generate param structs, etc.
587            MetaSimObject.cpp_classes.append(cls)
588
589    def _cpp_decl(cls):
590        name = cls.__name__
591        code = ""
592        code += "\n".join([e.cpp_declare() for e in cls._param_types.values()])
593        code += "\n"
594        param_names = cls._params.keys()
595        param_names.sort()
596        code += "struct Params"
597        if cls._cpp_base:
598            code += " : public %s::Params" % cls._cpp_base
599        code += " {\n    "
600        code += "\n    ".join([cls._params[pname].cpp_decl(pname) \
601                               for pname in param_names])
602        code += "\n};\n"
603        return code
604
605class NodeParam(object):
606    def __init__(self, name, param, value):
607        self.name = name
608        self.param = param
609        self.ptype = param.ptype
610        self.convert = param.convert
611        self.string = param.string
612        self.value = value
613
614class Node(object):
615    all = {}
616    def __init__(self, name, realtype, parent, paramcontext):
617        self.name = name
618        self.realtype = realtype
619        if isSimObject(realtype):
620            self.type = realtype.type
621        else:
622            self.type = None
623        self.parent = parent
624        self.children = []
625        self.child_names = {}
626        self.child_objects = {}
627        self.top_child_names = {}
628        self.params = []
629        self.param_names = {}
630        self.paramcontext = paramcontext
631
632        path = [ self.name ]
633        node = self.parent
634        while node is not None:
635            if node.name != 'root':
636                path.insert(0, node.name)
637            else:
638                assert(node.parent is None)
639            node = node.parent
640        self.path = '.'.join(path)
641
642    def find(self, realtype, path):
643        if not path:
644            if issubclass(self.realtype, realtype):
645                return self, True
646
647            obj = None
648            for child in self.children:
649                if issubclass(child.realtype, realtype):
650                    if obj is not None:
651                        raise AttributeError, \
652                              'parent.any matched more than one: %s %s' % \
653                              (obj.path, child.path)
654                    obj = child
655            return obj, obj is not None
656
657        try:
658            obj = self
659            for (node,index) in path[:-1]:
660                if obj.child_names.has_key(node):
661                    obj = obj.child_names[node]
662                else:
663                    obj = obj.top_child_names[node]
664                obj = Proxy.getindex(obj, index)
665
666            (last,index) = path[-1]
667            if obj.child_names.has_key(last):
668                value = obj.child_names[last]
669                return Proxy.getindex(value, index), True
670            elif obj.top_child_names.has_key(last):
671                value = obj.top_child_names[last]
672                return Proxy.getindex(value, index), True
673            elif obj.param_names.has_key(last):
674                value = obj.param_names[last]
675                realtype._convert(value.value)
676                return Proxy.getindex(value.value, index), True
677        except KeyError:
678            pass
679
680        return None, False
681
682    def unproxy(self, param, ptype):
683        if not isinstance(param, Proxy):
684            return param
685        return param.unproxy(self, ptype)
686
687    def fixup(self):
688        self.all[self.path] = self
689
690        for param in self.params:
691            ptype = param.ptype
692            pval = param.value
693
694            try:
695                if isinstance(pval, (list, tuple)):
696                    param.value = [ self.unproxy(pv, ptype) for pv in pval ]
697                else:
698                    param.value = self.unproxy(pval, ptype)
699            except Exception, e:
700                msg = 'Error while fixing up %s:%s\n%s' % \
701                      (self.path, param.name, e)
702                e.args = (msg, )
703                raise
704
705        for child in self.children:
706            assert(child != self)
707            child.fixup()
708
709    # print type and parameter values to .ini file
710    def display(self):
711        print '[' + self.path + ']'	# .ini section header
712
713        if isSimObject(self.realtype):
714            print 'type = %s' % self.type
715
716        if self.children:
717            # instantiate children in same order they were added for
718            # backward compatibility (else we can end up with cpu1
719            # before cpu0).  Changing ordering can also influence timing
720            # in the current memory system, as caches get added to a bus
721            # in different orders which affects their priority in the
722            # case of simulataneous requests.
723            self.children.sort(lambda x,y: cmp(x.name, y.name))
724            children = [ c.name for c in self.children if not c.paramcontext]
725            print 'children =', ' '.join(children)
726
727        self.params.sort(lambda x,y: cmp(x.name, y.name))
728        for param in self.params:
729            try:
730                if param.value is None:
731                    raise AttributeError, 'Parameter with no value'
732
733                value = param.convert(param.value)
734                string = param.string(value)
735            except Exception, e:
736                msg = 'exception in %s:%s\n%s' % (self.path, param.name, e)
737                e.args = (msg, )
738                raise
739
740            print '%s = %s' % (param.name, string)
741
742        print
743
744        # recursively dump out children
745        for c in self.children:
746            c.display()
747
748    # print type and parameter values to .ini file
749    def outputDot(self, dot):
750        label = "{%s|" % self.path
751        if isSimObject(self.realtype):
752            label +=  '%s|' % self.type
753
754        if self.children:
755            # instantiate children in same order they were added for
756            # backward compatibility (else we can end up with cpu1
757            # before cpu0).
758            for c in self.children:
759                dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
760
761        simobjs = []
762        for param in self.params:
763            try:
764                if param.value is None:
765                    raise AttributeError, 'Parameter with no value'
766
767                value = param.convert(param.value)
768                string = param.string(value)
769            except Exception, e:
770                msg = 'exception in %s:%s\n%s' % (self.name, param.name, e)
771                e.args = (msg, )
772                raise
773
774            if isConfigNode(param.ptype) and string != "Null":
775                simobjs.append(string)
776            else:
777                label += '%s = %s\\n' % (param.name, string)
778
779        for so in simobjs:
780            label += "|<%s> %s" % (so, so)
781            dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
782                                    tailport="w"))
783        label += '}'
784        dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
785
786        # recursively dump out children
787        for c in self.children:
788            c.outputDot(dot)
789
790    def _string(cls, value):
791        if not isinstance(value, Node):
792            raise AttributeError, 'expecting %s got %s' % (Node, value)
793        return value.path
794    _string = classmethod(_string)
795
796#####################################################################
797#
798# Parameter description classes
799#
800# The _params dictionary in each class maps parameter names to
801# either a Param or a VectorParam object.  These objects contain the
802# parameter description string, the parameter type, and the default
803# value (loaded from the PARAM section of the .odesc files).  The
804# _convert() method on these objects is used to force whatever value
805# is assigned to the parameter to the appropriate type.
806#
807# Note that the default values are loaded into the class's attribute
808# space when the parameter dictionary is initialized (in
809# MetaConfigNode._setparams()); after that point they aren't used.
810#
811#####################################################################
812
813def isNullPointer(value):
814    return isinstance(value, NullSimObject)
815
816class Value(object):
817    def __init__(self, obj, attr):
818        super(Value, self).__setattr__('attr', attr)
819        super(Value, self).__setattr__('obj', obj)
820
821    def _getattr(self):
822        return self.obj._values.get(self.attr)
823
824    def __setattr__(self, attr, value):
825        setattr(self._getattr(), attr, value)
826
827    def __getattr__(self, attr):
828        return getattr(self._getattr(), attr)
829
830    def __getitem__(self, index):
831        return self._getattr().__getitem__(index)
832
833    def __call__(self, *args, **kwargs):
834        return self._getattr().__call__(*args, **kwargs)
835
836    def __nonzero__(self):
837        return bool(self._getattr())
838
839    def __str__(self):
840        return str(self._getattr())
841
842    def __len__(self):
843        return len(self._getattr())
844
845# Regular parameter.
846class ParamBase(object):
847    def __init__(self, ptype, *args, **kwargs):
848        if isinstance(ptype, types.StringType):
849            self.ptype_string = ptype
850        elif isinstance(ptype, type):
851            self.ptype = ptype
852        else:
853            raise TypeError, "Param type is not a type (%s)" % ptype
854
855        if args:
856            if len(args) == 1:
857                self.desc = args[0]
858            elif len(args) == 2:
859                self.default = args[0]
860                self.desc = args[1]
861            else:
862                raise TypeError, 'too many arguments'
863
864        if kwargs.has_key('desc'):
865            assert(not hasattr(self, 'desc'))
866            self.desc = kwargs['desc']
867            del kwargs['desc']
868
869        if kwargs.has_key('default'):
870            assert(not hasattr(self, 'default'))
871            self.default = kwargs['default']
872            del kwargs['default']
873
874        if kwargs:
875            raise TypeError, 'extra unknown kwargs %s' % kwargs
876
877        if not hasattr(self, 'desc'):
878            raise TypeError, 'desc attribute missing'
879
880    def maybe_resolve_type(self, context):
881        # check if already resolved... don't use hasattr(),
882        # as that calls __getattr__()
883        if self.__dict__.has_key('ptype'):
884            return
885        try:
886            self.ptype = context[self.ptype_string]
887        except KeyError:
888            # no harm in trying... we'll try again later using global scope
889            pass
890
891    def __getattr__(self, attr):
892        if attr == 'ptype':
893            try:
894                self.ptype = param_types[self.ptype_string]
895                return self.ptype
896            except:
897                panic("undefined Param type %s" % self.ptype_string)
898        else:
899            raise AttributeError, "'%s' object has no attribute '%s'" % \
900                  (type(self).__name__, attr)
901
902    def valid(self, value):
903        if not isinstance(value, Proxy):
904            self.ptype._convert(value)
905
906    def convert(self, value):
907        return self.ptype._convert(value)
908
909    def string(self, value):
910        return self.ptype._string(value)
911
912    def set(self, name, instance, value):
913        instance.__dict__[name] = value
914
915    def cpp_decl(self, name):
916        return '%s %s;' % (self.ptype._cpp_param_decl, name)
917
918class ParamFactory(object):
919    def __init__(self, type):
920        self.ptype = type
921
922    # E.g., Param.Int(5, "number of widgets")
923    def __call__(self, *args, **kwargs):
924        return ParamBase(self.ptype, *args, **kwargs)
925
926    # Strange magic to theoretically allow dotted names as Param classes,
927    # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
928    def __getattr__(self, attr):
929        if attr == '__bases__':
930            raise AttributeError, ''
931        cls = type(self)
932        return cls(attr)
933
934    def __setattr__(self, attr, value):
935        if attr != 'ptype':
936            raise AttributeError, \
937                  'Attribute %s not available in %s' % (attr, self.__class__)
938        super(ParamFactory, self).__setattr__(attr, value)
939
940Param = ParamFactory(None)
941
942# Vector-valued parameter description.  Just like Param, except that
943# the value is a vector (list) of the specified type instead of a
944# single value.
945class VectorParamBase(ParamBase):
946    def __init__(self, type, *args, **kwargs):
947        ParamBase.__init__(self, type, *args, **kwargs)
948
949    def valid(self, value):
950        if value == None:
951            return True
952
953        if isinstance(value, (list, tuple)):
954            for val in value:
955                if not isinstance(val, Proxy):
956                    self.ptype._convert(val)
957        elif not isinstance(value, Proxy):
958            self.ptype._convert(value)
959
960    # Convert assigned value to appropriate type.  If the RHS is not a
961    # list or tuple, it generates a single-element list.
962    def convert(self, value):
963        if value == None:
964            return []
965
966        if isinstance(value, (list, tuple)):
967            # list: coerce each element into new list
968            return [ self.ptype._convert(v) for v in value ]
969        else:
970            # singleton: coerce & wrap in a list
971            return self.ptype._convert(value)
972
973    def string(self, value):
974        if isinstance(value, (list, tuple)):
975            return ' '.join([ self.ptype._string(v) for v in value])
976        else:
977            return self.ptype._string(value)
978
979    def cpp_decl(self, name):
980        return 'std::vector<%s> %s;' % (self.ptype._cpp_param_decl, name)
981
982class VectorParamFactory(ParamFactory):
983    # E.g., VectorParam.Int(5, "number of widgets")
984    def __call__(self, *args, **kwargs):
985        return VectorParamBase(self.ptype, *args, **kwargs)
986
987VectorParam = VectorParamFactory(None)
988
989#####################################################################
990#
991# Parameter Types
992#
993# Though native Python types could be used to specify parameter types
994# (the 'ptype' field of the Param and VectorParam classes), it's more
995# flexible to define our own set of types.  This gives us more control
996# over how Python expressions are converted to values (via the
997# __init__() constructor) and how these values are printed out (via
998# the __str__() conversion method).  Eventually we'll need these types
999# to correspond to distinct C++ types as well.
1000#
1001#####################################################################
1002
1003class MetaRange(type):
1004    def __init__(cls, name, bases, dict):
1005        super(MetaRange, cls).__init__(name, bases, dict)
1006        if name == 'Range':
1007            return
1008        cls._cpp_param_decl = 'Range<%s>' % cls.type._cpp_param_decl
1009
1010    def _convert(cls, value):
1011        if not isinstance(value, Range):
1012            raise TypeError, 'value %s is not a Pair' % value
1013        value = cls(value)
1014        value.first = cls.type._convert(value.first)
1015        value.second = cls.type._convert(value.second)
1016        return value
1017
1018    def _string(cls, value):
1019        first = int(value.first)
1020        second = int(value.second)
1021        if value.extend:
1022            second += first
1023        if not value.inclusive:
1024            second -= 1
1025        return '%s:%s' % (cls.type._string(first), cls.type._string(second))
1026
1027class Range(ParamType):
1028    __metaclass__ = MetaRange
1029    def __init__(self, *args, **kwargs):
1030        if len(args) == 0:
1031            self.first = kwargs.pop('start')
1032
1033            if 'end' in kwargs:
1034                self.second = kwargs.pop('end')
1035                self.inclusive = True
1036                self.extend = False
1037            elif 'size' in kwargs:
1038                self.second = kwargs.pop('size')
1039                self.inclusive = False
1040                self.extend = True
1041            else:
1042                raise TypeError, "Either end or size must be specified"
1043
1044        elif len(args) == 1:
1045            if kwargs:
1046                self.first = args[0]
1047                if 'end' in kwargs:
1048                    self.second = kwargs.pop('end')
1049                    self.inclusive = True
1050                    self.extend = False
1051                elif 'size' in kwargs:
1052                    self.second = kwargs.pop('size')
1053                    self.inclusive = False
1054                    self.extend = True
1055                else:
1056                    raise TypeError, "Either end or size must be specified"
1057            elif isinstance(args[0], Range):
1058                self.first = args[0].first
1059                self.second = args[0].second
1060                self.inclusive = args[0].inclusive
1061                self.extend = args[0].extend
1062            else:
1063                self.first = 0
1064                self.second = args[0]
1065                self.inclusive = False
1066                self.extend = True
1067
1068        elif len(args) == 2:
1069            self.first, self.second = args
1070            self.inclusive = True
1071            self.extend = False
1072        else:
1073            raise TypeError, "Too many arguments specified"
1074
1075        if kwargs:
1076            raise TypeError, "too many keywords: %s" % kwargs.keys()
1077
1078# Metaclass for bounds-checked integer parameters.  See CheckedInt.
1079class CheckedIntType(type):
1080    def __init__(cls, name, bases, dict):
1081        super(CheckedIntType, cls).__init__(name, bases, dict)
1082
1083        # CheckedInt is an abstract base class, so we actually don't
1084        # want to do any processing on it... the rest of this code is
1085        # just for classes that derive from CheckedInt.
1086        if name == 'CheckedInt':
1087            return
1088
1089        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1090            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1091                panic("CheckedInt subclass %s must define either\n" \
1092                      "    'min' and 'max' or 'size' and 'unsigned'\n" \
1093                      % name);
1094            if cls.unsigned:
1095                cls.min = 0
1096                cls.max = 2 ** cls.size - 1
1097            else:
1098                cls.min = -(2 ** (cls.size - 1))
1099                cls.max = (2 ** (cls.size - 1)) - 1
1100
1101        cls._cpp_param_decl = cls.cppname
1102
1103    def _convert(cls, value):
1104        if isinstance(value, bool):
1105            return int(value)
1106
1107        if not isinstance(value, (int, long, float, str)):
1108            raise TypeError, 'Integer param of invalid type %s' % type(value)
1109
1110        if isinstance(value, float):
1111            value = long(value)
1112        elif isinstance(value, str):
1113            value = toInteger(value)
1114
1115        if not cls.min <= value <= cls.max:
1116            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1117                  (cls.min, value, cls.max)
1118
1119        return value
1120
1121    def _string(cls, value):
1122        return str(value)
1123
1124# Abstract superclass for bounds-checked integer parameters.  This
1125# class is subclassed to generate parameter classes with specific
1126# bounds.  Initialization of the min and max bounds is done in the
1127# metaclass CheckedIntType.__init__.
1128class CheckedInt(long,ParamType):
1129    __metaclass__ = CheckedIntType
1130
1131class Int(CheckedInt):      cppname = 'int';      size = 32; unsigned = False
1132class Unsigned(CheckedInt): cppname = 'unsigned'; size = 32; unsigned = True
1133
1134class Int8(CheckedInt):     cppname =  'int8_t';  size =  8; unsigned = False
1135class UInt8(CheckedInt):    cppname = 'uint8_t';  size =  8; unsigned = True
1136class Int16(CheckedInt):    cppname =  'int16_t'; size = 16; unsigned = False
1137class UInt16(CheckedInt):   cppname = 'uint16_t'; size = 16; unsigned = True
1138class Int32(CheckedInt):    cppname =  'int32_t'; size = 32; unsigned = False
1139class UInt32(CheckedInt):   cppname = 'uint32_t'; size = 32; unsigned = True
1140class Int64(CheckedInt):    cppname =  'int64_t'; size = 64; unsigned = False
1141class UInt64(CheckedInt):   cppname = 'uint64_t'; size = 64; unsigned = True
1142
1143class Counter(CheckedInt): cppname = 'Counter'; size = 64; unsigned = True
1144class Tick(CheckedInt):    cppname = 'Tick';    size = 64; unsigned = True
1145
1146class Percent(CheckedInt): cppname = 'int'; min = 0; max = 100
1147
1148class MemorySize(CheckedInt):
1149    cppname = 'uint64_t'
1150    size = 64
1151    unsigned = True
1152    def __new__(cls, value):
1153        return super(MemorySize, cls).__new__(cls, toMemorySize(value))
1154
1155    def _convert(cls, value):
1156        return cls(value)
1157    _convert = classmethod(_convert)
1158
1159    def _string(cls, value):
1160        return '%d' % value
1161    _string = classmethod(_string)
1162
1163class Addr(MemorySize):
1164    pass
1165
1166class AddrRange(Range):
1167    type = Addr
1168
1169# Boolean parameter type.
1170class Bool(ParamType):
1171    _cpp_param_decl = 'bool'
1172    #def __new__(cls, value):
1173    #    return super(MemorySize, cls).__new__(cls, toBool(value))
1174
1175    def _convert(cls, value):
1176        return toBool(value)
1177    _convert = classmethod(_convert)
1178
1179    def _string(cls, value):
1180        if value:
1181            return "true"
1182        else:
1183            return "false"
1184    _string = classmethod(_string)
1185
1186# String-valued parameter.
1187class String(ParamType):
1188    _cpp_param_decl = 'string'
1189
1190    # Constructor.  Value must be Python string.
1191    def _convert(cls,value):
1192        if value is None:
1193            return ''
1194        if isinstance(value, str):
1195            return value
1196
1197        raise TypeError, \
1198              "String param got value %s %s" % (repr(value), type(value))
1199    _convert = classmethod(_convert)
1200
1201    # Generate printable string version.  Not too tricky.
1202    def _string(cls, value):
1203        return value
1204    _string = classmethod(_string)
1205
1206def IncEthernetAddr(addr, val = 1):
1207    bytes = map(lambda x: int(x, 16), addr.split(':'))
1208    bytes[5] += val
1209    for i in (5, 4, 3, 2, 1):
1210        val,rem = divmod(bytes[i], 256)
1211        bytes[i] = rem
1212        if val == 0:
1213            break
1214        bytes[i - 1] += val
1215    assert(bytes[0] <= 255)
1216    return ':'.join(map(lambda x: '%02x' % x, bytes))
1217
1218class NextEthernetAddr(object):
1219    __metaclass__ = Singleton
1220    addr = "00:90:00:00:00:01"
1221
1222    def __init__(self, inc = 1):
1223        self.value = self.addr
1224        self.addr = IncEthernetAddr(self.addr, inc)
1225
1226class EthernetAddr(ParamType):
1227    _cpp_param_decl = 'EthAddr'
1228
1229    def _convert(cls, value):
1230        if value == NextEthernetAddr:
1231            return value
1232
1233        if not isinstance(value, str):
1234            raise TypeError, "expected an ethernet address and didn't get one"
1235
1236        bytes = value.split(':')
1237        if len(bytes) != 6:
1238            raise TypeError, 'invalid ethernet address %s' % value
1239
1240        for byte in bytes:
1241            if not 0 <= int(byte) <= 256:
1242                raise TypeError, 'invalid ethernet address %s' % value
1243
1244        return value
1245    _convert = classmethod(_convert)
1246
1247    def _string(cls, value):
1248        if value == NextEthernetAddr:
1249            value = value().value
1250        return value
1251    _string = classmethod(_string)
1252
1253# Special class for NULL pointers.  Note the special check in
1254# make_param_value() above that lets these be assigned where a
1255# SimObject is required.
1256# only one copy of a particular node
1257class NullSimObject(object):
1258    __metaclass__ = Singleton
1259
1260    def __call__(cls):
1261        return cls
1262
1263    def _instantiate(self, parent = None, path = ''):
1264        pass
1265
1266    def _convert(cls, value):
1267        if value == Nxone:
1268            return
1269
1270        if isinstance(value, cls):
1271            return value
1272
1273        raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1274              (repr(value), type(value), cls)
1275    _convert = classmethod(_convert)
1276
1277    def _string():
1278        return 'NULL'
1279    _string = staticmethod(_string)
1280
1281# The only instance you'll ever need...
1282Null = NULL = NullSimObject()
1283
1284# Enumerated types are a little more complex.  The user specifies the
1285# type as Enum(foo) where foo is either a list or dictionary of
1286# alternatives (typically strings, but not necessarily so).  (In the
1287# long run, the integer value of the parameter will be the list index
1288# or the corresponding dictionary value.  For now, since we only check
1289# that the alternative is valid and then spit it into a .ini file,
1290# there's not much point in using the dictionary.)
1291
1292# What Enum() must do is generate a new type encapsulating the
1293# provided list/dictionary so that specific values of the parameter
1294# can be instances of that type.  We define two hidden internal
1295# classes (_ListEnum and _DictEnum) to serve as base classes, then
1296# derive the new type from the appropriate base class on the fly.
1297
1298
1299# Metaclass for Enum types
1300class MetaEnum(type):
1301    def __init__(cls, name, bases, init_dict):
1302        if init_dict.has_key('map'):
1303            if not isinstance(cls.map, dict):
1304                raise TypeError, "Enum-derived class attribute 'map' " \
1305                      "must be of type dict"
1306            # build list of value strings from map
1307            cls.vals = cls.map.keys()
1308            cls.vals.sort()
1309        elif init_dict.has_key('vals'):
1310            if not isinstance(cls.vals, list):
1311                raise TypeError, "Enum-derived class attribute 'vals' " \
1312                      "must be of type list"
1313            # build string->value map from vals sequence
1314            cls.map = {}
1315            for idx,val in enumerate(cls.vals):
1316                cls.map[val] = idx
1317        else:
1318            raise TypeError, "Enum-derived class must define "\
1319                  "attribute 'map' or 'vals'"
1320
1321        cls._cpp_param_decl = name
1322
1323        super(MetaEnum, cls).__init__(name, bases, init_dict)
1324
1325    def cpp_declare(cls):
1326        s = 'enum %s {\n    ' % cls.__name__
1327        s += ',\n    '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1328        s += '\n};\n'
1329        return s
1330
1331# Base class for enum types.
1332class Enum(ParamType):
1333    __metaclass__ = MetaEnum
1334    vals = []
1335
1336    def _convert(self, value):
1337        if value not in self.map:
1338            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1339                  % (value, self.vals)
1340        return value
1341    _convert = classmethod(_convert)
1342
1343    # Generate printable string version of value.
1344    def _string(self, value):
1345        return str(value)
1346    _string = classmethod(_string)
1347#
1348# "Constants"... handy aliases for various values.
1349#
1350
1351class Frequency(int,ParamType):
1352    _cpp_param_decl = 'Tick'
1353
1354    def __new__(cls, value):
1355        if isinstance(value, basestring):
1356            val = int(env['FREQUENCY'] / toFrequency(value))
1357        else:
1358            val = toFrequency(value)
1359        return super(cls, Frequency).__new__(cls, val)
1360
1361    def _convert(cls, value):
1362        return cls(value)
1363    _convert = classmethod(_convert)
1364
1365    def _string(cls, value):
1366        return '%d' % value
1367    _string = classmethod(_string)
1368
1369class Latency(int,ParamType):
1370    _cpp_param_decl = 'Tick'
1371    def __new__(cls, value):
1372        if isinstance(value, basestring):
1373            val = int(env['FREQUENCY'] * toLatency(value))
1374        else:
1375            val = toLatency(value)
1376        return super(cls, Latency).__new__(cls, val)
1377
1378    def _convert(cls, value):
1379        return cls(value)
1380    _convert = classmethod(_convert)
1381
1382    def _string(cls, value):
1383        return '%d' % value
1384    _string = classmethod(_string)
1385
1386
1387# Some memory range specifications use this as a default upper bound.
1388MaxAddr = Addr.max
1389MaxTick = Tick.max
1390AllMemory = AddrRange(0, MaxAddr)
1391
1392#####################################################################
1393
1394# The final hook to generate .ini files.  Called from configuration
1395# script once config is built.
1396def instantiate(root):
1397    instance = root.instantiate('root')
1398    instance.fixup()
1399    instance.display()
1400    if not noDot:
1401       dot = pydot.Dot()
1402       instance.outputDot(dot)
1403       dot.orientation = "portrait"
1404       dot.size = "8.5,11"
1405       dot.ranksep="equally"
1406       dot.rank="samerank"
1407       dot.write("config.dot")
1408       dot.write_ps("config.ps")
1409
1410# SimObject is a minimal extension of ConfigNode, implementing a
1411# hierarchy node that corresponds to an M5 SimObject.  It prints out a
1412# "type=" line to indicate its SimObject class, prints out the
1413# assigned parameters corresponding to its class, and allows
1414# parameters to be set by keyword in the constructor.  Note that most
1415# of the heavy lifting for the SimObject param handling is done in the
1416# MetaConfigNode metaclass.
1417class SimObject(ConfigNode, ParamType):
1418    __metaclass__ = MetaSimObject
1419    type = 'SimObject'
1420
1421
1422# __all__ defines the list of symbols that get exported when
1423# 'from config import *' is invoked.  Try to keep this reasonably
1424# short to avoid polluting other namespaces.
1425__all__ = ['ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam',
1426           'parent', 'Enum',
1427           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1428           'Int32', 'UInt32', 'Int64', 'UInt64',
1429           'Counter', 'Addr', 'Tick', 'Percent',
1430           'MemorySize', 'Frequency', 'Latency',
1431           'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'NULL',
1432           'NextEthernetAddr', 'instantiate']
1433