config.py revision 2922:654ef3d30b61
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, \
670                  "Can't resolve proxy '%s' from '%s'" % \
671                  (self.path(), base.path())
672
673        if isinstance(result, BaseProxy):
674            if result == self:
675                raise RuntimeError, "Cycle in unproxy"
676            result = result.unproxy(obj)
677
678        return self._mulcheck(result)
679
680    def getindex(obj, index):
681        if index == None:
682            return obj
683        try:
684            obj = obj[index]
685        except TypeError:
686            if index != 0:
687                raise
688            # if index is 0 and item is not subscriptable, just
689            # use item itself (so cpu[0] works on uniprocessors)
690        return obj
691    getindex = staticmethod(getindex)
692
693    def set_param_desc(self, pdesc):
694        self._pdesc = pdesc
695
696class AttrProxy(BaseProxy):
697    def __init__(self, search_self, search_up, attr):
698        super(AttrProxy, self).__init__(search_self, search_up)
699        self._attr = attr
700        self._modifiers = []
701
702    def __getattr__(self, attr):
703        # python uses __bases__ internally for inheritance
704        if attr.startswith('_'):
705            return super(AttrProxy, self).__getattr__(self, attr)
706        if hasattr(self, '_pdesc'):
707            raise AttributeError, "Attribute reference on bound proxy"
708        self._modifiers.append(attr)
709        return self
710
711    # support indexing on proxies (e.g., Self.cpu[0])
712    def __getitem__(self, key):
713        if not isinstance(key, int):
714            raise TypeError, "Proxy object requires integer index"
715        self._modifiers.append(key)
716        return self
717
718    def find(self, obj):
719        try:
720            val = getattr(obj, self._attr)
721        except:
722            return None, False
723        while isproxy(val):
724            val = val.unproxy(obj)
725        for m in self._modifiers:
726            if isinstance(m, str):
727                val = getattr(val, m)
728            elif isinstance(m, int):
729                val = val[m]
730            else:
731                assert("Item must be string or integer")
732            while isproxy(val):
733                val = val.unproxy(obj)
734        return val, True
735
736    def path(self):
737        p = self._attr
738        for m in self._modifiers:
739            if isinstance(m, str):
740                p += '.%s' % m
741            elif isinstance(m, int):
742                p += '[%d]' % m
743            else:
744                assert("Item must be string or integer")
745        return p
746
747class AnyProxy(BaseProxy):
748    def find(self, obj):
749        return obj.find_any(self._pdesc.ptype)
750
751    def path(self):
752        return 'any'
753
754def isproxy(obj):
755    if isinstance(obj, (BaseProxy, EthernetAddr)):
756        return True
757    elif isinstance(obj, (list, tuple)):
758        for v in obj:
759            if isproxy(v):
760                return True
761    return False
762
763class ProxyFactory(object):
764    def __init__(self, search_self, search_up):
765        self.search_self = search_self
766        self.search_up = search_up
767
768    def __getattr__(self, attr):
769        if attr == 'any':
770            return AnyProxy(self.search_self, self.search_up)
771        else:
772            return AttrProxy(self.search_self, self.search_up, attr)
773
774# global objects for handling proxies
775Parent = ProxyFactory(search_self = False, search_up = True)
776Self = ProxyFactory(search_self = True, search_up = False)
777
778#####################################################################
779#
780# Parameter description classes
781#
782# The _params dictionary in each class maps parameter names to either
783# a Param or a VectorParam object.  These objects contain the
784# parameter description string, the parameter type, and the default
785# value (if any).  The convert() method on these objects is used to
786# force whatever value is assigned to the parameter to the appropriate
787# type.
788#
789# Note that the default values are loaded into the class's attribute
790# space when the parameter dictionary is initialized (in
791# MetaSimObject._new_param()); after that point they aren't used.
792#
793#####################################################################
794
795# Dummy base class to identify types that are legitimate for SimObject
796# parameters.
797class ParamValue(object):
798
799    # default for printing to .ini file is regular string conversion.
800    # will be overridden in some cases
801    def ini_str(self):
802        return str(self)
803
804    # allows us to blithely call unproxy() on things without checking
805    # if they're really proxies or not
806    def unproxy(self, base):
807        return self
808
809# Regular parameter description.
810class ParamDesc(object):
811    def __init__(self, ptype_str, ptype, *args, **kwargs):
812        self.ptype_str = ptype_str
813        # remember ptype only if it is provided
814        if ptype != None:
815            self.ptype = ptype
816
817        if args:
818            if len(args) == 1:
819                self.desc = args[0]
820            elif len(args) == 2:
821                self.default = args[0]
822                self.desc = args[1]
823            else:
824                raise TypeError, 'too many arguments'
825
826        if kwargs.has_key('desc'):
827            assert(not hasattr(self, 'desc'))
828            self.desc = kwargs['desc']
829            del kwargs['desc']
830
831        if kwargs.has_key('default'):
832            assert(not hasattr(self, 'default'))
833            self.default = kwargs['default']
834            del kwargs['default']
835
836        if kwargs:
837            raise TypeError, 'extra unknown kwargs %s' % kwargs
838
839        if not hasattr(self, 'desc'):
840            raise TypeError, 'desc attribute missing'
841
842    def __getattr__(self, attr):
843        if attr == 'ptype':
844            try:
845                ptype = eval(self.ptype_str, m5.objects.__dict__)
846                if not isinstance(ptype, type):
847                    panic("Param qualifier is not a type: %s" % self.ptype)
848                self.ptype = ptype
849                return ptype
850            except NameError:
851                pass
852        raise AttributeError, "'%s' object has no attribute '%s'" % \
853              (type(self).__name__, attr)
854
855    def convert(self, value):
856        if isinstance(value, BaseProxy):
857            value.set_param_desc(self)
858            return value
859        if not hasattr(self, 'ptype') and isNullPointer(value):
860            # deferred evaluation of SimObject; continue to defer if
861            # we're just assigning a null pointer
862            return value
863        if isinstance(value, self.ptype):
864            return value
865        if isNullPointer(value) and issubclass(self.ptype, SimObject):
866            return value
867        return self.ptype(value)
868
869# Vector-valued parameter description.  Just like ParamDesc, except
870# that the value is a vector (list) of the specified type instead of a
871# single value.
872
873class VectorParamValue(list):
874    def ini_str(self):
875        return ' '.join([v.ini_str() for v in self])
876
877    def unproxy(self, base):
878        return [v.unproxy(base) for v in self]
879
880class SimObjVector(VectorParamValue):
881    def print_ini(self):
882        for v in self:
883            v.print_ini()
884
885class VectorParamDesc(ParamDesc):
886    # Convert assigned value to appropriate type.  If the RHS is not a
887    # list or tuple, it generates a single-element list.
888    def convert(self, value):
889        if isinstance(value, (list, tuple)):
890            # list: coerce each element into new list
891            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
892            if isSimObjectSequence(tmp_list):
893                return SimObjVector(tmp_list)
894            else:
895                return VectorParamValue(tmp_list)
896        else:
897            # singleton: leave it be (could coerce to a single-element
898            # list here, but for some historical reason we don't...
899            return ParamDesc.convert(self, value)
900
901
902class ParamFactory(object):
903    def __init__(self, param_desc_class, ptype_str = None):
904        self.param_desc_class = param_desc_class
905        self.ptype_str = ptype_str
906
907    def __getattr__(self, attr):
908        if self.ptype_str:
909            attr = self.ptype_str + '.' + attr
910        return ParamFactory(self.param_desc_class, attr)
911
912    # E.g., Param.Int(5, "number of widgets")
913    def __call__(self, *args, **kwargs):
914        caller_frame = inspect.currentframe().f_back
915        ptype = None
916        try:
917            ptype = eval(self.ptype_str,
918                         caller_frame.f_globals, caller_frame.f_locals)
919            if not isinstance(ptype, type):
920                raise TypeError, \
921                      "Param qualifier is not a type: %s" % ptype
922        except NameError:
923            # if name isn't defined yet, assume it's a SimObject, and
924            # try to resolve it later
925            pass
926        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
927
928Param = ParamFactory(ParamDesc)
929VectorParam = ParamFactory(VectorParamDesc)
930
931#####################################################################
932#
933# Parameter Types
934#
935# Though native Python types could be used to specify parameter types
936# (the 'ptype' field of the Param and VectorParam classes), it's more
937# flexible to define our own set of types.  This gives us more control
938# over how Python expressions are converted to values (via the
939# __init__() constructor) and how these values are printed out (via
940# the __str__() conversion method).  Eventually we'll need these types
941# to correspond to distinct C++ types as well.
942#
943#####################################################################
944
945# superclass for "numeric" parameter values, to emulate math
946# operations in a type-safe way.  e.g., a Latency times an int returns
947# a new Latency object.
948class NumericParamValue(ParamValue):
949    def __str__(self):
950        return str(self.value)
951
952    def __float__(self):
953        return float(self.value)
954
955    # hook for bounds checking
956    def _check(self):
957        return
958
959    def __mul__(self, other):
960        newobj = self.__class__(self)
961        newobj.value *= other
962        newobj._check()
963        return newobj
964
965    __rmul__ = __mul__
966
967    def __div__(self, other):
968        newobj = self.__class__(self)
969        newobj.value /= other
970        newobj._check()
971        return newobj
972
973    def __sub__(self, other):
974        newobj = self.__class__(self)
975        newobj.value -= other
976        newobj._check()
977        return newobj
978
979class Range(ParamValue):
980    type = int # default; can be overridden in subclasses
981    def __init__(self, *args, **kwargs):
982
983        def handle_kwargs(self, kwargs):
984            if 'end' in kwargs:
985                self.second = self.type(kwargs.pop('end'))
986            elif 'size' in kwargs:
987                self.second = self.first + self.type(kwargs.pop('size')) - 1
988            else:
989                raise TypeError, "Either end or size must be specified"
990
991        if len(args) == 0:
992            self.first = self.type(kwargs.pop('start'))
993            handle_kwargs(self, kwargs)
994
995        elif len(args) == 1:
996            if kwargs:
997                self.first = self.type(args[0])
998                handle_kwargs(self, kwargs)
999            elif isinstance(args[0], Range):
1000                self.first = self.type(args[0].first)
1001                self.second = self.type(args[0].second)
1002            else:
1003                self.first = self.type(0)
1004                self.second = self.type(args[0]) - 1
1005
1006        elif len(args) == 2:
1007            self.first = self.type(args[0])
1008            self.second = self.type(args[1])
1009        else:
1010            raise TypeError, "Too many arguments specified"
1011
1012        if kwargs:
1013            raise TypeError, "too many keywords: %s" % kwargs.keys()
1014
1015    def __str__(self):
1016        return '%s:%s' % (self.first, self.second)
1017
1018# Metaclass for bounds-checked integer parameters.  See CheckedInt.
1019class CheckedIntType(type):
1020    def __init__(cls, name, bases, dict):
1021        super(CheckedIntType, cls).__init__(name, bases, dict)
1022
1023        # CheckedInt is an abstract base class, so we actually don't
1024        # want to do any processing on it... the rest of this code is
1025        # just for classes that derive from CheckedInt.
1026        if name == 'CheckedInt':
1027            return
1028
1029        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1030            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1031                panic("CheckedInt subclass %s must define either\n" \
1032                      "    'min' and 'max' or 'size' and 'unsigned'\n" \
1033                      % name);
1034            if cls.unsigned:
1035                cls.min = 0
1036                cls.max = 2 ** cls.size - 1
1037            else:
1038                cls.min = -(2 ** (cls.size - 1))
1039                cls.max = (2 ** (cls.size - 1)) - 1
1040
1041# Abstract superclass for bounds-checked integer parameters.  This
1042# class is subclassed to generate parameter classes with specific
1043# bounds.  Initialization of the min and max bounds is done in the
1044# metaclass CheckedIntType.__init__.
1045class CheckedInt(NumericParamValue):
1046    __metaclass__ = CheckedIntType
1047
1048    def _check(self):
1049        if not self.min <= self.value <= self.max:
1050            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1051                  (self.min, self.value, self.max)
1052
1053    def __init__(self, value):
1054        if isinstance(value, str):
1055            self.value = toInteger(value)
1056        elif isinstance(value, (int, long, float)):
1057            self.value = long(value)
1058        self._check()
1059
1060class Int(CheckedInt):      size = 32; unsigned = False
1061class Unsigned(CheckedInt): size = 32; unsigned = True
1062
1063class Int8(CheckedInt):     size =  8; unsigned = False
1064class UInt8(CheckedInt):    size =  8; unsigned = True
1065class Int16(CheckedInt):    size = 16; unsigned = False
1066class UInt16(CheckedInt):   size = 16; unsigned = True
1067class Int32(CheckedInt):    size = 32; unsigned = False
1068class UInt32(CheckedInt):   size = 32; unsigned = True
1069class Int64(CheckedInt):    size = 64; unsigned = False
1070class UInt64(CheckedInt):   size = 64; unsigned = True
1071
1072class Counter(CheckedInt):  size = 64; unsigned = True
1073class Tick(CheckedInt):     size = 64; unsigned = True
1074class TcpPort(CheckedInt):  size = 16; unsigned = True
1075class UdpPort(CheckedInt):  size = 16; unsigned = True
1076
1077class Percent(CheckedInt):  min = 0; max = 100
1078
1079class Float(ParamValue, float):
1080    pass
1081
1082class MemorySize(CheckedInt):
1083    size = 64
1084    unsigned = True
1085    def __init__(self, value):
1086        if isinstance(value, MemorySize):
1087            self.value = value.value
1088        else:
1089            self.value = toMemorySize(value)
1090        self._check()
1091
1092class MemorySize32(CheckedInt):
1093    size = 32
1094    unsigned = True
1095    def __init__(self, value):
1096        if isinstance(value, MemorySize):
1097            self.value = value.value
1098        else:
1099            self.value = toMemorySize(value)
1100        self._check()
1101
1102class Addr(CheckedInt):
1103    size = 64
1104    unsigned = True
1105    def __init__(self, value):
1106        if isinstance(value, Addr):
1107            self.value = value.value
1108        else:
1109            try:
1110                self.value = toMemorySize(value)
1111            except TypeError:
1112                self.value = long(value)
1113        self._check()
1114
1115class AddrRange(Range):
1116    type = Addr
1117
1118# String-valued parameter.  Just mixin the ParamValue class
1119# with the built-in str class.
1120class String(ParamValue,str):
1121    pass
1122
1123# Boolean parameter type.  Python doesn't let you subclass bool, since
1124# it doesn't want to let you create multiple instances of True and
1125# False.  Thus this is a little more complicated than String.
1126class Bool(ParamValue):
1127    def __init__(self, value):
1128        try:
1129            self.value = toBool(value)
1130        except TypeError:
1131            self.value = bool(value)
1132
1133    def __str__(self):
1134        return str(self.value)
1135
1136    def ini_str(self):
1137        if self.value:
1138            return 'true'
1139        return 'false'
1140
1141def IncEthernetAddr(addr, val = 1):
1142    bytes = map(lambda x: int(x, 16), addr.split(':'))
1143    bytes[5] += val
1144    for i in (5, 4, 3, 2, 1):
1145        val,rem = divmod(bytes[i], 256)
1146        bytes[i] = rem
1147        if val == 0:
1148            break
1149        bytes[i - 1] += val
1150    assert(bytes[0] <= 255)
1151    return ':'.join(map(lambda x: '%02x' % x, bytes))
1152
1153class NextEthernetAddr(object):
1154    addr = "00:90:00:00:00:01"
1155
1156    def __init__(self, inc = 1):
1157        self.value = NextEthernetAddr.addr
1158        NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
1159
1160class EthernetAddr(ParamValue):
1161    def __init__(self, value):
1162        if value == NextEthernetAddr:
1163            self.value = value
1164            return
1165
1166        if not isinstance(value, str):
1167            raise TypeError, "expected an ethernet address and didn't get one"
1168
1169        bytes = value.split(':')
1170        if len(bytes) != 6:
1171            raise TypeError, 'invalid ethernet address %s' % value
1172
1173        for byte in bytes:
1174            if not 0 <= int(byte) <= 256:
1175                raise TypeError, 'invalid ethernet address %s' % value
1176
1177        self.value = value
1178
1179    def unproxy(self, base):
1180        if self.value == NextEthernetAddr:
1181            self.addr = self.value().value
1182        return self
1183
1184    def __str__(self):
1185        if self.value == NextEthernetAddr:
1186            if hasattr(self, 'addr'):
1187                return self.addr
1188            else:
1189                return "NextEthernetAddr (unresolved)"
1190        else:
1191            return self.value
1192
1193# Special class for NULL pointers.  Note the special check in
1194# make_param_value() above that lets these be assigned where a
1195# SimObject is required.
1196# only one copy of a particular node
1197class NullSimObject(object):
1198    __metaclass__ = Singleton
1199
1200    def __call__(cls):
1201        return cls
1202
1203    def _instantiate(self, parent = None, path = ''):
1204        pass
1205
1206    def ini_str(self):
1207        return 'Null'
1208
1209    def unproxy(self, base):
1210        return self
1211
1212    def set_path(self, parent, name):
1213        pass
1214    def __str__(self):
1215        return 'Null'
1216
1217# The only instance you'll ever need...
1218Null = NULL = NullSimObject()
1219
1220# Enumerated types are a little more complex.  The user specifies the
1221# type as Enum(foo) where foo is either a list or dictionary of
1222# alternatives (typically strings, but not necessarily so).  (In the
1223# long run, the integer value of the parameter will be the list index
1224# or the corresponding dictionary value.  For now, since we only check
1225# that the alternative is valid and then spit it into a .ini file,
1226# there's not much point in using the dictionary.)
1227
1228# What Enum() must do is generate a new type encapsulating the
1229# provided list/dictionary so that specific values of the parameter
1230# can be instances of that type.  We define two hidden internal
1231# classes (_ListEnum and _DictEnum) to serve as base classes, then
1232# derive the new type from the appropriate base class on the fly.
1233
1234
1235# Metaclass for Enum types
1236class MetaEnum(type):
1237    def __init__(cls, name, bases, init_dict):
1238        if init_dict.has_key('map'):
1239            if not isinstance(cls.map, dict):
1240                raise TypeError, "Enum-derived class attribute 'map' " \
1241                      "must be of type dict"
1242            # build list of value strings from map
1243            cls.vals = cls.map.keys()
1244            cls.vals.sort()
1245        elif init_dict.has_key('vals'):
1246            if not isinstance(cls.vals, list):
1247                raise TypeError, "Enum-derived class attribute 'vals' " \
1248                      "must be of type list"
1249            # build string->value map from vals sequence
1250            cls.map = {}
1251            for idx,val in enumerate(cls.vals):
1252                cls.map[val] = idx
1253        else:
1254            raise TypeError, "Enum-derived class must define "\
1255                  "attribute 'map' or 'vals'"
1256
1257        super(MetaEnum, cls).__init__(name, bases, init_dict)
1258
1259    def cpp_declare(cls):
1260        s = 'enum %s {\n    ' % cls.__name__
1261        s += ',\n    '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1262        s += '\n};\n'
1263        return s
1264
1265# Base class for enum types.
1266class Enum(ParamValue):
1267    __metaclass__ = MetaEnum
1268    vals = []
1269
1270    def __init__(self, value):
1271        if value not in self.map:
1272            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1273                  % (value, self.vals)
1274        self.value = value
1275
1276    def __str__(self):
1277        return self.value
1278
1279ticks_per_sec = None
1280
1281# how big does a rounding error need to be before we warn about it?
1282frequency_tolerance = 0.001  # 0.1%
1283
1284# convert a floting-point # of ticks to integer, and warn if rounding
1285# discards too much precision
1286def tick_check(float_ticks):
1287    if float_ticks == 0:
1288        return 0
1289    int_ticks = int(round(float_ticks))
1290    err = (float_ticks - int_ticks) / float_ticks
1291    if err > frequency_tolerance:
1292        print >> sys.stderr, "Warning: rounding error > tolerance"
1293        print >> sys.stderr, "    %f rounded to %d" % (float_ticks, int_ticks)
1294        #raise ValueError
1295    return int_ticks
1296
1297def getLatency(value):
1298    if isinstance(value, Latency) or isinstance(value, Clock):
1299        return value.value
1300    elif isinstance(value, Frequency) or isinstance(value, RootClock):
1301        return 1 / value.value
1302    elif isinstance(value, str):
1303        try:
1304            return toLatency(value)
1305        except ValueError:
1306            try:
1307                return 1 / toFrequency(value)
1308            except ValueError:
1309                pass # fall through
1310    raise ValueError, "Invalid Frequency/Latency value '%s'" % value
1311
1312
1313class Latency(NumericParamValue):
1314    def __init__(self, value):
1315        self.value = getLatency(value)
1316
1317    def __getattr__(self, attr):
1318        if attr in ('latency', 'period'):
1319            return self
1320        if attr == 'frequency':
1321            return Frequency(self)
1322        raise AttributeError, "Latency object has no attribute '%s'" % attr
1323
1324    # convert latency to ticks
1325    def ini_str(self):
1326        return str(tick_check(self.value * ticks_per_sec))
1327
1328class Frequency(NumericParamValue):
1329    def __init__(self, value):
1330        self.value = 1 / getLatency(value)
1331
1332    def __getattr__(self, attr):
1333        if attr == 'frequency':
1334            return self
1335        if attr in ('latency', 'period'):
1336            return Latency(self)
1337        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1338
1339    # convert frequency to ticks per period
1340    def ini_str(self):
1341        return self.period.ini_str()
1342
1343# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
1344# We can't inherit from Frequency because we don't want it to be directly
1345# assignable to a regular Frequency parameter.
1346class RootClock(ParamValue):
1347    def __init__(self, value):
1348        self.value = 1 / getLatency(value)
1349
1350    def __getattr__(self, attr):
1351        if attr == 'frequency':
1352            return Frequency(self)
1353        if attr in ('latency', 'period'):
1354            return Latency(self)
1355        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1356
1357    def ini_str(self):
1358        return str(tick_check(self.value))
1359
1360# A generic frequency and/or Latency value.  Value is stored as a latency,
1361# but to avoid ambiguity this object does not support numeric ops (* or /).
1362# An explicit conversion to a Latency or Frequency must be made first.
1363class Clock(ParamValue):
1364    def __init__(self, value):
1365        self.value = getLatency(value)
1366
1367    def __getattr__(self, attr):
1368        if attr == 'frequency':
1369            return Frequency(self)
1370        if attr in ('latency', 'period'):
1371            return Latency(self)
1372        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1373
1374    def ini_str(self):
1375        return self.period.ini_str()
1376
1377class NetworkBandwidth(float,ParamValue):
1378    def __new__(cls, value):
1379        val = toNetworkBandwidth(value) / 8.0
1380        return super(cls, NetworkBandwidth).__new__(cls, val)
1381
1382    def __str__(self):
1383        return str(self.val)
1384
1385    def ini_str(self):
1386        return '%f' % (ticks_per_sec / float(self))
1387
1388class MemoryBandwidth(float,ParamValue):
1389    def __new__(self, value):
1390        val = toMemoryBandwidth(value)
1391        return super(cls, MemoryBandwidth).__new__(cls, val)
1392
1393    def __str__(self):
1394        return str(self.val)
1395
1396    def ini_str(self):
1397        return '%f' % (ticks_per_sec / float(self))
1398
1399#
1400# "Constants"... handy aliases for various values.
1401#
1402
1403# Some memory range specifications use this as a default upper bound.
1404MaxAddr = Addr.max
1405MaxTick = Tick.max
1406AllMemory = AddrRange(0, MaxAddr)
1407
1408
1409#####################################################################
1410#
1411# Port objects
1412#
1413# Ports are used to interconnect objects in the memory system.
1414#
1415#####################################################################
1416
1417# Port reference: encapsulates a reference to a particular port on a
1418# particular SimObject.
1419class PortRef(object):
1420    def __init__(self, simobj, name, isVec):
1421        assert(isSimObject(simobj))
1422        self.simobj = simobj
1423        self.name = name
1424        self.index = -1
1425        self.isVec = isVec # is this a vector port?
1426        self.peer = None   # not associated with another port yet
1427        self.ccConnected = False # C++ port connection done?
1428
1429    # Set peer port reference.  Called via __setattr__ as a result of
1430    # a port assignment, e.g., "obj1.port1 = obj2.port2".
1431    def setPeer(self, other):
1432        if self.isVec:
1433            curMap = self.simobj._port_map.get(self.name, [])
1434            self.index = len(curMap)
1435            curMap.append(other)
1436        else:
1437            curMap = self.simobj._port_map.get(self.name)
1438            if curMap and not self.isVec:
1439                print "warning: overwriting port", self.simobj, self.name
1440            curMap = other
1441        self.simobj._port_map[self.name] = curMap
1442        self.peer = other
1443
1444    def clone(self, memo):
1445        newRef = copy.copy(self)
1446        assert(isSimObject(newRef.simobj))
1447        newRef.simobj = newRef.simobj(_memo=memo)
1448        # Tricky: if I'm the *second* PortRef in the pair to be
1449        # cloned, then my peer is still in the middle of its clone
1450        # method, and thus hasn't returned to its owner's
1451        # SimObject.__init__ to get installed in _port_map.  As a
1452        # result I have no way of finding the *new* peer object.  So I
1453        # mark myself as "waiting" for my peer, and I let the *first*
1454        # PortRef clone call set up both peer pointers after I return.
1455        newPeer = newRef.simobj._port_map.get(self.name)
1456        if newPeer:
1457            if self.isVec:
1458                assert(self.index != -1)
1459                newPeer = newPeer[self.index]
1460            # other guy is all set up except for his peer pointer
1461            assert(newPeer.peer == -1) # peer must be waiting for handshake
1462            newPeer.peer = newRef
1463            newRef.peer = newPeer
1464        else:
1465            # other guy is in clone; just wait for him to do the work
1466            newRef.peer = -1 # mark as waiting for handshake
1467        return newRef
1468
1469    # Call C++ to create corresponding port connection between C++ objects
1470    def ccConnect(self):
1471        if self.ccConnected: # already done this
1472            return
1473        peer = self.peer
1474        cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
1475                             peer.simobj.getCCObject(), peer.name, peer.index)
1476        self.ccConnected = True
1477        peer.ccConnected = True
1478
1479# Port description object.  Like a ParamDesc object, this represents a
1480# logical port in the SimObject class, not a particular port on a
1481# SimObject instance.  The latter are represented by PortRef objects.
1482class Port(object):
1483    def __init__(self, desc):
1484        self.desc = desc
1485        self.isVec = False
1486
1487    # Generate a PortRef for this port on the given SimObject with the
1488    # given name
1489    def makeRef(self, simobj, name):
1490        return PortRef(simobj, name, self.isVec)
1491
1492    # Connect an instance of this port (on the given SimObject with
1493    # the given name) with the port described by the supplied PortRef
1494    def connect(self, simobj, name, ref):
1495        if not isinstance(ref, PortRef):
1496            raise TypeError, \
1497                  "assigning non-port reference port '%s'" % name
1498        myRef = self.makeRef(simobj, name)
1499        myRef.setPeer(ref)
1500        ref.setPeer(myRef)
1501
1502# VectorPort description object.  Like Port, but represents a vector
1503# of connections (e.g., as on a Bus).
1504class VectorPort(Port):
1505    def __init__(self, desc):
1506        Port.__init__(self, desc)
1507        self.isVec = True
1508
1509#####################################################################
1510
1511# __all__ defines the list of symbols that get exported when
1512# 'from config import *' is invoked.  Try to keep this reasonably
1513# short to avoid polluting other namespaces.
1514__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam',
1515           'Parent', 'Self',
1516           'Enum', 'Bool', 'String', 'Float',
1517           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1518           'Int32', 'UInt32', 'Int64', 'UInt64',
1519           'Counter', 'Addr', 'Tick', 'Percent',
1520           'TcpPort', 'UdpPort', 'EthernetAddr',
1521           'MemorySize', 'MemorySize32',
1522           'Latency', 'Frequency', 'RootClock', 'Clock',
1523           'NetworkBandwidth', 'MemoryBandwidth',
1524           'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory',
1525           'Null', 'NULL',
1526           'NextEthernetAddr',
1527           'Port', 'VectorPort']
1528
1529