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