Get Port stuff working with full-system scripts.
authorSteve Reinhardt <stever@eecs.umich.edu>
Thu, 15 Jun 2006 15:45:51 +0000 (11:45 -0400)
committerSteve Reinhardt <stever@eecs.umich.edu>
Thu, 15 Jun 2006 15:45:51 +0000 (11:45 -0400)
Key was adding support for cloning port references (trickier than it sounds).
Got rid of class/instance thing and go back to instance cloning...
still don't allow changing SimObject parameters/children after a
class (instance) has been subclassed or instantiated (or cloned), which
should avoid bizarre unintended behavior.

configs/test/fs.py:
    Add ".port" to busses to get a port reference.
    Get rid of commented-out code.
src/python/m5/__init__.py:
    resolveSimObject should call getCCObject() instead of createCCObject()
    to avoid cycles in recursively creating objects.
src/python/m5/config.py:
    Get rid of class/instance thing and go back to instance cloning.
    Deep copy has to happen only on instance cloning then (and not on subclassing).
    Add getCCObject() method to force creation of C++ SimObject without
    recursively creating its children.
    Add support for cloning port references (trickier than it sounds).
    Also clean up some very obsolete comments.
src/python/m5/objects/Bridge.py:
src/python/m5/objects/Device.py:
    Add ports.

--HG--
extra : convert_revision : 4816d05ead0de520748aace06dbd1911a33f0af8

configs/test/fs.py
src/python/m5/__init__.py
src/python/m5/config.py
src/python/m5/objects/Bridge.py
src/python/m5/objects/Device.py

index 333a4dfafe50945fdc7883dfdcebdc378a0f247c..c742e916ca5e85d7150d87485db1cc034d7c7472 100644 (file)
@@ -129,17 +129,7 @@ class BaseTsunami(Tsunami):
     ethernet = NSGigE(configdata=NSGigEPciData(),
                       pci_bus=0, pci_dev=1, pci_func=0)
     etherint = NSGigEInt(device=Parent.ethernet)
-#    ethernet = Sinic(configdata=SinicPciData(),
-#                      pci_bus=0, pci_dev=1, pci_func=0)
-#    etherint = SinicInt(device=Parent.ethernet)
     console = AlphaConsole(pio_addr=0x80200000000, disk=Parent.simple_disk)
-#    bridge = PciFake(configdata=BridgePciData(), pci_bus=0, pci_dev=2, pci_func=0)
-
-#class FreeBSDTsunami(BaseTsunami):
-#    disk0 = FreeBSDRootDisk(delay='0us', driveID='master')
-#    ide = IdeController(disks=[Parent.disk0],
-#                        configdata=IdeControllerPciData(),
-#                        pci_func=0, pci_dev=0, pci_bus=0)
 
 class LinuxTsunami(BaseTsunami):
     disk0 = LinuxRootDisk(driveID='master')
@@ -149,45 +139,45 @@ class LinuxTsunami(BaseTsunami):
                         configdata=IdeControllerPciData(),
                         pci_func=0, pci_dev=0, pci_bus=0)
 
-class LinuxAlphaSystem(LinuxAlphaSystem):
+class MyLinuxAlphaSystem(LinuxAlphaSystem):
     magicbus = Bus(bus_id=0)
     magicbus2 = Bus(bus_id=1)
     bridge = Bridge()
     physmem = PhysicalMemory(range = AddrRange('128MB'))
-    bridge.side_a = magicbus
-    bridge.side_b = magicbus2
-    c1 = Connector(side_a=Parent.physmem, side_b=Parent.magicbus2)
+    bridge.side_a = magicbus.port
+    bridge.side_b = magicbus2.port
+    physmem.port = magicbus2.port
     tsunami = LinuxTsunami()
