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