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