-    tsunami.cchip.pio = magicbus
-    tsunami.pchip.pio = magicbus
-    tsunami.pciconfig.pio = magicbus
-    tsunami.fake_sm_chip.pio = magicbus
-    tsunami.ethernet.pio = magicbus
-    tsunami.ethernet.dma = magicbus
-    tsunami.fake_uart1.pio = magicbus
-    tsunami.fake_uart2.pio = magicbus
-    tsunami.fake_uart3.pio = magicbus
-    tsunami.fake_uart4.pio = magicbus
-    tsunami.ide.pio = magicbus
-    tsunami.ide.dma = magicbus
-    tsunami.fake_ppc.pio = magicbus
-    tsunami.fake_OROM.pio = magicbus
-    tsunami.fake_pnp_addr.pio = magicbus
-    tsunami.fake_pnp_write.pio = magicbus
-    tsunami.fake_pnp_read0.pio = magicbus
-    tsunami.fake_pnp_read1.pio = magicbus
-    tsunami.fake_pnp_read2.pio = magicbus
-    tsunami.fake_pnp_read3.pio = magicbus
-    tsunami.fake_pnp_read4.pio = magicbus
-    tsunami.fake_pnp_read5.pio = magicbus
-    tsunami.fake_pnp_read6.pio = magicbus
-    tsunami.fake_pnp_read7.pio = magicbus
-    tsunami.fake_ata0.pio = magicbus
-    tsunami.fake_ata1.pio = magicbus
-    tsunami.fb.pio = magicbus
-    tsunami.io.pio = magicbus
-    tsunami.uart.pio = magicbus
-    tsunami.console.pio = magicbus
+    tsunami.cchip.pio = magicbus.port
+    tsunami.pchip.pio = magicbus.port
+    tsunami.pciconfig.pio = magicbus.port
+    tsunami.fake_sm_chip.pio = magicbus.port
+    tsunami.ethernet.pio = magicbus.port
+    tsunami.ethernet.dma = magicbus.port
+    tsunami.fake_uart1.pio = magicbus.port
+    tsunami.fake_uart2.pio = magicbus.port
+    tsunami.fake_uart3.pio = magicbus.port
+    tsunami.fake_uart4.pio = magicbus.port
+    tsunami.ide.pio = magicbus.port
+    tsunami.ide.dma = magicbus.port
+    tsunami.fake_ppc.pio = magicbus.port
+    tsunami.fake_OROM.pio = magicbus.port
+    tsunami.fake_pnp_addr.pio = magicbus.port
+    tsunami.fake_pnp_write.pio = magicbus.port
+    tsunami.fake_pnp_read0.pio = magicbus.port
+    tsunami.fake_pnp_read1.pio = magicbus.port
+    tsunami.fake_pnp_read2.pio = magicbus.port
+    tsunami.fake_pnp_read3.pio = magicbus.port
+    tsunami.fake_pnp_read4.pio = magicbus.port
+    tsunami.fake_pnp_read5.pio = magicbus.port
+    tsunami.fake_pnp_read6.pio = magicbus.port
+    tsunami.fake_pnp_read7.pio = magicbus.port
+    tsunami.fake_ata0.pio = magicbus.port
+    tsunami.fake_ata1.pio = magicbus.port
+    tsunami.fb.pio = magicbus.port
+    tsunami.io.pio = magicbus.port
+    tsunami.uart.pio = magicbus.port
+    tsunami.console.pio = magicbus.port
     raw_image = RawDiskImage(image_file=disk('linux-latest.img'),
                              read_only=True)
     simple_disk = SimpleDisk(disk=Parent.raw_image)
@@ -196,7 +186,7 @@ class LinuxAlphaSystem(LinuxAlphaSystem):
         cpu = TimingSimpleCPU()
     else:
         cpu = AtomicSimpleCPU()
-    cpu.mem = Parent.magicbus2
+    cpu.mem = magicbus2
     cpu.itb = AlphaITB()
     cpu.dtb = AlphaDTB()
     sim_console = SimConsole(listener=ConsoleListener(port=3456))
@@ -224,11 +214,12 @@ def DualRoot(clientSystem, serverSystem):
     self.clock = '5GHz'
     return self
 
-root = DualRoot(LinuxAlphaSystem(readfile=script('netperf-stream-nt-client.rcS')),
-                LinuxAlphaSystem(readfile=script('netperf-server.rcS')))
+root = DualRoot(
+    MyLinuxAlphaSystem(readfile=script('netperf-stream-nt-client.rcS')),
+    MyLinuxAlphaSystem(readfile=script('netperf-server.rcS')))
 
 m5.instantiate(root)
 
 exit_event = m5.simulate()
 
