From e981a97dec3df921f3800fd9ae5ec01ed4e9d2b1 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Tue, 13 Jun 2006 23:19:28 -0400 Subject: Move SimObject creation and Port connection loops into Python. Add Port and VectorPort objects and support for specifying port connections via assignment. The whole C++ ConfigNode hierarchy is gone now, as are C++ Connector objects. configs/test/fs.py: configs/test/test.py: Rewrite for new port connector syntax. src/SConscript: Remove unneeded files: - mem/connector.* - sim/config* src/dev/io_device.hh: src/mem/bridge.cc: src/mem/bridge.hh: src/mem/bus.cc: src/mem/bus.hh: src/mem/mem_object.hh: src/mem/physical.cc: src/mem/physical.hh: Allow getPort() to take an optional index to support vector ports (eventually). src/python/m5/__init__.py: Move SimObject construction and port connection operations into Python (with C++ calls). src/python/m5/config.py: Move SimObject construction and port connection operations into Python (with C++ calls). Add support for declaring and connecting MemObject ports in Python. src/python/m5/objects/Bus.py: src/python/m5/objects/PhysicalMemory.py: Add port declaration. src/sim/builder.cc: src/sim/builder.hh: src/sim/serialize.cc: src/sim/serialize.hh: ConfigNodes are gone; builder just gets the name of a .ini file section now. src/sim/main.cc: Move SimObject construction and port connection operations into Python (with C++ calls). Split remaining initialization operations into two parts, loadIniFile() and finalInit(). src/sim/param.cc: src/sim/param.hh: SimObject resolution done globally in Python now (not via ConfigNode hierarchy). src/sim/sim_object.cc: Remove unneeded #include. --HG-- extra : convert_revision : 2fa4001eaaec0c9a4231ef6e854f8e156d930dfe --- src/python/m5/config.py | 125 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 97e13c900..f23fd2c6f 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -133,6 +133,10 @@ class Singleton(type): # ##################################################################### + +# dict to look up SimObjects based on path +instanceDict = {} + def isSimObject(value): return isinstance(value, SimObject) @@ -200,7 +204,8 @@ class MetaSimObject(type): 'type' : types.StringType } # Attributes that can be set any time keywords = { 'check' : types.FunctionType, - 'children' : types.ListType } + 'children' : types.ListType, + 'ccObject' : types.ObjectType } # __new__ is called before __init__, and is where the statements # in the body of the class definition get loaded into the class's @@ -233,6 +238,7 @@ class MetaSimObject(type): # initialize required attributes cls._params = multidict() cls._values = multidict() + cls._ports = multidict() cls._instantiated = False # really instantiated or subclassed cls._anon_subclass_counter = 0 @@ -248,6 +254,7 @@ class MetaSimObject(type): if isinstance(base, MetaSimObject): cls._params.parent = base._params cls._values.parent = base._values + cls._ports.parent = base._ports base._instantiated = True # now process the _init_dict items @@ -259,6 +266,10 @@ class MetaSimObject(type): elif isinstance(val, ParamDesc): cls._new_param(key, val) + # port objects + elif isinstance(val, Port): + cls._ports[key] = val + # init-time-only keywords elif cls.init_keywords.has_key(key): cls._set_keyword(key, val, cls.init_keywords[key]) @@ -313,6 +324,10 @@ class MetaSimObject(type): cls._set_keyword(attr, value, cls.keywords[attr]) return + if cls._ports.has_key(attr): + self._ports[attr].connect(self, attr, value) + return + # must be SimObject param param = cls._params.get(attr, None) if param: @@ -428,6 +443,9 @@ class SimObject(object): 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) @@ -443,6 +461,11 @@ class SimObject(object): "use makeClass() to make class first" def __getattr__(self, attr): + if self._ports.has_key(attr): + # return reference that can be assigned to another port + # via __setattr__ + return self._ports[attr].makeRef(self, attr) + if self._values.has_key(attr): return self._values[attr] @@ -457,6 +480,11 @@ class SimObject(object): object.__setattr__(self, attr, value) return + if self._ports.has_key(attr): + # set up port connection + self._ports[attr].connect(self, attr, value) + return + # must be SimObject param param = self._params.get(attr, None) if param: @@ -554,6 +582,8 @@ class SimObject(object): def print_ini(self): print '[' + self.path() + ']' # .ini section header + instanceDict[self.path()] = self + if hasattr(self, 'type') and not isinstance(self, ParamContext): print 'type=%s' % self.type @@ -585,6 +615,24 @@ class SimObject(object): for child in child_names: self._children[child].print_ini() + # 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()) + for child in self._children.itervalues(): + child.createCCObject() + + # Create C++ port connections corresponding to the connections in + # _port_map (& recursively for all children) + def connectPorts(self): + for portRef in self._port_map.itervalues(): + applyOrMap(portRef, 'ccConnect') + for child in self._children.itervalues(): + child.connectPorts() + # generate output file for 'dot' to display as a pretty graph. # this code is currently broken. def outputDot(self, dot): @@ -1419,6 +1467,78 @@ MaxAddr = Addr.max MaxTick = Tick.max AllMemory = AddrRange(0, MaxAddr) + +##################################################################### +# +# Port objects +# +# Ports are used to interconnect objects in the memory system. +# +##################################################################### + +# Port reference: encapsulates a reference to a particular port on a +# particular SimObject. +class PortRef(object): + def __init__(self, simobj, name, isVec): + self.simobj = simobj + self.name = name + self.index = -1 + self.isVec = isVec # is this a vector port? + self.peer = None # not associated with another port yet + self.ccConnected = False # C++ port connection done? + + # Set peer port reference. Called via __setattr__ as a result of + # a port assignment, e.g., "obj1.port1 = obj2.port2". + def setPeer(self, other): + if self.isVec: + curMap = self.simobj._port_map.get(self.name, []) + self.index = len(curMap) + curMap.append(other) + else: + curMap = self.simobj._port_map.get(self.name) + if curMap and not self.isVec: + print "warning: overwriting port", self.simobj, self.name + curMap = other + self.simobj._port_map[self.name] = curMap + self.peer = other + + # 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) + self.ccConnected = True + peer.ccConnected = True + +# Port description object. Like a ParamDesc object, this represents a +# logical port in the SimObject class, not a particular port on a +# SimObject instance. The latter are represented by PortRef objects. +class Port(object): + def __init__(self, desc): + self.desc = desc + self.isVec = False + + # Generate a PortRef for this port on the given SimObject with the + # given name + def makeRef(self, simobj, name): + return PortRef(simobj, name, self.isVec) + + # 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): + myRef = self.makeRef(simobj, name) + myRef.setPeer(ref) + ref.setPeer(myRef) + +# VectorPort description object. Like Port, but represents a vector +# of connections (e.g., as on a Bus). +class VectorPort(Port): + def __init__(self, desc): + Port.__init__(self, desc) + self.isVec = True + ##################################################################### # __all__ defines the list of symbols that get exported when @@ -1436,5 +1556,6 @@ __all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', 'NetworkBandwidth', 'MemoryBandwidth', 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'Null', 'NULL', - 'NextEthernetAddr'] + 'NextEthernetAddr', + 'Port', 'VectorPort'] -- cgit v1.2.3 From 88e22ee081f1b0259b624fe320af22a58f144251 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Thu, 15 Jun 2006 11:45:51 -0400 Subject: Get Port stuff working with full-system scripts. 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 --- src/python/m5/config.py | 421 +++++++++++++++++++++--------------------------- 1 file changed, 180 insertions(+), 241 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index f23fd2c6f..058e72578 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -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) -- cgit v1.2.3 From d96d28e56d39eec0baa1377779119495cfbf4701 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Sat, 17 Jun 2006 12:08:19 -0400 Subject: Rename SWIG "main" module to "cc_main" so it's clear from the Python side that this is the interface to C++. src/SConscript: main_wrap.cc -> cc_main_wrap.cc src/python/SConscript: src/python/m5/__init__.py: src/sim/main.cc: s/main/cc_main/ src/python/m5/config.py: s/main/cc_main/ Also directly import cc_main so we don't need to put the "m5." in front all the time. --HG-- extra : convert_revision : 755552f70cf671881ff31e476c677b95ef12950d --- src/python/m5/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 058e72578..c29477465 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -30,7 +30,7 @@ import os, re, sys, types, inspect, copy import m5 -from m5 import panic +from m5 import panic, cc_main from convert import * from multidict import multidict @@ -529,7 +529,7 @@ class SimObject(object): def getCCObject(self): if not self._ccObject: self._ccObject = -1 # flag to catch cycles in recursion - self._ccObject = m5.main.createSimObject(self.path()) + self._ccObject = cc_main.createSimObject(self.path()) elif self._ccObject == -1: raise RuntimeError, "%s: recursive call to getCCObject()" \ % self.path() @@ -1443,7 +1443,7 @@ class PortRef(object): if self.ccConnected: # already done this return peer = self.peer - m5.main.connectPorts(self.simobj.getCCObject(), self.name, self.index, + cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, peer.simobj.getCCObject(), peer.name, peer.index) self.ccConnected = True peer.ccConnected = True -- cgit v1.2.3 From f64c175f9ae81be3c002a82ea14a2844a7ee100e Mon Sep 17 00:00:00 2001 From: Kevin Lim Date: Thu, 29 Jun 2006 19:40:12 -0400 Subject: Add in support for quiescing the system, taking checkpoints, restoring from checkpoints, changing memory modes, and switching CPUs. Key new functions that can be called on the m5 object at the python interpreter: doQuiesce(root) - A helper function that quiesces the object passed in and all of its children. resume(root) - Another helper function that tells the object and all of its children that the quiesce is over. checkpoint(root) - Takes a checkpoint of the system. Checkpoint directory must be set before hand. setCheckpointDir(name) - Sets the checkpoint directory. restoreCheckpoint(root) - Restores the values from the checkpoint located in the checkpoint directory. changeToAtomic(system) - Changes the system and all of its children to atomic memory mode. changeToTiming(system) - Changes the system and all of its children to timing memory mode. switchCpus(list) - Takes in a list of tuples, where each tuple is a pair of (old CPU, new CPU). Quiesces the old CPUs, and then switches over to the new CPUs. src/SConscript: Remove serializer, replaced by python code. src/python/m5/__init__.py: Updates to support quiescing, checkpointing, changing memory modes, and switching CPUs. src/python/m5/config.py: Several functions defined on the SimObject for quiescing, changing timing modes, and switching CPUs src/sim/main.cc: Add some extra functions that are exported to python through SWIG. src/sim/serialize.cc: Change serialization around a bit. Now it is controlled through Python, so there's no need for SerializeEvents or SerializeParams. Also add in a new unserializeAll() function that loads a checkpoint and handles unserializing all objects. src/sim/serialize.hh: Add unserializeAll function and a setCheckpointName function. src/sim/sim_events.cc: Add process() function for CountedQuiesceEvent, which calls exitSimLoop() once its counter reaches 0. src/sim/sim_events.hh: Add in a CountedQuiesceEvent, which is used when the system is preparing to quiesce. Any objects that can't be quiesced immediately are given a pointer to a CountedQuiesceEvent. The event has its counter set via Python, and as objects finish quiescing they call process() on the event. Eventually the event causes the simulation to stop once all objects have quiesced. src/sim/sim_object.cc: Add a few functions for quiescing, checkpointing, and changing memory modes. src/sim/sim_object.hh: Add a state variable to all SimObjects that tracks both the timing mode of the object and the quiesce state of the object. Currently this isn't serialized, and I'm not sure it needs to be so long as the timing mode starts up the same after a checkpoint. --HG-- extra : convert_revision : a8c738d3911c68d5a7caf7de24d732dcc62cfb61 --- src/python/m5/config.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index c29477465..adabe0743 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -543,6 +543,33 @@ class SimObject(object): for child in self._children.itervalues(): child.connectPorts() + def startQuiesce(self, quiesce_event, recursive): + count = 0 + # ParamContexts don't serialize + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + if self._ccObject.quiesce(quiesce_event): + count = 1 + if recursive: + for child in self._children.itervalues(): + count += child.startQuiesce(quiesce_event, True) + return count + + def resume(self): + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + self._ccObject.resume() + for child in self._children.itervalues(): + child.resume() + + def changeTiming(self, mode): + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + self._ccObject.setMemoryMode(mode) + for child in self._children.itervalues(): + child.changeTiming(mode) + + def takeOverFrom(self, old_cpu): + cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject) + self._ccObject.takeOverFrom(cpu_ptr) + # generate output file for 'dot' to display as a pretty graph. # this code is currently broken. def outputDot(self, dot): -- cgit v1.2.3 From c8b3d8a1edbab505e5f9748cfa1ee866ed1fb02f Mon Sep 17 00:00:00 2001 From: Korey Sewell Date: Sun, 2 Jul 2006 23:11:24 -0400 Subject: Fix default SMT configuration in O3CPU (i.e. fetch policy, workloads/numThreads) Edit Test3 for newmem src/base/traceflags.py: Add O3CPU flag src/cpu/base.cc: for some reason adding a BaseCPU flag doesnt work so just go back to old way... src/cpu/o3/alpha/cpu_builder.cc: Determine number threads by workload size instead of solely by parameter. Default SMT fetch policy to RoundRobin if it's not specified in Config file src/cpu/o3/commit.hh: only use nextNPC for !ALPHA src/cpu/o3/commit_impl.hh: add FetchTrapPending as condition for commit src/cpu/o3/cpu.cc: panic if active threads is more than Impl::MaxThreads src/cpu/o3/fetch.hh: src/cpu/o3/inst_queue.hh: src/cpu/o3/inst_queue_impl.hh: src/cpu/o3/rob.hh: src/cpu/o3/rob_impl.hh: name stuff src/cpu/o3/fetch_impl.hh: fatal if try to use SMT branch count, that's unimplemented right now src/python/m5/config.py: make it clearer that a parameter is not valid within a configuration class --HG-- extra : convert_revision : 55069847304e40e257f9225f0dc3894ce6491b34 --- src/python/m5/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index adabe0743..6f2873d40 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -274,7 +274,7 @@ class MetaSimObject(type): cls._values[attr] = value else: raise AttributeError, \ - "Class %s has no parameter %s" % (cls.__name__, attr) + "Class %s has no parameter \'%s\'" % (cls.__name__, attr) def __getattr__(cls, attr): if cls._values.has_key(attr): -- cgit v1.2.3 From d8fd09cc159a7b5b0d314a41b09cfcdef91de55f Mon Sep 17 00:00:00 2001 From: Kevin Lim Date: Wed, 5 Jul 2006 17:59:33 -0400 Subject: Rename quiesce to drain to avoid confusion with the pseudo instruction. src/cpu/simple/timing.cc: src/cpu/simple/timing.hh: src/python/m5/__init__.py: src/python/m5/config.py: src/sim/main.cc: src/sim/sim_events.cc: src/sim/sim_events.hh: src/sim/sim_object.cc: src/sim/sim_object.hh: Rename quiesce to drain. --HG-- extra : convert_revision : fc3244a3934812e1edb8050f1f51f30382baf774 --- src/python/m5/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 6f2873d40..cffe06984 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -543,15 +543,15 @@ class SimObject(object): for child in self._children.itervalues(): child.connectPorts() - def startQuiesce(self, quiesce_event, recursive): + def startDrain(self, drain_event, recursive): count = 0 # ParamContexts don't serialize if isinstance(self, SimObject) and not isinstance(self, ParamContext): - if self._ccObject.quiesce(quiesce_event): + if self._ccObject.drain(drain_event): count = 1 if recursive: for child in self._children.itervalues(): - count += child.startQuiesce(quiesce_event, True) + count += child.startDrain(drain_event, True) return count def resume(self): -- cgit v1.2.3 From 8c547d80b1a091f41f5516f58ad7368181fe4041 Mon Sep 17 00:00:00 2001 From: Kevin Lim Date: Thu, 6 Jul 2006 13:57:21 -0400 Subject: Change the return value of drain. False means the object wasn't able to drain yet. src/python/m5/config.py: Invert the return value. src/sim/sim_object.cc: Invert the return value of drain. src/sim/sim_object.hh: Change the return value of drain. --HG-- extra : convert_revision : 41bb122c6f29302d8b3815d7bd6a2ea8fba64df9 --- src/python/m5/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index cffe06984..8291e1e1b 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -547,7 +547,7 @@ class SimObject(object): count = 0 # ParamContexts don't serialize if isinstance(self, SimObject) and not isinstance(self, ParamContext): - if self._ccObject.drain(drain_event): + if not self._ccObject.drain(drain_event): count = 1 if recursive: for child in self._children.itervalues(): -- cgit v1.2.3 From 2bc9229ea7195b307222bad6de966ea4a27a3f6b Mon Sep 17 00:00:00 2001 From: Ali Saidi Date: Wed, 12 Jul 2006 20:22:07 -0400 Subject: memory mode information now contained in system object States are now running, draining, or drained. memory state information moved into system object system parameter is not fs only for cpus Implement drain() support in devices Update for drain() call that returns number of times drain_event->process() will be called Break O3 CPU! No sense in putting in a hack change that kevin is going to remove in a few minutes i imagine src/cpu/simple/atomic.cc: src/cpu/simple/atomic.hh: Since se mode has a system, allow access to it Verify that the atomic cpu is connected to an atomic system on resume src/cpu/simple/base.cc: Since se mode has a system, allow access to it src/cpu/simple/timing.cc: src/cpu/simple/timing.hh: Update for new drain() call that returns number of times drain_event->process() will be called and memory state being moved into the system Since se mode has a system, allow access to it Verify that the timing cpu is connected to an timing system on resume src/dev/ide_disk.cc: src/dev/io_device.cc: src/dev/io_device.hh: src/dev/ns_gige.cc: src/dev/ns_gige.hh: src/dev/pcidev.cc: src/dev/pcidev.hh: src/dev/sinic.cc: src/dev/sinic.hh: Implement drain() support in devices src/python/m5/config.py: Allow drain to return number of times drain_event->process() will be called. Normally 0 or 1 but things like O3 cpu or devices with multiple ports may want to call it many times src/python/m5/objects/BaseCPU.py: move system parameter out of fs to everyone src/sim/sim_object.cc: src/sim/sim_object.hh: States are now running, draining, or drained. memory state information moved into system object src/sim/system.cc: src/sim/system.hh: memory mode information now contained in system object --HG-- extra : convert_revision : 1389c77e66ee6d9710bf77b4306fb47e107b21cf --- src/python/m5/config.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 8291e1e1b..8eed28dcc 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -547,8 +547,7 @@ class SimObject(object): count = 0 # ParamContexts don't serialize if isinstance(self, SimObject) and not isinstance(self, ParamContext): - if not self._ccObject.drain(drain_event): - count = 1 + count += self._ccObject.drain(drain_event) if recursive: for child in self._children.itervalues(): count += child.startDrain(drain_event, True) @@ -561,7 +560,7 @@ class SimObject(object): child.resume() def changeTiming(self, mode): - if isinstance(self, SimObject) and not isinstance(self, ParamContext): + if isinstance(self, System): self._ccObject.setMemoryMode(mode) for child in self._children.itervalues(): child.changeTiming(mode) -- cgit v1.2.3