SimObject.py revision 2711
110268SGeoffrey.Blake@arm.com# Copyright (c) 2004-2005 The Regents of The University of Michigan
210268SGeoffrey.Blake@arm.com# All rights reserved.
310268SGeoffrey.Blake@arm.com#
410268SGeoffrey.Blake@arm.com# Redistribution and use in source and binary forms, with or without
510268SGeoffrey.Blake@arm.com# modification, are permitted provided that the following conditions are
610268SGeoffrey.Blake@arm.com# met: redistributions of source code must retain the above copyright
710268SGeoffrey.Blake@arm.com# notice, this list of conditions and the following disclaimer;
810268SGeoffrey.Blake@arm.com# redistributions in binary form must reproduce the above copyright
910268SGeoffrey.Blake@arm.com# notice, this list of conditions and the following disclaimer in the
1010268SGeoffrey.Blake@arm.com# documentation and/or other materials provided with the distribution;
1110268SGeoffrey.Blake@arm.com# neither the name of the copyright holders nor the names of its
1210268SGeoffrey.Blake@arm.com# contributors may be used to endorse or promote products derived from
1310268SGeoffrey.Blake@arm.com# this software without specific prior written permission.
1410268SGeoffrey.Blake@arm.com#
1510268SGeoffrey.Blake@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1610268SGeoffrey.Blake@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1710268SGeoffrey.Blake@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1810268SGeoffrey.Blake@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1910268SGeoffrey.Blake@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2010268SGeoffrey.Blake@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2110268SGeoffrey.Blake@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2210268SGeoffrey.Blake@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2310268SGeoffrey.Blake@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2410268SGeoffrey.Blake@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2510268SGeoffrey.Blake@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2610268SGeoffrey.Blake@arm.com#
2710268SGeoffrey.Blake@arm.com# Authors: Steve Reinhardt
2810268SGeoffrey.Blake@arm.com#          Nathan Binkert
2910268SGeoffrey.Blake@arm.com
3010268SGeoffrey.Blake@arm.comimport os, re, sys, types, inspect
3110268SGeoffrey.Blake@arm.com
3210268SGeoffrey.Blake@arm.comimport m5
3310268SGeoffrey.Blake@arm.comfrom m5 import panic
3410268SGeoffrey.Blake@arm.comfrom convert import *
3510268SGeoffrey.Blake@arm.comfrom multidict import multidict
3610268SGeoffrey.Blake@arm.com
3710268SGeoffrey.Blake@arm.comnoDot = False
3810268SGeoffrey.Blake@arm.comtry:
3910268SGeoffrey.Blake@arm.com    import pydot
4010268SGeoffrey.Blake@arm.comexcept:
4110268SGeoffrey.Blake@arm.com    noDot = True
4210268SGeoffrey.Blake@arm.com
4310268SGeoffrey.Blake@arm.comclass Singleton(type):
4410268SGeoffrey.Blake@arm.com    def __call__(cls, *args, **kwargs):
4510268SGeoffrey.Blake@arm.com        if hasattr(cls, '_instance'):
4610268SGeoffrey.Blake@arm.com            return cls._instance
4710268SGeoffrey.Blake@arm.com
4810268SGeoffrey.Blake@arm.com        cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
4910268SGeoffrey.Blake@arm.com        return cls._instance
5010268SGeoffrey.Blake@arm.com
5110268SGeoffrey.Blake@arm.com#####################################################################
5210268SGeoffrey.Blake@arm.com#
53# M5 Python Configuration Utility
54#
55# The basic idea is to write simple Python programs that build Python
56# objects corresponding to M5 SimObjects for the desired simulation
57# configuration.  For now, the Python emits a .ini file that can be
58# parsed by M5.  In the future, some tighter integration between M5
59# and the Python interpreter may allow bypassing the .ini file.
60#
61# Each SimObject class in M5 is represented by a Python class with the
62# same name.  The Python inheritance tree mirrors the M5 C++ tree
63# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
64# SimObjects inherit from a single SimObject base class).  To specify
65# an instance of an M5 SimObject in a configuration, the user simply
66# instantiates the corresponding Python object.  The parameters for
67# that SimObject are given by assigning to attributes of the Python
68# object, either using keyword assignment in the constructor or in
69# separate assignment statements.  For example:
70#
71# cache = BaseCache(size='64KB')
72# cache.hit_latency = 3
73# cache.assoc = 8
74#
75# The magic lies in the mapping of the Python attributes for SimObject
76# classes to the actual SimObject parameter specifications.  This
77# allows parameter validity checking in the Python code.  Continuing
78# the example above, the statements "cache.blurfl=3" or
79# "cache.assoc='hello'" would both result in runtime errors in Python,
80# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
81# parameter requires an integer, respectively.  This magic is done
82# primarily by overriding the special __setattr__ method that controls
83# assignment to object attributes.
84#
85# Once a set of Python objects have been instantiated in a hierarchy,
86# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
87# will generate a .ini file.  See simple-4cpu.py for an example
88# (corresponding to m5-test/simple-4cpu.ini).
89#
90#####################################################################
91
92#####################################################################
93#
94# ConfigNode/SimObject classes
95#
96# The Python class hierarchy rooted by ConfigNode (which is the base
97# class of SimObject, which in turn is the base class of all other M5
98# SimObject classes) has special attribute behavior.  In general, an
99# object in this hierarchy has three categories of attribute-like
100# things:
101#
102# 1. Regular Python methods and variables.  These must start with an
103# underscore to be treated normally.
104#
105# 2. SimObject parameters.  These values are stored as normal Python
106# attributes, but all assignments to these attributes are checked
107# against the pre-defined set of parameters stored in the class's
108# _params dictionary.  Assignments to attributes that do not
109# correspond to predefined parameters, or that are not of the correct
110# type, incur runtime errors.
111#
112# 3. Hierarchy children.  The child nodes of a ConfigNode are stored
113# in the node's _children dictionary, but can be accessed using the
114# Python attribute dot-notation (just as they are printed out by the
115# simulator).  Children cannot be created using attribute assigment;
116# they must be added by specifying the parent node in the child's
117# constructor or using the '+=' operator.
118
119# The SimObject parameters are the most complex, for a few reasons.
120# First, both parameter descriptions and parameter values are
121# inherited.  Thus parameter description lookup must go up the
122# inheritance chain like normal attribute lookup, but this behavior
123# must be explicitly coded since the lookup occurs in each class's
124# _params attribute.  Second, because parameter values can be set
125# on SimObject classes (to implement default values), the parameter
126# checking behavior must be enforced on class attribute assignments as
127# well as instance attribute assignments.  Finally, because we allow
128# class specialization via inheritance (e.g., see the L1Cache class in
129# the simple-4cpu.py example), we must do parameter checking even on
130# class instantiation.  To provide all these features, we use a
131# metaclass to define most of the SimObject parameter behavior for
132# this class hierarchy.
133#
134#####################################################################
135
136def isSimObject(value):
137    return isinstance(value, SimObject)
138
139def isSimObjectClass(value):
140    try:
141        return issubclass(value, SimObject)
142    except TypeError:
143        # happens if value is not a class at all
144        return False
145
146def isSimObjSequence(value):
147    if not isinstance(value, (list, tuple)):
148        return False
149
150    for val in value:
151        if not isNullPointer(val) and not isSimObject(val):
152            return False
153
154    return True
155
156def isSimObjClassSequence(value):
157    if not isinstance(value, (list, tuple)):
158        return False
159
160    for val in value:
161        if not isNullPointer(val) and not isSimObjectClass(val):
162            return False
163
164    return True
165
166def isNullPointer(value):
167    return isinstance(value, NullSimObject)
168
169# The metaclass for ConfigNode (and thus for everything that derives
170# from ConfigNode, including SimObject).  This class controls how new
171# classes that derive from ConfigNode are instantiated, and provides
172# inherited class behavior (just like a class controls how instances
173# of that class are instantiated, and provides inherited instance
174# behavior).
175class MetaSimObject(type):
176    # Attributes that can be set only at initialization time
177    init_keywords = { 'abstract' : types.BooleanType,
178                      'type' : types.StringType }
179    # Attributes that can be set any time
180    keywords = { 'check' : types.FunctionType,
181                 'children' : types.ListType }
182
183    # __new__ is called before __init__, and is where the statements
184    # in the body of the class definition get loaded into the class's
185    # __dict__.  We intercept this to filter out parameter assignments
186    # and only allow "private" attributes to be passed to the base
187    # __new__ (starting with underscore).
188    def __new__(mcls, name, bases, dict):
189        if dict.has_key('_init_dict'):
190            # must have been called from makeSubclass() rather than
191            # via Python class declaration; bypass filtering process.
192            cls_dict = dict
193        else:
194            # Copy "private" attributes (including special methods
195            # such as __new__) to the official dict.  Everything else
196            # goes in _init_dict to be filtered in __init__.
197            cls_dict = {}
198            for key,val in dict.items():
199                if key.startswith('_'):
200                    cls_dict[key] = val
201                    del dict[key]
202            cls_dict['_init_dict'] = dict
203        return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
204
205    # subclass initialization
206    def __init__(cls, name, bases, dict):
207        # calls type.__init__()... I think that's a no-op, but leave
208        # it here just in case it's not.
209        super(MetaSimObject, cls).__init__(name, bases, dict)
210
211        # initialize required attributes
212        cls._params = multidict()
213        cls._values = multidict()
214        cls._anon_subclass_counter = 0
215
216        # We don't support multiple inheritance.  If you want to, you
217        # must fix multidict to deal with it properly.
218        if len(bases) > 1:
219            raise TypeError, "SimObjects do not support multiple inheritance"
220
221        base = bases[0]
222
223        # the only time the following is not true is when we define
224        # the SimObject class itself
225        if isinstance(base, MetaSimObject):
226            cls._params.parent = base._params
227            cls._values.parent = base._values
228
229        # now process the _init_dict items
230        for key,val in cls._init_dict.items():
231            if isinstance(val, (types.FunctionType, types.TypeType)):
232                type.__setattr__(cls, key, val)
233
234            # param descriptions
235            elif isinstance(val, ParamDesc):
236                cls._new_param(key, val)
237
238            # init-time-only keywords
239            elif cls.init_keywords.has_key(key):
240                cls._set_keyword(key, val, cls.init_keywords[key])
241
242            # default: use normal path (ends up in __setattr__)
243            else:
244                setattr(cls, key, val)
245
246        # Pull the deep-copy memoization dict out of the class dict if
247        # it's there...
248        memo = cls.__dict__.get('_memo', {})
249
250        # Handle SimObject values
251        for key,val in cls._values.iteritems():
252            # SimObject instances need to be promoted to classes.
253            # Existing classes should not have any instance values, so
254            # these can only occur at the lowest level dict (the
255            # parameters just being set in this class definition).
256            if isSimObject(val):
257                assert(val == cls._values.local[key])
258                cls._values[key] = val.makeClass(memo)
259            elif isSimObjSequence(val) and len(val):
260                assert(val == cls._values.local[key])
261                cls._values[key] = [ v.makeClass(memo) for v in val ]
262            # SimObject classes need to be subclassed so that
263            # parameters that get set at this level only affect this
264            # level and derivatives.
265            elif isSimObjectClass(val):
266                assert(not cls._values.local.has_key(key))
267                cls._values[key] = val.makeSubclass({}, memo)
268            elif isSimObjClassSequence(val) and len(val):
269                assert(not cls._values.local.has_key(key))
270                cls._values[key] = [ v.makeSubclass({}, memo) for v in val ]
271
272
273    def _set_keyword(cls, keyword, val, kwtype):
274        if not isinstance(val, kwtype):
275            raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
276                  (keyword, type(val), kwtype)
277        if isinstance(val, types.FunctionType):
278            val = classmethod(val)
279        type.__setattr__(cls, keyword, val)
280
281    def _new_param(cls, name, value):
282        cls._params[name] = value
283        if hasattr(value, 'default'):
284            setattr(cls, name, value.default)
285
286    # Set attribute (called on foo.attr = value when foo is an
287    # instance of class cls).
288    def __setattr__(cls, attr, value):
289        # normal processing for private attributes
290        if attr.startswith('_'):
291            type.__setattr__(cls, attr, value)
292            return
293
294        if cls.keywords.has_key(attr):
295            cls._set_keyword(attr, value, cls.keywords[attr])
296            return
297
298        # must be SimObject param
299        param = cls._params.get(attr, None)
300        if param:
301            # It's ok: set attribute by delegating to 'object' class.
302            try:
303                cls._values[attr] = param.convert(value)
304            except Exception, e:
305                msg = "%s\nError setting param %s.%s to %s\n" % \
306                      (e, cls.__name__, attr, value)
307                e.args = (msg, )
308                raise
309        # I would love to get rid of this
310        elif isSimObject(value) or isSimObjSequence(value):
311           cls._values[attr] = value
312        else:
313            raise AttributeError, \
314                  "Class %s has no parameter %s" % (cls.__name__, attr)
315
316    def __getattr__(cls, attr):
317        if cls._values.has_key(attr):
318            return cls._values[attr]
319
320        raise AttributeError, \
321              "object '%s' has no attribute '%s'" % (cls.__name__, attr)
322
323    # Create a subclass of this class.  Basically a function interface
324    # to the standard Python class definition mechanism, primarily for
325    # internal use.  'memo' dict param supports "deep copy" (really
326    # "deep subclass") operations... within a given operation,
327    # multiple references to a class should result in a single
328    # subclass object with multiple references to it (as opposed to
329    # mutiple unique subclasses).
330    def makeSubclass(cls, init_dict, memo = {}):
331        subcls = memo.get(cls)
332        if not subcls:
333            name = cls.__name__ + '_' + str(cls._anon_subclass_counter)
334            cls._anon_subclass_counter += 1
335            subcls = MetaSimObject(name, (cls,),
336                                   { '_init_dict': init_dict, '_memo': memo })
337        return subcls
338
339# The ConfigNode class is the root of the special hierarchy.  Most of
340# the code in this class deals with the configuration hierarchy itself
341# (parent/child node relationships).
342class SimObject(object):
343    # Specify metaclass.  Any class inheriting from SimObject will
344    # get this metaclass.
345    __metaclass__ = MetaSimObject
346
347    # __new__ operator allocates new instances of the class.  We
348    # override it here just to support "deep instantiation" operation
349    # via the _memo dict.  When recursively instantiating an object
350    # hierarchy we want to make sure that each class is instantiated
351    # only once, and that if there are multiple references to the same
352    # original class, we end up with the corresponding instantiated
353    # references all pointing to the same instance.
354    def __new__(cls, _memo = None, **kwargs):
355        if _memo is not None and _memo.has_key(cls):
356            # return previously instantiated object
357            assert(len(kwargs) == 0)
358            return _memo[cls]
359        else:
360            # Need a new one... if it needs to be memoized, this will
361            # happen in __init__.  We defer the insertion until then
362            # so __init__ can use the memo dict to tell whether or not
363            # to perform the initialization.
364            return super(SimObject, cls).__new__(cls, **kwargs)
365
366    # Initialize new instance previously allocated by __new__.  For
367    # objects with SimObject-valued params, we need to recursively
368    # instantiate the classes represented by those param values as
369    # well (in a consistent "deep copy"-style fashion; see comment
370    # above).
371    def __init__(self, _memo = None, **kwargs):
372        if _memo is not None:
373            # We're inside a "deep instantiation"
374            assert(isinstance(_memo, dict))
375            assert(len(kwargs) == 0)
376            if _memo.has_key(self.__class__):
377                # __new__ returned an existing, already initialized
378                # instance, so there's nothing to do here
379                assert(_memo[self.__class__] == self)
380                return
381            # no pre-existing object, so remember this one here
382            _memo[self.__class__] = self
383        else:
384            # This is a new top-level instantiation... don't memoize
385            # this objcet, but prepare to memoize any recursively
386            # instantiated objects.
387            _memo = {}
388
389        self._children = {}
390        # Inherit parameter values from class using multidict so
391        # individual value settings can be overridden.
392        self._values = multidict(self.__class__._values)
393        # For SimObject-valued parameters, the class should have
394        # classes (not instances) for the values.  We need to
395        # instantiate these classes rather than just inheriting the
396        # class object.
397        for key,val in self.__class__._values.iteritems():
398            if isSimObjectClass(val):
399                setattr(self, key, val(_memo))
400            elif isSimObjClassSequence(val) and len(val):
401                setattr(self, key, [ v(_memo) for v in val ])
402        # apply attribute assignments from keyword args, if any
403        for key,val in kwargs.iteritems():
404            setattr(self, key, val)
405
406    # Use this instance as a template to create a new class.
407    def makeClass(self, memo = {}):
408        cls = memo.get(self)
409        if not cls:
410            cls =  self.__class__.makeSubclass(self._values.local)
411            memo[self] = cls
412        return cls
413
414    # Direct instantiation of instances (cloning) is no longer
415    # allowed; must generate class from instance first.
416    def __call__(self, **kwargs):
417        raise TypeError, "cannot instantiate SimObject; "\
418              "use makeClass() to make class first"
419
420    def __getattr__(self, attr):
421        if self._values.has_key(attr):
422            return self._values[attr]
423
424        raise AttributeError, "object '%s' has no attribute '%s'" \
425              % (self.__class__.__name__, attr)
426
427    # Set attribute (called on foo.attr = value when foo is an
428    # instance of class cls).
429    def __setattr__(self, attr, value):
430        # normal processing for private attributes
431        if attr.startswith('_'):
432            object.__setattr__(self, attr, value)
433            return
434
435        # must be SimObject param
436        param = self._params.get(attr, None)
437        if param:
438            # It's ok: set attribute by delegating to 'object' class.
439            try:
440                value = param.convert(value)
441            except Exception, e:
442                msg = "%s\nError setting param %s.%s to %s\n" % \
443                      (e, self.__class__.__name__, attr, value)
444                e.args = (msg, )
445                raise
446        # I would love to get rid of this
447        elif isSimObject(value) or isSimObjSequence(value):
448            pass
449        else:
450            raise AttributeError, "Class %s has no parameter %s" \
451                  % (self.__class__.__name__, attr)
452
453        # clear out old child with this name, if any
454        self.clear_child(attr)
455
456        if isSimObject(value):
457            value.set_path(self, attr)
458        elif isSimObjSequence(value):
459            value = SimObjVector(value)
460            [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)]
461
462        self._values[attr] = value
463
464    # this hack allows tacking a '[0]' onto parameters that may or may
465    # not be vectors, and always getting the first element (e.g. cpus)
466    def __getitem__(self, key):
467        if key == 0:
468            return self
469        raise TypeError, "Non-zero index '%s' to SimObject" % key
470
471    # clear out children with given name, even if it's a vector
472    def clear_child(self, name):
473        if not self._children.has_key(name):
474            return
475        child = self._children[name]
476        if isinstance(child, SimObjVector):
477            for i in xrange(len(child)):
478                del self._children["s%d" % (name, i)]
479        del self._children[name]
480
481    def add_child(self, name, value):
482        self._children[name] = value
483
484    def set_path(self, parent, name):
485        if not hasattr(self, '_parent'):
486            self._parent = parent
487            self._name = name
488            parent.add_child(name, self)
489
490    def path(self):
491        if not hasattr(self, '_parent'):
492            return 'root'
493        ppath = self._parent.path()
494        if ppath == 'root':
495            return self._name
496        return ppath + "." + self._name
497
498    def __str__(self):
499        return self.path()
500
501    def ini_str(self):
502        return self.path()
503
504    def find_any(self, ptype):
505        if isinstance(self, ptype):
506            return self, True
507
508        found_obj = None
509        for child in self._children.itervalues():
510            if isinstance(child, ptype):
511                if found_obj != None and child != found_obj:
512                    raise AttributeError, \
513                          'parent.any matched more than one: %s %s' % \
514                          (found_obj.path, child.path)
515                found_obj = child
516        # search param space
517        for pname,pdesc in self._params.iteritems():
518            if issubclass(pdesc.ptype, ptype):
519                match_obj = self._values[pname]
520                if found_obj != None and found_obj != match_obj:
521                    raise AttributeError, \
522                          'parent.any matched more than one: %s' % obj.path
523                found_obj = match_obj
524        return found_obj, found_obj != None
525
526    def unproxy(self, base):
527        return self
528
529    def print_ini(self):
530        print '[' + self.path() + ']'	# .ini section header
531
532        if hasattr(self, 'type') and not isinstance(self, ParamContext):
533            print 'type=%s' % self.type
534
535        child_names = self._children.keys()
536        child_names.sort()
537        np_child_names = [c for c in child_names \
538                          if not isinstance(self._children[c], ParamContext)]
539        if len(np_child_names):
540            print 'children=%s' % ' '.join(np_child_names)
541
542        param_names = self._params.keys()
543        param_names.sort()
544        for param in param_names:
545            value = self._values.get(param, None)
546            if value != None:
547                if isproxy(value):
548                    try:
549                        value = value.unproxy(self)
550                    except:
551                        print >> sys.stderr, \
552                              "Error in unproxying param '%s' of %s" % \
553                              (param, self.path())
554                        raise
555                    setattr(self, param, value)
556                print '%s=%s' % (param, self._values[param].ini_str())
557
558        print	# blank line between objects
559
560        for child in child_names:
561            self._children[child].print_ini()
562
563    # generate output file for 'dot' to display as a pretty graph.
564    # this code is currently broken.
565    def outputDot(self, dot):
566        label = "{%s|" % self.path
567        if isSimObject(self.realtype):
568            label +=  '%s|' % self.type
569
570        if self.children:
571            # instantiate children in same order they were added for
572            # backward compatibility (else we can end up with cpu1
573            # before cpu0).
574            for c in self.children:
575                dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
576
577        simobjs = []
578        for param in self.params:
579            try:
580                if param.value is None:
581                    raise AttributeError, 'Parameter with no value'
582
583                value = param.value
584                string = param.string(value)
585            except Exception, e:
586                msg = 'exception in %s:%s\n%s' % (self.name, param.name, e)
587                e.args = (msg, )
588                raise
589
590            if isSimObject(param.ptype) and string != "Null":
591                simobjs.append(string)
592            else:
593                label += '%s = %s\\n' % (param.name, string)
594
595        for so in simobjs:
596            label += "|<%s> %s" % (so, so)
597            dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
598                                    tailport="w"))
599        label += '}'
600        dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
601
602        # recursively dump out children
603        for c in self.children:
604            c.outputDot(dot)
605
606class ParamContext(SimObject):
607    pass
608
609#####################################################################
610#
611# Proxy object support.
612#
613#####################################################################
614
615class BaseProxy(object):
616    def __init__(self, search_self, search_up):
617        self._search_self = search_self
618        self._search_up = search_up
619        self._multiplier = None
620
621    def __setattr__(self, attr, value):
622        if not attr.startswith('_'):
623            raise AttributeError, 'cannot set attribute on proxy object'
624        super(BaseProxy, self).__setattr__(attr, value)
625
626    # support multiplying proxies by constants
627    def __mul__(self, other):
628        if not isinstance(other, (int, long, float)):
629            raise TypeError, "Proxy multiplier must be integer"
630        if self._multiplier == None:
631            self._multiplier = other
632        else:
633            # support chained multipliers
634            self._multiplier *= other
635        return self
636
637    __rmul__ = __mul__
638
639    def _mulcheck(self, result):
640        if self._multiplier == None:
641            return result
642        return result * self._multiplier
643
644    def unproxy(self, base):
645        obj = base
646        done = False
647
648        if self._search_self:
649            result, done = self.find(obj)
650
651        if self._search_up:
652            while not done:
653                try: obj = obj._parent
654                except: break
655
656                result, done = self.find(obj)
657
658        if not done:
659            raise AttributeError, "Can't resolve proxy '%s' from '%s'" % \
660                  (self.path(), base.path())
661
662        if isinstance(result, BaseProxy):
663            if result == self:
664                raise RuntimeError, "Cycle in unproxy"
665            result = result.unproxy(obj)
666
667        return self._mulcheck(result)
668
669    def getindex(obj, index):
670        if index == None:
671            return obj
672        try:
673            obj = obj[index]
674        except TypeError:
675            if index != 0:
676                raise
677            # if index is 0 and item is not subscriptable, just
678            # use item itself (so cpu[0] works on uniprocessors)
679        return obj
680    getindex = staticmethod(getindex)
681
682    def set_param_desc(self, pdesc):
683        self._pdesc = pdesc
684
685class AttrProxy(BaseProxy):
686    def __init__(self, search_self, search_up, attr):
687        super(AttrProxy, self).__init__(search_self, search_up)
688        self._attr = attr
689        self._modifiers = []
690
691    def __getattr__(self, attr):
692        # python uses __bases__ internally for inheritance
693        if attr.startswith('_'):
694            return super(AttrProxy, self).__getattr__(self, attr)
695        if hasattr(self, '_pdesc'):
696            raise AttributeError, "Attribute reference on bound proxy"
697        self._modifiers.append(attr)
698        return self
699
700    # support indexing on proxies (e.g., Self.cpu[0])
701    def __getitem__(self, key):
702        if not isinstance(key, int):
703            raise TypeError, "Proxy object requires integer index"
704        self._modifiers.append(key)
705        return self
706
707    def find(self, obj):
708        try:
709            val = getattr(obj, self._attr)
710        except:
711            return None, False
712        while isproxy(val):
713            val = val.unproxy(obj)
714        for m in self._modifiers:
715            if isinstance(m, str):
716                val = getattr(val, m)
717            elif isinstance(m, int):
718                val = val[m]
719            else:
720                assert("Item must be string or integer")
721            while isproxy(val):
722                val = val.unproxy(obj)
723        return val, True
724
725    def path(self):
726        p = self._attr
727        for m in self._modifiers:
728            if isinstance(m, str):
729                p += '.%s' % m
730            elif isinstance(m, int):
731                p += '[%d]' % m
732            else:
733                assert("Item must be string or integer")
734        return p
735
736class AnyProxy(BaseProxy):
737    def find(self, obj):
738        return obj.find_any(self._pdesc.ptype)
739
740    def path(self):
741        return 'any'
742
743def isproxy(obj):
744    if isinstance(obj, (BaseProxy, EthernetAddr)):
745        return True
746    elif isinstance(obj, (list, tuple)):
747        for v in obj:
748            if isproxy(v):
749                return True
750    return False
751
752class ProxyFactory(object):
753    def __init__(self, search_self, search_up):
754        self.search_self = search_self
755        self.search_up = search_up
756
757    def __getattr__(self, attr):
758        if attr == 'any':
759            return AnyProxy(self.search_self, self.search_up)
760        else:
761            return AttrProxy(self.search_self, self.search_up, attr)
762
763# global objects for handling proxies
764Parent = ProxyFactory(search_self = False, search_up = True)
765Self = ProxyFactory(search_self = True, search_up = False)
766
767#####################################################################
768#
769# Parameter description classes
770#
771# The _params dictionary in each class maps parameter names to
772# either a Param or a VectorParam object.  These objects contain the
773# parameter description string, the parameter type, and the default
774# value (loaded from the PARAM section of the .odesc files).  The
775# _convert() method on these objects is used to force whatever value
776# is assigned to the parameter to the appropriate type.
777#
778# Note that the default values are loaded into the class's attribute
779# space when the parameter dictionary is initialized (in
780# MetaConfigNode._setparams()); after that point they aren't used.
781#
782#####################################################################
783
784# Dummy base class to identify types that are legitimate for SimObject
785# parameters.
786class ParamValue(object):
787
788    # default for printing to .ini file is regular string conversion.
789    # will be overridden in some cases
790    def ini_str(self):
791        return str(self)
792
793    # allows us to blithely call unproxy() on things without checking
794    # if they're really proxies or not
795    def unproxy(self, base):
796        return self
797
798# Regular parameter description.
799class ParamDesc(object):
800    def __init__(self, ptype_str, ptype, *args, **kwargs):
801        self.ptype_str = ptype_str
802        # remember ptype only if it is provided
803        if ptype != None:
804            self.ptype = ptype
805
806        if args:
807            if len(args) == 1:
808                self.desc = args[0]
809            elif len(args) == 2:
810                self.default = args[0]
811                self.desc = args[1]
812            else:
813                raise TypeError, 'too many arguments'
814
815        if kwargs.has_key('desc'):
816            assert(not hasattr(self, 'desc'))
817            self.desc = kwargs['desc']
818            del kwargs['desc']
819
820        if kwargs.has_key('default'):
821            assert(not hasattr(self, 'default'))
822            self.default = kwargs['default']
823            del kwargs['default']
824
825        if kwargs:
826            raise TypeError, 'extra unknown kwargs %s' % kwargs
827
828        if not hasattr(self, 'desc'):
829            raise TypeError, 'desc attribute missing'
830
831    def __getattr__(self, attr):
832        if attr == 'ptype':
833            try:
834                ptype = eval(self.ptype_str, m5.objects.__dict__)
835                if not isinstance(ptype, type):
836                    panic("Param qualifier is not a type: %s" % self.ptype)
837                self.ptype = ptype
838                return ptype
839            except NameError:
840                pass
841        raise AttributeError, "'%s' object has no attribute '%s'" % \
842              (type(self).__name__, attr)
843
844    def convert(self, value):
845        if isinstance(value, BaseProxy):
846            value.set_param_desc(self)
847            return value
848        if not hasattr(self, 'ptype') and isNullPointer(value):
849            # deferred evaluation of SimObject; continue to defer if
850            # we're just assigning a null pointer
851            return value
852        if isinstance(value, self.ptype):
853            return value
854        if isNullPointer(value) and issubclass(self.ptype, SimObject):
855            return value
856        return self.ptype(value)
857
858# Vector-valued parameter description.  Just like ParamDesc, except
859# that the value is a vector (list) of the specified type instead of a
860# single value.
861
862class VectorParamValue(list):
863    def ini_str(self):
864        return ' '.join([v.ini_str() for v in self])
865
866    def unproxy(self, base):
867        return [v.unproxy(base) for v in self]
868
869class SimObjVector(VectorParamValue):
870    def print_ini(self):
871        for v in self:
872            v.print_ini()
873
874class VectorParamDesc(ParamDesc):
875    # Convert assigned value to appropriate type.  If the RHS is not a
876    # list or tuple, it generates a single-element list.
877    def convert(self, value):
878        if isinstance(value, (list, tuple)):
879            # list: coerce each element into new list
880            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
881            if isSimObjSequence(tmp_list):
882                return SimObjVector(tmp_list)
883            else:
884                return VectorParamValue(tmp_list)
885        else:
886            # singleton: leave it be (could coerce to a single-element
887            # list here, but for some historical reason we don't...
888            return ParamDesc.convert(self, value)
889
890
891class ParamFactory(object):
892    def __init__(self, param_desc_class, ptype_str = None):
893        self.param_desc_class = param_desc_class
894        self.ptype_str = ptype_str
895
896    def __getattr__(self, attr):
897        if self.ptype_str:
898            attr = self.ptype_str + '.' + attr
899        return ParamFactory(self.param_desc_class, attr)
900
901    # E.g., Param.Int(5, "number of widgets")
902    def __call__(self, *args, **kwargs):
903        caller_frame = inspect.currentframe().f_back
904        ptype = None
905        try:
906            ptype = eval(self.ptype_str,
907                         caller_frame.f_globals, caller_frame.f_locals)
908            if not isinstance(ptype, type):
909                raise TypeError, \
910                      "Param qualifier is not a type: %s" % ptype
911        except NameError:
912            # if name isn't defined yet, assume it's a SimObject, and
913            # try to resolve it later
914            pass
915        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
916
917Param = ParamFactory(ParamDesc)
918VectorParam = ParamFactory(VectorParamDesc)
919
920#####################################################################
921#
922# Parameter Types
923#
924# Though native Python types could be used to specify parameter types
925# (the 'ptype' field of the Param and VectorParam classes), it's more
926# flexible to define our own set of types.  This gives us more control
927# over how Python expressions are converted to values (via the
928# __init__() constructor) and how these values are printed out (via
929# the __str__() conversion method).  Eventually we'll need these types
930# to correspond to distinct C++ types as well.
931#
932#####################################################################
933
934# superclass for "numeric" parameter values, to emulate math
935# operations in a type-safe way.  e.g., a Latency times an int returns
936# a new Latency object.
937class NumericParamValue(ParamValue):
938    def __str__(self):
939        return str(self.value)
940
941    def __float__(self):
942        return float(self.value)
943
944    # hook for bounds checking
945    def _check(self):
946        return
947
948    def __mul__(self, other):
949        newobj = self.__class__(self)
950        newobj.value *= other
951        newobj._check()
952        return newobj
953
954    __rmul__ = __mul__
955
956    def __div__(self, other):
957        newobj = self.__class__(self)
958        newobj.value /= other
959        newobj._check()
960        return newobj
961
962    def __sub__(self, other):
963        newobj = self.__class__(self)
964        newobj.value -= other
965        newobj._check()
966        return newobj
967
968class Range(ParamValue):
969    type = int # default; can be overridden in subclasses
970    def __init__(self, *args, **kwargs):
971
972        def handle_kwargs(self, kwargs):
973            if 'end' in kwargs:
974                self.second = self.type(kwargs.pop('end'))
975            elif 'size' in kwargs:
976                self.second = self.first + self.type(kwargs.pop('size')) - 1
977            else:
978                raise TypeError, "Either end or size must be specified"
979
980        if len(args) == 0:
981            self.first = self.type(kwargs.pop('start'))
982            handle_kwargs(self, kwargs)
983
984        elif len(args) == 1:
985            if kwargs:
986                self.first = self.type(args[0])
987                handle_kwargs(self, kwargs)
988            elif isinstance(args[0], Range):
989                self.first = self.type(args[0].first)
990                self.second = self.type(args[0].second)
991            else:
992                self.first = self.type(0)
993                self.second = self.type(args[0]) - 1
994
995        elif len(args) == 2:
996            self.first = self.type(args[0])
997            self.second = self.type(args[1])
998        else:
999            raise TypeError, "Too many arguments specified"
1000
1001        if kwargs:
1002            raise TypeError, "too many keywords: %s" % kwargs.keys()
1003
1004    def __str__(self):
1005        return '%s:%s' % (self.first, self.second)
1006
1007# Metaclass for bounds-checked integer parameters.  See CheckedInt.
1008class CheckedIntType(type):
1009    def __init__(cls, name, bases, dict):
1010        super(CheckedIntType, cls).__init__(name, bases, dict)
1011
1012        # CheckedInt is an abstract base class, so we actually don't
1013        # want to do any processing on it... the rest of this code is
1014        # just for classes that derive from CheckedInt.
1015        if name == 'CheckedInt':
1016            return
1017
1018        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1019            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1020                panic("CheckedInt subclass %s must define either\n" \
1021                      "    'min' and 'max' or 'size' and 'unsigned'\n" \
1022                      % name);
1023            if cls.unsigned:
1024                cls.min = 0
1025                cls.max = 2 ** cls.size - 1
1026            else:
1027                cls.min = -(2 ** (cls.size - 1))
1028                cls.max = (2 ** (cls.size - 1)) - 1
1029
1030# Abstract superclass for bounds-checked integer parameters.  This
1031# class is subclassed to generate parameter classes with specific
1032# bounds.  Initialization of the min and max bounds is done in the
1033# metaclass CheckedIntType.__init__.
1034class CheckedInt(NumericParamValue):
1035    __metaclass__ = CheckedIntType
1036
1037    def _check(self):
1038        if not self.min <= self.value <= self.max:
1039            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1040                  (self.min, self.value, self.max)
1041
1042    def __init__(self, value):
1043        if isinstance(value, str):
1044            self.value = toInteger(value)
1045        elif isinstance(value, (int, long, float)):
1046            self.value = long(value)
1047        self._check()
1048
1049class Int(CheckedInt):      size = 32; unsigned = False
1050class Unsigned(CheckedInt): size = 32; unsigned = True
1051
1052class Int8(CheckedInt):     size =  8; unsigned = False
1053class UInt8(CheckedInt):    size =  8; unsigned = True
1054class Int16(CheckedInt):    size = 16; unsigned = False
1055class UInt16(CheckedInt):   size = 16; unsigned = True
1056class Int32(CheckedInt):    size = 32; unsigned = False
1057class UInt32(CheckedInt):   size = 32; unsigned = True
1058class Int64(CheckedInt):    size = 64; unsigned = False
1059class UInt64(CheckedInt):   size = 64; unsigned = True
1060
1061class Counter(CheckedInt):  size = 64; unsigned = True
1062class Tick(CheckedInt):     size = 64; unsigned = True
1063class TcpPort(CheckedInt):  size = 16; unsigned = True
1064class UdpPort(CheckedInt):  size = 16; unsigned = True
1065
1066class Percent(CheckedInt):  min = 0; max = 100
1067
1068class Float(ParamValue, float):
1069    pass
1070
1071class MemorySize(CheckedInt):
1072    size = 64
1073    unsigned = True
1074    def __init__(self, value):
1075        if isinstance(value, MemorySize):
1076            self.value = value.value
1077        else:
1078            self.value = toMemorySize(value)
1079        self._check()
1080
1081class MemorySize32(CheckedInt):
1082    size = 32
1083    unsigned = True
1084    def __init__(self, value):
1085        if isinstance(value, MemorySize):
1086            self.value = value.value
1087        else:
1088            self.value = toMemorySize(value)
1089        self._check()
1090
1091class Addr(CheckedInt):
1092    size = 64
1093    unsigned = True
1094    def __init__(self, value):
1095        if isinstance(value, Addr):
1096            self.value = value.value
1097        else:
1098            try:
1099                self.value = toMemorySize(value)
1100            except TypeError:
1101                self.value = long(value)
1102        self._check()
1103
1104class AddrRange(Range):
1105    type = Addr
1106
1107# String-valued parameter.  Just mixin the ParamValue class
1108# with the built-in str class.
1109class String(ParamValue,str):
1110    pass
1111
1112# Boolean parameter type.  Python doesn't let you subclass bool, since
1113# it doesn't want to let you create multiple instances of True and
1114# False.  Thus this is a little more complicated than String.
1115class Bool(ParamValue):
1116    def __init__(self, value):
1117        try:
1118            self.value = toBool(value)
1119        except TypeError:
1120            self.value = bool(value)
1121
1122    def __str__(self):
1123        return str(self.value)
1124
1125    def ini_str(self):
1126        if self.value:
1127            return 'true'
1128        return 'false'
1129
1130def IncEthernetAddr(addr, val = 1):
1131    bytes = map(lambda x: int(x, 16), addr.split(':'))
1132    bytes[5] += val
1133    for i in (5, 4, 3, 2, 1):
1134        val,rem = divmod(bytes[i], 256)
1135        bytes[i] = rem
1136        if val == 0:
1137            break
1138        bytes[i - 1] += val
1139    assert(bytes[0] <= 255)
1140    return ':'.join(map(lambda x: '%02x' % x, bytes))
1141
1142class NextEthernetAddr(object):
1143    addr = "00:90:00:00:00:01"
1144
1145    def __init__(self, inc = 1):
1146        self.value = NextEthernetAddr.addr
1147        NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
1148
1149class EthernetAddr(ParamValue):
1150    def __init__(self, value):
1151        if value == NextEthernetAddr:
1152            self.value = value
1153            return
1154
1155        if not isinstance(value, str):
1156            raise TypeError, "expected an ethernet address and didn't get one"
1157
1158        bytes = value.split(':')
1159        if len(bytes) != 6:
1160            raise TypeError, 'invalid ethernet address %s' % value
1161
1162        for byte in bytes:
1163            if not 0 <= int(byte) <= 256:
1164                raise TypeError, 'invalid ethernet address %s' % value
1165
1166        self.value = value
1167
1168    def unproxy(self, base):
1169        if self.value == NextEthernetAddr:
1170            self.addr = self.value().value
1171        return self
1172
1173    def __str__(self):
1174        if self.value == NextEthernetAddr:
1175            if hasattr(self, 'addr'):
1176                return self.addr
1177            else:
1178                return "NextEthernetAddr (unresolved)"
1179        else:
1180            return self.value
1181
1182# Special class for NULL pointers.  Note the special check in
1183# make_param_value() above that lets these be assigned where a
1184# SimObject is required.
1185# only one copy of a particular node
1186class NullSimObject(object):
1187    __metaclass__ = Singleton
1188
1189    def __call__(cls):
1190        return cls
1191
1192    def _instantiate(self, parent = None, path = ''):
1193        pass
1194
1195    def ini_str(self):
1196        return 'Null'
1197
1198    def unproxy(self, base):
1199        return self
1200
1201    def set_path(self, parent, name):
1202        pass
1203    def __str__(self):
1204        return 'Null'
1205
1206# The only instance you'll ever need...
1207Null = NULL = NullSimObject()
1208
1209# Enumerated types are a little more complex.  The user specifies the
1210# type as Enum(foo) where foo is either a list or dictionary of
1211# alternatives (typically strings, but not necessarily so).  (In the
1212# long run, the integer value of the parameter will be the list index
1213# or the corresponding dictionary value.  For now, since we only check
1214# that the alternative is valid and then spit it into a .ini file,
1215# there's not much point in using the dictionary.)
1216
1217# What Enum() must do is generate a new type encapsulating the
1218# provided list/dictionary so that specific values of the parameter
1219# can be instances of that type.  We define two hidden internal
1220# classes (_ListEnum and _DictEnum) to serve as base classes, then
1221# derive the new type from the appropriate base class on the fly.
1222
1223
1224# Metaclass for Enum types
1225class MetaEnum(type):
1226    def __init__(cls, name, bases, init_dict):
1227        if init_dict.has_key('map'):
1228            if not isinstance(cls.map, dict):
1229                raise TypeError, "Enum-derived class attribute 'map' " \
1230                      "must be of type dict"
1231            # build list of value strings from map
1232            cls.vals = cls.map.keys()
1233            cls.vals.sort()
1234        elif init_dict.has_key('vals'):
1235            if not isinstance(cls.vals, list):
1236                raise TypeError, "Enum-derived class attribute 'vals' " \
1237                      "must be of type list"
1238            # build string->value map from vals sequence
1239            cls.map = {}
1240            for idx,val in enumerate(cls.vals):
1241                cls.map[val] = idx
1242        else:
1243            raise TypeError, "Enum-derived class must define "\
1244                  "attribute 'map' or 'vals'"
1245
1246        super(MetaEnum, cls).__init__(name, bases, init_dict)
1247
1248    def cpp_declare(cls):
1249        s = 'enum %s {\n    ' % cls.__name__
1250        s += ',\n    '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1251        s += '\n};\n'
1252        return s
1253
1254# Base class for enum types.
1255class Enum(ParamValue):
1256    __metaclass__ = MetaEnum
1257    vals = []
1258
1259    def __init__(self, value):
1260        if value not in self.map:
1261            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1262                  % (value, self.vals)
1263        self.value = value
1264
1265    def __str__(self):
1266        return self.value
1267
1268ticks_per_sec = None
1269
1270# how big does a rounding error need to be before we warn about it?
1271frequency_tolerance = 0.001  # 0.1%
1272
1273# convert a floting-point # of ticks to integer, and warn if rounding
1274# discards too much precision
1275def tick_check(float_ticks):
1276    if float_ticks == 0:
1277        return 0
1278    int_ticks = int(round(float_ticks))
1279    err = (float_ticks - int_ticks) / float_ticks
1280    if err > frequency_tolerance:
1281        print >> sys.stderr, "Warning: rounding error > tolerance"
1282        print >> sys.stderr, "    %f rounded to %d" % (float_ticks, int_ticks)
1283        #raise ValueError
1284    return int_ticks
1285
1286def getLatency(value):
1287    if isinstance(value, Latency) or isinstance(value, Clock):
1288        return value.value
1289    elif isinstance(value, Frequency) or isinstance(value, RootClock):
1290        return 1 / value.value
1291    elif isinstance(value, str):
1292        try:
1293            return toLatency(value)
1294        except ValueError:
1295            try:
1296                return 1 / toFrequency(value)
1297            except ValueError:
1298                pass # fall through
1299    raise ValueError, "Invalid Frequency/Latency value '%s'" % value
1300
1301
1302class Latency(NumericParamValue):
1303    def __init__(self, value):
1304        self.value = getLatency(value)
1305
1306    def __getattr__(self, attr):
1307        if attr in ('latency', 'period'):
1308            return self
1309        if attr == 'frequency':
1310            return Frequency(self)
1311        raise AttributeError, "Latency object has no attribute '%s'" % attr
1312
1313    # convert latency to ticks
1314    def ini_str(self):
1315        return str(tick_check(self.value * ticks_per_sec))
1316
1317class Frequency(NumericParamValue):
1318    def __init__(self, value):
1319        self.value = 1 / getLatency(value)
1320
1321    def __getattr__(self, attr):
1322        if attr == 'frequency':
1323            return self
1324        if attr in ('latency', 'period'):
1325            return Latency(self)
1326        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1327
1328    # convert frequency to ticks per period
1329    def ini_str(self):
1330        return self.period.ini_str()
1331
1332# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
1333# We can't inherit from Frequency because we don't want it to be directly
1334# assignable to a regular Frequency parameter.
1335class RootClock(ParamValue):
1336    def __init__(self, value):
1337        self.value = 1 / getLatency(value)
1338
1339    def __getattr__(self, attr):
1340        if attr == 'frequency':
1341            return Frequency(self)
1342        if attr in ('latency', 'period'):
1343            return Latency(self)
1344        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1345
1346    def ini_str(self):
1347        return str(tick_check(self.value))
1348
1349# A generic frequency and/or Latency value.  Value is stored as a latency,
1350# but to avoid ambiguity this object does not support numeric ops (* or /).
1351# An explicit conversion to a Latency or Frequency must be made first.
1352class Clock(ParamValue):
1353    def __init__(self, value):
1354        self.value = getLatency(value)
1355
1356    def __getattr__(self, attr):
1357        if attr == 'frequency':
1358            return Frequency(self)
1359        if attr in ('latency', 'period'):
1360            return Latency(self)
1361        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1362
1363    def ini_str(self):
1364        return self.period.ini_str()
1365
1366class NetworkBandwidth(float,ParamValue):
1367    def __new__(cls, value):
1368        val = toNetworkBandwidth(value) / 8.0
1369        return super(cls, NetworkBandwidth).__new__(cls, val)
1370
1371    def __str__(self):
1372        return str(self.val)
1373
1374    def ini_str(self):
1375        return '%f' % (ticks_per_sec / float(self))
1376
1377class MemoryBandwidth(float,ParamValue):
1378    def __new__(self, value):
1379        val = toMemoryBandwidth(value)
1380        return super(cls, MemoryBandwidth).__new__(cls, val)
1381
1382    def __str__(self):
1383        return str(self.val)
1384
1385    def ini_str(self):
1386        return '%f' % (ticks_per_sec / float(self))
1387
1388#
1389# "Constants"... handy aliases for various values.
1390#
1391
1392# Some memory range specifications use this as a default upper bound.
1393MaxAddr = Addr.max
1394MaxTick = Tick.max
1395AllMemory = AddrRange(0, MaxAddr)
1396
1397#####################################################################
1398
1399# __all__ defines the list of symbols that get exported when
1400# 'from config import *' is invoked.  Try to keep this reasonably
1401# short to avoid polluting other namespaces.
1402__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam',
1403           'Parent', 'Self',
1404           'Enum', 'Bool', 'String', 'Float',
1405           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1406           'Int32', 'UInt32', 'Int64', 'UInt64',
1407           'Counter', 'Addr', 'Tick', 'Percent',
1408           'TcpPort', 'UdpPort', 'EthernetAddr',
1409           'MemorySize', 'MemorySize32',
1410           'Latency', 'Frequency', 'RootClock', 'Clock',
1411           'NetworkBandwidth', 'MemoryBandwidth',
1412           'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory',
1413           'Null', 'NULL',
1414           'NextEthernetAddr']
1415
1416