-print 'Exiting @', m5.curTick(), 'because', exit_event.getCause()
+print 'Exiting @ cycle', m5.curTick(), 'because', exit_event.getCause()
index a4fc9a5e399adb3456f46d7daae97639ba33ebc8..f849a899bed2ae3c8bc39d57cf292c32f653754b 100644 (file)
@@ -107,11 +107,7 @@ env.update(os.environ)
 # Function to provide to C++ so it can look up instances based on paths
 def resolveSimObject(name):
     obj = config.instanceDict[name]
-    if not obj._ccObject:
-        obj.createCCObject()
-    if obj._ccObject == -1:
-        panic("resolveSimObject: recursive lookup error on %s" % name)
-    return obj._ccObject
+    return obj.getCCObject()
 
 # The final hook to generate .ini files.  Called from the user script
 # once the config is built.
index f23fd2c6fa37d834d32332cbf350243a5f952911..058e7257824db1f7adb3c972d21ba9926002fb3d 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2004-2005 The Regents of The University of Michigan
+# Copyright (c) 2004-2006 The Regents of The University of Michigan
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,7 +27,7 @@
 # Authors: Steve Reinhardt
 #          Nathan Binkert
 
-import os, re, sys, types, inspect
+import os, re, sys, types, inspect, copy
 
 import m5
 from m5 import panic
@@ -84,69 +84,22 @@ class Singleton(type):
 #
 # Once a set of Python objects have been instantiated in a hierarchy,
 # calling 'instantiate(obj)' (where obj is the root of the hierarchy)
-# will generate a .ini file.  See simple-4cpu.py for an example
-# (corresponding to m5-test/simple-4cpu.ini).
+# will generate a .ini file.
 #
 #####################################################################
 
-#####################################################################
-#
-# ConfigNode/SimObject classes
-#
-# The Python class hierarchy rooted by ConfigNode (which is the base
-# class of SimObject, which in turn is the base class of all other M5
-# SimObject classes) has special attribute behavior.  In general, an
-# object in this hierarchy has three categories of attribute-like
-# things:
-#
-# 1. Regular Python methods and variables.  These must start with an
-# underscore to be treated normally.
-#
-# 2. SimObject parameters.  These values are stored as normal Python
-# attributes, but all assignments to these attributes are checked
-# against the pre-defined set of parameters stored in the class's
-# _params dictionary.  Assignments to attributes that do not
-# correspond to predefined parameters, or that are not of the correct
-# type, incur runtime errors.
-#
-# 3. Hierarchy children.  The child nodes of a ConfigNode are stored
-# in the node's _children dictionary, but can be accessed using the
-# Python attribute dot-notation (just as they are printed out by the
-# simulator).  Children cannot be created using attribute assigment;
-# they must be added by specifying the parent node in the child's
-# constructor or using the '+=' operator.
-
-# The SimObject parameters are the most complex, for a few reasons.
-# First, both parameter descriptions and parameter values are
-# inherited.  Thus parameter description lookup must go up the
-# inheritance chain like normal attribute lookup, but this behavior
-# must be explicitly coded since the lookup occurs in each class's
-# _params attribute.  Second, because parameter values can be set
-# on SimObject classes (to implement default values), the parameter
-# checking behavior must be enforced on class attribute assignments as
-# well as instance attribute assignments.  Finally, because we allow
-# class specialization via inheritance (e.g., see the L1Cache class in
-# the simple-4cpu.py example), we must do parameter checking even on
-# class instantiation.  To provide all these features, we use a
-# metaclass to define most of the SimObject parameter behavior for
-# this class hierarchy.
-#
-#####################################################################
-
-
 # dict to look up SimObjects based on path
 instanceDict = {}
 
+#############################
+#
+# Utility methods
+#
+#############################
+
 def isSimObject(value):
     return isinstance(value, SimObject)
 
-def isSimObjectClass(value):
-    try:
-        return issubclass(value, SimObject)
-    except TypeError:
-        # happens if value is not a class at all
-        return False
-
 def isSimObjectSequence(value):
     if not isinstance(value, (list, tuple)) or len(value) == 0:
         return False
@@ -157,22 +110,9 @@ def isSimObjectSequence(value):
 
     return True
 
-def isSimObjectClassSequence(value):
-    if not isinstance(value, (list, tuple)) or len(value) == 0:
-        return False
-
-    for val in value:
-        if not isNullPointer(val) and not isSimObjectClass(val):
-            return False
-
-    return True
-
 def isSimObjectOrSequence(value):
     return isSimObject(value) or isSimObjectSequence(value)
 
