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