From 0337db3388db335ea23f02f3aa00bca9d483ef1c Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Tue, 30 May 2006 13:11:34 -0400 Subject: Link in Python interpreter. Use embedded zip archive to carry Python code instead of homegrown embedded string/file mechanism. Do argument parsing in Python instead of C++. SConstruct: Add Python interpreter include path & library. Define two new simple builders which copy & concatenate files, respectively, for use by the Python embedded zipfile code. src/SConscript: Encapsulate environment creation in a function. Add code to append Python zip archive to final executable. Eliminate references to obsolete files. src/python/SConscript: Rewrite to generate embedded zip archive of Python code (replacing old "embedded string" mechanism). src/python/m5/__init__.py: Move main arg-parsing loop here (out of C++ main()). src/python/m5/config.py: Minor fix (version incompatibility?). src/sim/main.cc: Invoke embedded Python interpreter to parse args and generate config.ini, replacing C++ arg parsing code. --HG-- extra : convert_revision : 72d21236b2bee139ff39ba4cf031a4a1f8560029 --- 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 1e25e0d09..ce7e5a964 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -794,7 +794,7 @@ class ParamFactory(object): # E.g., Param.Int(5, "number of widgets") def __call__(self, *args, **kwargs): - caller_frame = inspect.stack()[1][0] + caller_frame = inspect.currentframe().f_back ptype = None try: ptype = eval(self.ptype_str, -- cgit v1.2.3 From cb0cf2dd8ab1cd60ef13de925ac862268c07297f Mon Sep 17 00:00:00 2001 From: Ali Saidi Date: Wed, 31 May 2006 19:26:56 -0400 Subject: Updated Authors from bk prs info --HG-- extra : convert_revision : 77f475b156d81c03a2811818fa23593d5615c685 --- src/python/m5/config.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index ce7e5a964..d1471c807 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -23,6 +23,9 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt +# Nathan Binkert from __future__ import generators import os, re, sys, types, inspect -- cgit v1.2.3 From 29e34a739b991af8d8e1eafe75ecb0904c324dc8 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Fri, 9 Jun 2006 23:01:31 -0400 Subject: Move main control from C++ into Python. User script now invokes initialization and simulation loop after building configuration. These functions are exported from C++ to Python using SWIG. SConstruct: Set up SWIG builder & scanner. Set up symlinking of source files into build directory (by not disabling the default behavior). configs/test/test.py: Rewrite to use new script-driven interface. Include a sample option. src/SConscript: Set up symlinking of source files into build directory (by not disabling the default behavior). Add SWIG-generated main_wrap.cc to source list. src/arch/SConscript: Set up symlinking of source files into build directory (by not disabling the default behavior). src/arch/alpha/ev5.cc: src/arch/alpha/isa/decoder.isa: src/cpu/o3/alpha_cpu_impl.hh: src/cpu/trace/opt_cpu.cc: src/cpu/trace/trace_cpu.cc: src/sim/pseudo_inst.cc: src/sim/root.cc: src/sim/serialize.cc: src/sim/syscall_emul.cc: SimExit() is now exitSimLoop(). src/cpu/base.cc: SimExitEvent is now SimLoopExitEvent src/python/SConscript: Add SWIG build command for main.i. Use python/m5 in build dir as source for zip archive... easy now with file duplication enabled. src/python/m5/__init__.py: - Move copyright notice back to C++ so we can print it right away, even for interactive sessions. - Get rid of argument parsing code; just provide default option descriptors for user script to call optparse with. - Don't clutter m5 namespace by sucking in all of m5.config and m5.objects. - Move instantiate() function here from config.py. src/python/m5/config.py: - Move instantiate() function to __init__.py. - Param.Foo deferred type lookups must use m5.objects namespace now (not m5). src/python/m5/objects/AlphaConsole.py: src/python/m5/objects/AlphaFullCPU.py: src/python/m5/objects/AlphaTLB.py: src/python/m5/objects/BadDevice.py: src/python/m5/objects/BaseCPU.py: src/python/m5/objects/BaseCache.py: src/python/m5/objects/Bridge.py: src/python/m5/objects/Bus.py: src/python/m5/objects/CoherenceProtocol.py: src/python/m5/objects/Device.py: src/python/m5/objects/DiskImage.py: src/python/m5/objects/Ethernet.py: src/python/m5/objects/Ide.py: src/python/m5/objects/IntrControl.py: src/python/m5/objects/MemObject.py: src/python/m5/objects/MemTest.py: src/python/m5/objects/Pci.py: src/python/m5/objects/PhysicalMemory.py: src/python/m5/objects/Platform.py: src/python/m5/objects/Process.py: src/python/m5/objects/Repl.py: src/python/m5/objects/Root.py: src/python/m5/objects/SimConsole.py: src/python/m5/objects/SimpleDisk.py: src/python/m5/objects/System.py: src/python/m5/objects/Tsunami.py: src/python/m5/objects/Uart.py: Fix up imports (m5 namespace no longer includes m5.config). src/sim/eventq.cc: src/sim/eventq.hh: Support for Python-called simulate() function: - Use IsExitEvent flag to signal events that want to exit the simulation loop gracefully (instead of calling exit() to terminate the process). - Modify interface to hand exit event object back to caller so it can be inspected for cause. src/sim/host.hh: Add MaxTick constant. src/sim/main.cc: Move copyright notice back to C++ so we can print it right away, even for interactive sessions. Use PYTHONPATH environment var to set module path (instead of clunky code injection method). Move main control from here into Python: - Separate initialization code and simulation loop into separate functions callable from Python. - Make Python interpreter invocation more pure (more like directly invoking interpreter). Add -i and -p flags (only options on binary itself; other options processed by Python). Import readline package when using interactive mode. src/sim/sim_events.cc: SimExitEvent is now SimLoopExitEvent, and uses IsSimExit flag to terminate loop (instead of exiting simulator process). src/sim/sim_events.hh: SimExitEvent is now SimLoopExitEvent, and uses IsSimExit flag to terminate loop (instead of exiting simulator process). Get rid of a few unused constructors. src/sim/sim_exit.hh: SimExit() is now exitSimLoop(). Get rid of unused functions. Add comments. --HG-- extra : convert_revision : 280b0d671516b25545a6f24cefa64a68319ff3d4 --- src/python/m5/config.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index d1471c807..f6a2a84fb 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -728,7 +728,7 @@ class ParamDesc(object): def __getattr__(self, attr): if attr == 'ptype': try: - ptype = eval(self.ptype_str, m5.__dict__) + ptype = eval(self.ptype_str, m5.objects.__dict__) if not isinstance(ptype, type): panic("Param qualifier is not a type: %s" % self.ptype) self.ptype = ptype @@ -1290,23 +1290,6 @@ AllMemory = AddrRange(0, MaxAddr) ##################################################################### -# The final hook to generate .ini files. Called from configuration -# script once config is built. -def instantiate(root): - global ticks_per_sec - ticks_per_sec = float(root.clock.frequency) - root.print_ini() - noDot = True # temporary until we fix dot - if not noDot: - dot = pydot.Dot() - instance.outputDot(dot) - dot.orientation = "portrait" - dot.size = "8.5,11" - dot.ranksep="equally" - dot.rank="samerank" - dot.write("config.dot") - dot.write_ps("config.ps") - # __all__ defines the list of symbols that get exported when # 'from config import *' is invoked. Try to keep this reasonably # short to avoid polluting other namespaces. @@ -1322,5 +1305,5 @@ __all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', 'NetworkBandwidth', 'MemoryBandwidth', 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'Null', 'NULL', - 'NextEthernetAddr', 'instantiate'] + 'NextEthernetAddr'] -- cgit v1.2.3 From 39f85a1de44789bf0afd1d492167d4fe4601bd82 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Sat, 10 Jun 2006 19:58:36 -0400 Subject: Only allow SimObject classes to be instantiated (no cloning!). Provide a makeClass() method to generate a new class using a SimObject instance as a template. All instantiation, subclassing, and class generation is done recursively using "deep copy"-style memoization to maintain object relationships in the face of multiple references to shared objects/classes. src/python/m5/multidict.py: Rename local dict attribute from 'dict' to 'local' for clarity. --HG-- extra : convert_revision : 73ed6836216308709d7bb68d09f8131acd5f1822 --- src/python/m5/config.py | 196 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 151 insertions(+), 45 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index f6a2a84fb..9de768d18 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -27,11 +27,10 @@ # Authors: Steve Reinhardt # Nathan Binkert -from __future__ import generators import os, re, sys, types, inspect import m5 -panic = m5.panic +from m5 import panic from convert import * from multidict import multidict @@ -137,6 +136,13 @@ class Singleton(type): 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 isSimObjSequence(value): if not isinstance(value, (list, tuple)): return False @@ -147,6 +153,16 @@ def isSimObjSequence(value): return True +def isSimObjClassSequence(value): + if not isinstance(value, (list, tuple)): + return False + + for val in value: + if not isNullPointer(val) and not isSimObjectClass(val): + return False + + return True + def isNullPointer(value): return isinstance(value, NullSimObject) @@ -170,19 +186,26 @@ class MetaSimObject(type): # and only allow "private" attributes to be passed to the base # __new__ (starting with underscore). def __new__(mcls, name, bases, dict): - # 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 + 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 return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) - # initialization + # subclass initialization def __init__(cls, name, bases, dict): + # calls type.__init__()... I think that's a no-op, but leave + # it here just in case it's not. super(MetaSimObject, cls).__init__(name, bases, dict) # initialize required attributes @@ -197,27 +220,13 @@ class MetaSimObject(type): base = bases[0] + # the only time the following is not true is when we define + # the SimObject class itself if isinstance(base, MetaSimObject): cls._params.parent = base._params cls._values.parent = base._values - # If your parent has a value in it that's a config node, clone - # it. Do this now so if we update any of the values' - # attributes we are updating the clone and not the original. - for key,val in base._values.iteritems(): - - # don't clone if (1) we're about to overwrite it with - # a local setting or (2) we've already cloned a copy - # from an earlier (more derived) base - if cls._init_dict.has_key(key) or cls._values.has_key(key): - continue - - if isSimObject(val): - cls._values[key] = val() - elif isSimObjSequence(val) and len(val): - cls._values[key] = [ v() for v in val ] - - # now process remaining _init_dict items + # 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) @@ -234,6 +243,33 @@ 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 isSimObject(val): + assert(val == cls._values.local[key]) + cls._values[key] = val.makeClass(memo) + elif isSimObjSequence(val) and len(val): + assert(val == cls._values.local[key]) + cls._values[key] = [ v.makeClass(memo) for v in val ] + # SimObject classes need to be subclassed so that + # parameters that get set at this level only affect this + # level and derivatives. + elif isSimObjectClass(val): + assert(not cls._values.local.has_key(key)) + cls._values[key] = val.makeSubclass({}, memo) + elif isSimObjClassSequence(val) and len(val): + assert(not cls._values.local.has_key(key)) + cls._values[key] = [ v.makeSubclass({}, memo) for v in val ] + + def _set_keyword(cls, keyword, val, kwtype): if not isinstance(val, kwtype): raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ @@ -284,6 +320,22 @@ 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 code in this class deals with the configuration hierarchy itself # (parent/child node relationships). @@ -292,27 +344,78 @@ class SimObject(object): # get this metaclass. __metaclass__ = MetaSimObject - def __init__(self, _value_parent = None, **kwargs): + # __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._children = {} - if _value_parent and type(_value_parent) != type(self): - # this was called as a type conversion rather than a clone - raise TypeError, "Cannot convert %s to %s" % \ - (_value_parent.__class__.__name__, self.__class__.__name__) - if not _value_parent: - _value_parent = self.__class__ - # clone values - self._values = multidict(_value_parent._values) - for key,val in _value_parent._values.iteritems(): - if isSimObject(val): - setattr(self, key, val()) - elif isSimObjSequence(val) and len(val): - setattr(self, key, [ v() for v in val ]) + # 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 isSimObjClassSequence(val) and len(val): + setattr(self, key, [ v(_memo) for v in val ]) # apply attribute assignments from keyword args, if any for key,val in kwargs.iteritems(): setattr(self, key, val) + # 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. def __call__(self, **kwargs): - return self.__class__(_value_parent = self, **kwargs) + raise TypeError, "cannot instantiate SimObject; "\ + "use makeClass() to make class first" def __getattr__(self, attr): if self._values.has_key(attr): @@ -1069,7 +1172,10 @@ class EthernetAddr(ParamValue): def __str__(self): if self.value == NextEthernetAddr: - return self.addr + if hasattr(self, 'addr'): + return self.addr + else: + return "NextEthernetAddr (unresolved)" else: return self.value -- cgit v1.2.3 From bb58e4b85163f263ebf5ea54fee5dba0109eb88c Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Sat, 10 Jun 2006 21:13:36 -0400 Subject: Don't allow SimObject-valued class params to be set after the class has been instantiated or subclassed. This is one of the main situations that leads to confusing results. configs/test/fs.py: Clean up to avoid modifying BaseCPU after it's been subclassed. --HG-- extra : convert_revision : 335cb87bc3b211ecc8969cfb99ffc28f62f1f877 --- src/python/m5/config.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 9de768d18..3eb99972f 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -211,6 +211,7 @@ class MetaSimObject(type): # initialize required attributes cls._params = multidict() cls._values = multidict() + cls._instantiated = False # really instantiated or subclassed cls._anon_subclass_counter = 0 # We don't support multiple inheritance. If you want to, you @@ -225,6 +226,7 @@ class MetaSimObject(type): if isinstance(base, MetaSimObject): cls._params.parent = base._params cls._values.parent = base._values + base._instantiated = True # now process the _init_dict items for key,val in cls._init_dict.items(): @@ -299,6 +301,12 @@ class MetaSimObject(type): param = cls._params.get(attr, None) if param: # It's ok: set attribute by delegating to 'object' class. + if (isSimObject(value) or isSimObjSequence(value)) \ + and cls._instantiated: + raise AttributeError, \ + "Cannot set SimObject parameter '%s' after\n" \ + " class %s has been instantiated or subclassed" \ + % (attr, cls.__name__) try: cls._values[attr] = param.convert(value) except Exception, e: @@ -386,6 +394,8 @@ class SimObject(object): # instantiated objects. _memo = {} + self.__class__._instantiated = True + self._children = {} # Inherit parameter values from class using multidict so # individual value settings can be overridden. -- cgit v1.2.3 From d3a57835234ab2ca3830bad380e434ec2c8d2418 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Sun, 11 Jun 2006 20:37:56 -0400 Subject: Add some utility functions to ease handling SimObjects and sequences of SimObjects in a uniform manner. --HG-- extra : convert_revision : 5db28b778c6b1ca63ad0a112ad7e92cd610f64d3 --- src/python/m5/config.py | 57 +++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 21 deletions(-) (limited to 'src/python/m5/config.py') diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 3eb99972f..97e13c900 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -143,8 +143,8 @@ def isSimObjectClass(value): # happens if value is not a class at all return False -def isSimObjSequence(value): - if not isinstance(value, (list, tuple)): +def isSimObjectSequence(value): + if not isinstance(value, (list, tuple)) or len(value) == 0: return False for val in value: @@ -153,8 +153,8 @@ def isSimObjSequence(value): return True -def isSimObjClassSequence(value): - if not isinstance(value, (list, tuple)): +def isSimObjectClassSequence(value): + if not isinstance(value, (list, tuple)) or len(value) == 0: return False for val in value: @@ -163,9 +163,31 @@ def isSimObjClassSequence(value): 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) +# Apply method to object. +# applyMethod(obj, 'meth', ) is equivalent to obj.meth() +def applyMethod(obj, meth, *args, **kwargs): + return getattr(obj, meth)(*args, **kwargs) + +# If the first argument is an (non-sequence) object, apply the named +# method with the given arguments. If the first argument is a +# sequence, apply the method to each element of the sequence (a la +# 'map'). +def applyOrMap(objOrSeq, meth, *args, **kwargs): + if not isinstance(objOrSeq, (list, tuple)): + return applyMethod(objOrSeq, meth, *args, **kwargs) + else: + 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 @@ -255,21 +277,15 @@ class MetaSimObject(type): # 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 isSimObject(val): - assert(val == cls._values.local[key]) - cls._values[key] = val.makeClass(memo) - elif isSimObjSequence(val) and len(val): + if isSimObjectOrSequence(val): assert(val == cls._values.local[key]) - cls._values[key] = [ v.makeClass(memo) for v in val ] + 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 isSimObjectClass(val): - assert(not cls._values.local.has_key(key)) - cls._values[key] = val.makeSubclass({}, memo) - elif isSimObjClassSequence(val) and len(val): + elif isSimObjectClassOrSequence(val): assert(not cls._values.local.has_key(key)) - cls._values[key] = [ v.makeSubclass({}, memo) for v in val ] + cls._values[key] = applyOrMap(val, 'makeSubclass', {}, memo) def _set_keyword(cls, keyword, val, kwtype): @@ -301,8 +317,7 @@ class MetaSimObject(type): param = cls._params.get(attr, None) if param: # It's ok: set attribute by delegating to 'object' class. - if (isSimObject(value) or isSimObjSequence(value)) \ - and cls._instantiated: + if isSimObjectOrSequence(value) and cls._instantiated: raise AttributeError, \ "Cannot set SimObject parameter '%s' after\n" \ " class %s has been instantiated or subclassed" \ @@ -315,7 +330,7 @@ class MetaSimObject(type): e.args = (msg, ) raise # I would love to get rid of this - elif isSimObject(value) or isSimObjSequence(value): + elif isSimObjectOrSequence(value): cls._values[attr] = value else: raise AttributeError, \ @@ -407,7 +422,7 @@ class SimObject(object): for key,val in self.__class__._values.iteritems(): if isSimObjectClass(val): setattr(self, key, val(_memo)) - elif isSimObjClassSequence(val) and len(val): + elif isSimObjectClassSequence(val) and len(val): setattr(self, key, [ v(_memo) for v in val ]) # apply attribute assignments from keyword args, if any for key,val in kwargs.iteritems(): @@ -454,7 +469,7 @@ class SimObject(object): e.args = (msg, ) raise # I would love to get rid of this - elif isSimObject(value) or isSimObjSequence(value): + elif isSimObjectOrSequence(value): pass else: raise AttributeError, "Class %s has no parameter %s" \ @@ -465,7 +480,7 @@ class SimObject(object): if isSimObject(value): value.set_path(self, attr) - elif isSimObjSequence(value): + elif isSimObjectSequence(value): value = SimObjVector(value) [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)] @@ -888,7 +903,7 @@ class VectorParamDesc(ParamDesc): if isinstance(value, (list, tuple)): # list: coerce each element into new list tmp_list = [ ParamDesc.convert(self, v) for v in value ] - if isSimObjSequence(tmp_list): + if isSimObjectSequence(tmp_list): return SimObjVector(tmp_list) else: return VectorParamValue(tmp_list) -- cgit v1.2.3 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