-def isSimObjectClassOrSequence(value):
-    return isSimObjectClass(value) or isSimObjectClassSequence(value)
-
 def isNullPointer(value):
     return isinstance(value, NullSimObject)
 
@@ -192,41 +132,36 @@ def applyOrMap(objOrSeq, meth, *args, **kwargs):
         return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq]
 
 
-# The metaclass for ConfigNode (and thus for everything that derives
-# from ConfigNode, including SimObject).  This class controls how new
-# classes that derive from ConfigNode are instantiated, and provides
-# inherited class behavior (just like a class controls how instances
-# of that class are instantiated, and provides inherited instance
-# behavior).
+# The metaclass for SimObject.  This class controls how new classes
+# that derive from SimObject are instantiated, and provides inherited
+# class behavior (just like a class controls how instances of that
+# class are instantiated, and provides inherited instance behavior).
 class MetaSimObject(type):
     # Attributes that can be set only at initialization time
     init_keywords = { 'abstract' : types.BooleanType,
                       'type' : types.StringType }
     # Attributes that can be set any time
-    keywords = { 'check' : types.FunctionType,
-                 'children' : types.ListType,
-                 'ccObject' : types.ObjectType }
+    keywords = { 'check' : types.FunctionType }
 
     # __new__ is called before __init__, and is where the statements
     # in the body of the class definition get loaded into the class's
-    # __dict__.  We intercept this to filter out parameter assignments
+    # __dict__.  We intercept this to filter out parameter & port assignments
     # and only allow "private" attributes to be passed to the base
     # __new__ (starting with underscore).
     def __new__(mcls, name, bases, dict):
-        if dict.has_key('_init_dict'):
-            # must have been called from makeSubclass() rather than
-            # via Python class declaration; bypass filtering process.
-            cls_dict = dict
-        else:
-            # Copy "private" attributes (including special methods
-            # such as __new__) to the official dict.  Everything else
-            # goes in _init_dict to be filtered in __init__.
-            cls_dict = {}
-            for key,val in dict.items():
-                if key.startswith('_'):
-                    cls_dict[key] = val
-                    del dict[key]
-            cls_dict['_init_dict'] = dict
+        # Copy "private" attributes, functions, and classes to the
+        # official dict.  Everything else goes in _init_dict to be
+        # filtered in __init__.
+        cls_dict = {}
+        value_dict = {}
+        for key,val in dict.items():
+            if key.startswith('_') or isinstance(val, (types.FunctionType,
+                                                       types.TypeType)):
+                cls_dict[key] = val
+            else:
+                # must be a param/port setting
+                value_dict[key] = val
+        cls_dict['_value_dict'] = value_dict
         return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
 
     # subclass initialization
@@ -236,11 +171,15 @@ class MetaSimObject(type):
         super(MetaSimObject, cls).__init__(name, bases, dict)
 
         # initialize required attributes
-        cls._params = multidict()
-        cls._values = multidict()
-        cls._ports = multidict()
-        cls._instantiated = False # really instantiated or subclassed
-        cls._anon_subclass_counter = 0
+
+        # class-only attributes
+        cls._params = multidict() # param descriptions
+        cls._ports = multidict()  # port descriptions
+
+        # class or instance attributes
+        cls._values = multidict()   # param values
+        cls._port_map = multidict() # port bindings
+        cls._instantiated = False # really instantiated, cloned, or subclassed
 
         # We don't support multiple inheritance.  If you want to, you
         # must fix multidict to deal with it properly.
@@ -249,21 +188,28 @@ class MetaSimObject(type):
 
         base = bases[0]
 
-        # the only time the following is not true is when we define
-        # the SimObject class itself
+        # Set up general inheritance via multidicts.  A subclass will
+        # inherit all its settings from the base class.  The only time
+        # the following is not true is when we define the SimObject
+        # class itself (in which case the multidicts have no parent).
         if isinstance(base, MetaSimObject):
             cls._params.parent = base._params
-            cls._values.parent = base._values
             cls._ports.parent = base._ports
+            cls._values.parent = base._values
+            cls._port_map.parent = base._port_map
+            # mark base as having been subclassed
             base._instantiated = True
 
-        # now process the _init_dict items
-        for key,val in cls._init_dict.items():
-            if isinstance(val, (types.FunctionType, types.TypeType)):
-                type.__setattr__(cls, key, val)
-
+        # Now process the _value_dict items.  They could be defining
+        # new (or overriding existing) parameters or ports, setting
+        # class keywords (e.g., 'abstract'), or setting parameter
+        # values or port bindings.  The first 3 can only be set when
+        # the class is defined, so we handle them here.  The others
+        # can be set later too, so just emulate that by calling
+        # setattr().
+        for key,val in cls._value_dict.items():
             # param descriptions
-            elif isinstance(val, ParamDesc):
+            if isinstance(val, ParamDesc):
                 cls._new_param(key, val)
 
             # port objects
@@ -278,27 +224,6 @@ class MetaSimObject(type):
             else:
                 setattr(cls, key, val)
 
-        # Pull the deep-copy memoization dict out of the class dict if
-        # it's there...
-        memo = cls.__dict__.get('_memo', {})
-
-        # Handle SimObject values
-        for key,val in cls._values.iteritems():
-            # SimObject instances need to be promoted to classes.
-            # Existing classes should not have any instance values, so
-            # these can only occur at the lowest level dict (the
-            # parameters just being set in this class definition).
-            if isSimObjectOrSequence(val):
-                assert(val == cls._values.local[key])
-                cls._values[key] = applyOrMap(val, 'makeClass', memo)
-            # SimObject classes need to be subclassed so that
-            # parameters that get set at this level only affect this
-            # level and derivatives.
-            elif isSimObjectClassOrSequence(val):
-                assert(not cls._values.local.has_key(key))
-                cls._values[key] = applyOrMap(val, 'makeSubclass', {}, memo)
-
-
     def _set_keyword(cls, keyword, val, kwtype):
         if not isinstance(val, kwtype):
             raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
@@ -328,15 +253,15 @@ class MetaSimObject(type):
             self._ports[attr].connect(self, attr, value)
             return
 
-        # must be SimObject param
-        param = cls._params.get(attr, None)
-        if param:
-            # It's ok: set attribute by delegating to 'object' class.
-            if isSimObjectOrSequence(value) and cls._instantiated:
-                raise AttributeError, \
-                  "Cannot set SimObject parameter '%s' after\n" \
+        if isSimObjectOrSequence(value) and cls._instantiated:
+            raise RuntimeError, \
+                  "cannot set SimObject parameter '%s' after\n" \
                   "    class %s has been instantiated or subclassed" \
                   % (attr, cls.__name__)
+
+        # check for param
+        param = cls._params.get(attr, None)
+        if param:
             try:
                 cls._values[attr] = param.convert(value)
             except Exception, e:
@@ -344,9 +269,9 @@ class MetaSimObject(type):
                       (e, cls.__name__, attr, value)
                 e.args = (msg, )
                 raise
-        # I would love to get rid of this
         elif isSimObjectOrSequence(value):
-           cls._values[attr] = value
+            # if RHS is a SimObject, it's an implicit child assignment
+            cls._values[attr] = value
         else:
             raise AttributeError, \
                   "Class %s has no parameter %s" % (cls.__name__, attr)
@@ -358,23 +283,7 @@ class MetaSimObject(type):
         raise AttributeError, \
               "object '%s' has no attribute '%s'" % (cls.__name__, attr)
 
-    # Create a subclass of this class.  Basically a function interface
-    # to the standard Python class definition mechanism, primarily for
-    # internal use.  'memo' dict param supports "deep copy" (really
-    # "deep subclass") operations... within a given operation,
-    # multiple references to a class should result in a single
-    # subclass object with multiple references to it (as opposed to
-    # mutiple unique subclasses).
-    def makeSubclass(cls, init_dict, memo = {}):
-        subcls = memo.get(cls)
-        if not subcls:
-            name = cls.__name__ + '_' + str(cls._anon_subclass_counter)
-            cls._anon_subclass_counter += 1
-            subcls = MetaSimObject(name, (cls,),
-                                   { '_init_dict': init_dict, '_memo': memo })
-        return subcls
-
-# The ConfigNode class is the root of the special hierarchy.  Most of
+# The SimObject class is the root of the special hierarchy.  Most of
 # the code in this class deals with the configuration hierarchy itself
 # (parent/child node relationships).
 class SimObject(object):
@@ -382,83 +291,72 @@ class SimObject(object):
     # get this metaclass.
     __metaclass__ = MetaSimObject
 
-    # __new__ operator allocates new instances of the class.  We
-    # override it here just to support "deep instantiation" operation
-    # via the _memo dict.  When recursively instantiating an object
-    # hierarchy we want to make sure that each class is instantiated
-    # only once, and that if there are multiple references to the same
-    # original class, we end up with the corresponding instantiated
-    # references all pointing to the same instance.
-    def __new__(cls, _memo = None, **kwargs):
-        if _memo is not None and _memo.has_key(cls):
-            # return previously instantiated object
-            assert(len(kwargs) == 0)
-            return _memo[cls]
-        else:
-            # Need a new one... if it needs to be memoized, this will
-            # happen in __init__.  We defer the insertion until then
-            # so __init__ can use the memo dict to tell whether or not
-            # to perform the initialization.
-            return super(SimObject, cls).__new__(cls, **kwargs)
-
-    # Initialize new instance previously allocated by __new__.  For
-    # objects with SimObject-valued params, we need to recursively
-    # instantiate the classes represented by those param values as
-    # well (in a consistent "deep copy"-style fashion; see comment
-    # above).
-    def __init__(self, _memo = None, **kwargs):
-        if _memo is not None:
-            # We're inside a "deep instantiation"
-            assert(isinstance(_memo, dict))
-            assert(len(kwargs) == 0)
-            if _memo.has_key(self.__class__):
-                # __new__ returned an existing, already initialized
-                # instance, so there's nothing to do here
-                assert(_memo[self.__class__] == self)
-                return
-            # no pre-existing object, so remember this one here
-            _memo[self.__class__] = self
-        else:
-            # This is a new top-level instantiation... don't memoize
-            # this objcet, but prepare to memoize any recursively
-            # instantiated objects.
-            _memo = {}
-
-        self.__class__._instantiated = True
+    # Initialize new instance.  For objects with SimObject-valued
+    # children, we need to recursively clone the classes represented
+    # by those param values as well in a consistent "deep copy"-style
+    # fashion.  That is, we want to make sure that each instance is
+    # cloned only once, and that if there are multiple references to
+    # the same original object, we end up with the corresponding
+    # cloned references all pointing to the same cloned instance.
+    def __init__(self, **kwargs):
+        ancestor = kwargs.get('_ancestor')
+        memo_dict = kwargs.get('_memo')
+        if memo_dict is None:
+            # prepare to memoize any recursively instantiated objects
+            memo_dict = {}
+        elif ancestor:
+            # memoize me now to avoid problems with recursive calls
+            memo_dict[ancestor] = self
+
+        if not ancestor:
+            ancestor = self.__class__
+        ancestor._instantiated = True
 
+        # initialize required attributes
+        self._parent = None
         self._children = {}
+        self._ccObject = None  # pointer to C++ object
+        self._instantiated = False # really "cloned"
+
         # Inherit parameter values from class using multidict so
         # individual value settings can be overridden.
-        self._values = multidict(self.__class__._values)
-        # For SimObject-valued parameters, the class should have
-        # classes (not instances) for the values.  We need to
-        # instantiate these classes rather than just inheriting the
-        # class object.
-        for key,val in self.__class__._values.iteritems():
-            if isSimObjectClass(val):
-                setattr(self, key, val(_memo))
-            elif isSimObjectClassSequence(val) and len(val):
-                setattr(self, key, [ v(_memo) for v in val ])
+        self._values = multidict(ancestor._values)
+        # clone SimObject-valued parameters
+        for key,val in ancestor._values.iteritems():
+            if isSimObject(val):
+                setattr(self, key, val(_memo=memo_dict))
+            elif isSimObjectSequence(val) and len(val):
+                setattr(self, key, [ v(_memo=memo_dict) for v in val ])
+        # clone port references.  no need to use a multidict here
+        # since we will be creating new references for all ports.
+        self._port_map = {}
+        for key,val in ancestor._port_map.iteritems():
+            self._port_map[key] = applyOrMap(val, 'clone', memo_dict)
         # apply attribute assignments from keyword args, if any
         for key,val in kwargs.iteritems():
             setattr(self, key, val)
 
-        self._ccObject = None  # pointer to C++ object
-        self._port_map = {}    # map of port connections
-
-    # Use this instance as a template to create a new class.
-    def makeClass(self, memo = {}):
-        cls = memo.get(self)
-        if not cls:
-            cls =  self.__class__.makeSubclass(self._values.local)
-            memo[self] = cls
-        return cls
-
-    # Direct instantiation of instances (cloning) is no longer
-    # allowed; must generate class from instance first.
+    # "Clone" the current instance by creating another instance of
+    # this instance's class, but that inherits its parameter values
+    # and port mappings from the current instance.  If we're in a
+    # "deep copy" recursive clone, check the _memo dict to see if
+    # we've already cloned this instance.
     def __call__(self, **kwargs):
-        raise TypeError, "cannot instantiate SimObject; "\
-              "use makeClass() to make class first"
+        memo_dict = kwargs.get('_memo')
+        if memo_dict is None:
+            # no memo_dict: must be top-level clone operation.
+            # this is only allowed at the root of a hierarchy
+            if self._parent:
+                raise RuntimeError, "attempt to clone object %s " \
+                      "not at the root of a tree (parent = %s)" \
+                      % (self, self._parent)
+            # create a new dict and use that.
+            memo_dict = {}
+            kwargs['_memo'] = memo_dict
+        elif memo_dict.has_key(self):
+            # clone already done & memoized
+            return memo_dict[self]
+        return self.__class__(_ancestor = self, **kwargs)
 
     def __getattr__(self, attr):
         if self._ports.has_key(attr):
@@ -485,10 +383,14 @@ class SimObject(object):
             self._ports[attr].connect(self, attr, value)
             return
 
+        if isSimObjectOrSequence(value) and self._instantiated:
+            raise RuntimeError, \
+                  "cannot set SimObject parameter '%s' after\n" \
+                  "    instance been cloned %s" % (attr, `self`)
+
         # must be SimObject param
         param = self._params.get(attr, None)
         if param:
-            # It's ok: set attribute by delegating to 'object' class.
             try:
                 value = param.convert(value)
             except Exception, e:
@@ -496,7 +398,6 @@ class SimObject(object):
                       (e, self.__class__.__name__, attr, value)
                 e.args = (msg, )
                 raise
-        # I would love to get rid of this
         elif isSimObjectOrSequence(value):
             pass
         else:
@@ -535,13 +436,13 @@ class SimObject(object):
         self._children[name] = value
 
     def set_path(self, parent, name):
-        if not hasattr(self, '_parent'):
+        if not self._parent:
             self._parent = parent
             self._name = name
             parent.add_child(name, self)
 
     def path(self):
-        if not hasattr(self, '_parent'):
+        if not self._parent:
             return 'root'
         ppath = self._parent.path()
         if ppath == 'root':
@@ -618,13 +519,22 @@ class SimObject(object):
     # Call C++ to create C++ object corresponding to this object and
     # (recursively) all its children
     def createCCObject(self):
-        if self._ccObject:
-            return
-        self._ccObject = -1
-        self._ccObject = m5.main.createSimObject(self.path())
+        self.getCCObject() # force creation
         for child in self._children.itervalues():
             child.createCCObject()
 
+    # Get C++ object corresponding to this object, calling C++ if
+    # necessary to construct it.  Does *not* recursively create
+    # children.
+    def getCCObject(self):
+        if not self._ccObject:
+            self._ccObject = -1 # flag to catch cycles in recursion
+            self._ccObject = m5.main.createSimObject(self.path())
+        elif self._ccObject == -1:
+            raise RuntimeError, "%s: recursive call to getCCObject()" \
+                  % self.path()
+        return self._ccObject
+
     # Create C++ port connections corresponding to the connections in
     # _port_map (& recursively for all children)
     def connectPorts(self):
@@ -723,9 +633,9 @@ class BaseProxy(object):
 
         if self._search_up:
             while not done:
-                try: obj = obj._parent
-                except: break
-
+                obj = obj._parent
+                if not obj:
+                    break
                 result, done = self.find(obj)
 
         if not done:
@@ -841,16 +751,16 @@ Self = ProxyFactory(search_self = True, search_up = False)
 #
 # Parameter description classes
 #
-# The _params dictionary in each class maps parameter names to
-# either a Param or a VectorParam object.  These objects contain the
+# The _params dictionary in each class maps parameter names to either
+# a Param or a VectorParam object.  These objects contain the
 # parameter description string, the parameter type, and the default
-# value (loaded from the PARAM section of the .odesc files).  The
-# _convert() method on these objects is used to force whatever value
-# is assigned to the parameter to the appropriate type.
+# value (if any).  The convert() method on these objects is used to
+# force whatever value is assigned to the parameter to the appropriate
+# type.
 #
 # Note that the default values are loaded into the class's attribute
 # space when the parameter dictionary is initialized (in
-# MetaConfigNode._setparams()); after that point they aren't used.
+# MetaSimObject._new_param()); after that point they aren't used.
 #
 #####################################################################
 
@@ -1480,6 +1390,7 @@ AllMemory = AddrRange(0, MaxAddr)
 # particular SimObject.
 class PortRef(object):
     def __init__(self, simobj, name, isVec):
+        assert(isSimObject(simobj))
         self.simobj = simobj
         self.name = name
         self.index = -1
@@ -1502,13 +1413,38 @@ class PortRef(object):
         self.simobj._port_map[self.name] = curMap
         self.peer = other
 
+    def clone(self, memo):
+        newRef = copy.copy(self)
+        assert(isSimObject(newRef.simobj))
+        newRef.simobj = newRef.simobj(_memo=memo)
+        # Tricky: if I'm the *second* PortRef in the pair to be
+        # cloned, then my peer is still in the middle of its clone
+        # method, and thus hasn't returned to its owner's
+        # SimObject.__init__ to get installed in _port_map.  As a
+        # result I have no way of finding the *new* peer object.  So I
+        # mark myself as "waiting" for my peer, and I let the *first*
+        # PortRef clone call set up both peer pointers after I return.
+        newPeer = newRef.simobj._port_map.get(self.name)
+        if newPeer:
+            if self.isVec:
+                assert(self.index != -1)
+                newPeer = newPeer[self.index]
+            # other guy is all set up except for his peer pointer
+            assert(newPeer.peer == -1) # peer must be waiting for handshake
+            newPeer.peer = newRef
+            newRef.peer = newPeer
+        else:
+            # other guy is in clone; just wait for him to do the work
+            newRef.peer = -1 # mark as waiting for handshake
+        return newRef
+
     # Call C++ to create corresponding port connection between C++ objects
     def ccConnect(self):
         if self.ccConnected: # already done this
             return
         peer = self.peer
-        m5.main.connectPorts(self.simobj._ccObject, self.name, self.index,
-                             peer.simobj._ccObject, peer.name, peer.index)
+        m5.main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
+                             peer.simobj.getCCObject(), peer.name, peer.index)
         self.ccConnected = True
         peer.ccConnected = True
 
@@ -1528,6 +1464,9 @@ class Port(object):
     # Connect an instance of this port (on the given SimObject with
     # the given name) with the port described by the supplied PortRef
     def connect(self, simobj, name, ref):
+        if not isinstance(ref, PortRef):
+            raise TypeError, \
+                  "assigning non-port reference port '%s'" % name
         myRef = self.makeRef(simobj, name)
         myRef.setPeer(ref)
         ref.setPeer(myRef)
index 88053575527c1614520f25056c650fe7d8ad5942..c9e673afb6148a7d395961277afd421ee0326396 100644 (file)
@@ -3,6 +3,8 @@ from MemObject import MemObject
 
 class Bridge(MemObject):
     type = 'Bridge'
+    side_a = Port('Side A port')
+    side_b = Port('Side B port')
     queue_size_a = Param.Int(16, "The number of requests to buffer")
     queue_size_b = Param.Int(16, "The number of requests to buffer")
     delay = Param.Latency('0ns', "The latency of this bridge")
index 7798f5f048906d7a30dac0d2af7070b7d75570c4..222f750da6d93e75cce298dc5ce00ffb09f54666 100644 (file)
@@ -4,6 +4,7 @@ from MemObject import MemObject
 class PioDevice(MemObject):
     type = 'PioDevice'
     abstract = True
+    pio = Port("Programmed I/O port")
     platform = Param.Platform(Parent.any, "Platform this device is part of")
     system = Param.System(Parent.any, "System this device is part of")
 
@@ -16,3 +17,4 @@ class BasicPioDevice(PioDevice):
 class DmaDevice(PioDevice):
     type = 'DmaDevice'
     abstract = True
+    dma = Port("DMA port")