From abc76f20cb98c90e8dab416dd16dfd4a954013ba Mon Sep 17 00:00:00 2001 From: Nathan Binkert Date: Mon, 23 Jul 2007 21:51:38 -0700 Subject: Major changes to how SimObjects are created and initialized. Almost all creation and initialization now happens in python. Parameter objects are generated and initialized by python. The .ini file is now solely for debugging purposes and is not used in construction of the objects in any way. --HG-- extra : convert_revision : 7e722873e417cb3d696f2e34c35ff488b7bff4ed --- src/python/m5/SimObject.py | 205 +++++++++++++++++++++++++++++--------- src/python/m5/__init__.py | 200 ++++--------------------------------- src/python/m5/environment.py | 43 ++++++++ src/python/m5/params.py | 229 ++++++++++++++++++++++++++++++++++--------- src/python/m5/simulate.py | 199 +++++++++++++++++++++++++++++++++++++ src/python/m5/ticks.py | 6 +- 6 files changed, 608 insertions(+), 274 deletions(-) create mode 100644 src/python/m5/environment.py create mode 100644 src/python/m5/simulate.py (limited to 'src/python/m5') diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py index f87e13732..1e7d289e2 100644 --- a/src/python/m5/SimObject.py +++ b/src/python/m5/SimObject.py @@ -29,6 +29,8 @@ import sys, types +import proxy +import m5 from util import * from multidict import multidict @@ -61,7 +63,7 @@ def isSimObjectOrSequence(value): from params import * # There are a few things we need that aren't in params.__all__ since # normal users don't need them -from params import ParamDesc, isNullPointer, SimObjVector +from params import ParamDesc, VectorParamDesc, isNullPointer, SimObjVector noDot = False try: @@ -109,6 +111,9 @@ except: # ##################################################################### +# list of all SimObject classes +allClasses = {} + # dict to look up SimObjects based on path instanceDict = {} @@ -119,12 +124,14 @@ instanceDict = {} class MetaSimObject(type): # Attributes that can be set only at initialization time init_keywords = { 'abstract' : types.BooleanType, + 'cxx_namespace' : types.StringType, + 'cxx_class' : types.StringType, + 'cxx_type' : types.StringType, + 'cxx_predecls' : types.ListType, + 'swig_predecls' : types.ListType, 'type' : types.StringType } # Attributes that can be set any time - keywords = { 'check' : types.FunctionType, - 'cxx_type' : types.StringType, - 'cxx_predecls' : types.ListType, - 'swig_predecls' : types.ListType } + 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 @@ -132,6 +139,8 @@ class MetaSimObject(type): # and only allow "private" attributes to be passed to the base # __new__ (starting with underscore). def __new__(mcls, name, bases, dict): + assert name not in allClasses + # Copy "private" attributes, functions, and classes to the # official dict. Everything else goes in _init_dict to be # filtered in __init__. @@ -144,8 +153,13 @@ class MetaSimObject(type): else: # must be a param/port setting value_dict[key] = val + if 'abstract' not in value_dict: + value_dict['abstract'] = False cls_dict['_value_dict'] = value_dict - return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) + cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) + if 'type' in value_dict: + allClasses[name] = cls + return cls # subclass initialization def __init__(cls, name, bases, dict): @@ -183,6 +197,34 @@ class MetaSimObject(type): # mark base as having been subclassed base._instantiated = True + # default keyword values + if 'type' in cls._value_dict: + _type = cls._value_dict['type'] + if 'cxx_class' not in cls._value_dict: + cls._value_dict['cxx_class'] = _type + + namespace = cls._value_dict.get('cxx_namespace', None) + + _cxx_class = cls._value_dict['cxx_class'] + if 'cxx_type' not in cls._value_dict: + t = _cxx_class + '*' + if namespace: + t = '%s::%s' % (namespace, t) + cls._value_dict['cxx_type'] = t + if 'cxx_predecls' not in cls._value_dict: + # A forward class declaration is sufficient since we are + # just declaring a pointer. + decl = 'class %s;' % _cxx_class + if namespace: + decl = 'namespace %s { %s }' % (namespace, decl) + cls._value_dict['cxx_predecls'] = [decl] + + if 'swig_predecls' not in cls._value_dict: + # A forward class declaration is sufficient since we are + # just declaring a pointer. + cls._value_dict['swig_predecls'] = \ + cls._value_dict['cxx_predecls'] + # 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 @@ -207,12 +249,6 @@ class MetaSimObject(type): else: setattr(cls, key, val) - cls.cxx_type = cls.type + '*' - # A forward class declaration is sufficient since we are just - # declaring a pointer. - cls.cxx_predecls = ['class %s;' % cls.type] - cls.swig_predecls = cls.cxx_predecls - def _set_keyword(cls, keyword, val, kwtype): if not isinstance(val, kwtype): raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ @@ -310,18 +346,19 @@ class MetaSimObject(type): return cls.__name__ def cxx_decl(cls): - code = "#ifndef __PARAMS__%s\n#define __PARAMS__%s\n\n" % (cls, cls) - if str(cls) != 'SimObject': base = cls.__bases__[0].type else: base = None + code = "#ifndef __PARAMS__%s\n" % cls + code += "#define __PARAMS__%s\n\n" % cls + # The 'dict' attribute restricts us to the params declared in # the object itself, not including inherited params (which # will also be inherited from the base class's param struct # here). - params = cls._params.dict.values() + params = cls._params.local.values() try: ptypes = [p.ptype for p in params] except: @@ -330,9 +367,10 @@ class MetaSimObject(type): raise # get a list of lists of predeclaration lines - predecls = [p.cxx_predecls() for p in params] - # flatten - predecls = reduce(lambda x,y:x+y, predecls, []) + predecls = [] + predecls.extend(cls.cxx_predecls) + for p in params: + predecls.extend(p.cxx_predecls()) # remove redundant lines predecls2 = [] for pd in predecls: @@ -345,17 +383,21 @@ class MetaSimObject(type): if base: code += '#include "params/%s.hh"\n\n' % base - # Generate declarations for locally defined enumerations. - enum_ptypes = [t for t in ptypes if issubclass(t, Enum)] - if enum_ptypes: - code += "\n".join([t.cxx_decl() for t in enum_ptypes]) - code += "\n\n" + for ptype in ptypes: + if issubclass(ptype, Enum): + code += '#include "enums/%s.hh"\n' % ptype.__name__ + code += "\n\n" # now generate the actual param struct code += "struct %sParams" % cls if base: code += " : public %sParams" % base - code += " {\n" + code += "\n{\n" + if cls == SimObject: + code += " virtual ~%sParams() {}\n" % cls + if not hasattr(cls, 'abstract') or not cls.abstract: + if 'type' in cls.__dict__: + code += " %s create();\n" % cls.cxx_type decls = [p.cxx_decl() for p in params] decls.sort() code += "".join([" %s\n" % d for d in decls]) @@ -365,12 +407,34 @@ class MetaSimObject(type): code += "\n#endif\n" return code + def cxx_type_decl(cls): + if str(cls) != 'SimObject': + base = cls.__bases__[0] + else: + base = None + + code = '' + + if base: + code += '#include "%s_type.h"\n' % base + + # now generate dummy code for inheritance + code += "struct %s" % cls.cxx_class + if base: + code += " : public %s" % base.cxx_class + code += "\n{};\n" + + return code + def swig_decl(cls): + code = '%%module %s\n' % cls - code = '%%module %sParams\n' % cls + code += '%{\n' + code += '#include "params/%s.hh"\n' % cls + code += '%}\n\n' if str(cls) != 'SimObject': - base = cls.__bases__[0].type + base = cls.__bases__[0] else: base = None @@ -378,11 +442,12 @@ class MetaSimObject(type): # the object itself, not including inherited params (which # will also be inherited from the base class's param struct # here). - params = cls._params.dict.values() + params = cls._params.local.values() ptypes = [p.ptype for p in params] # get a list of lists of predeclaration lines - predecls = [p.swig_predecls() for p in params] + predecls = [] + predecls.extend([ p.swig_predecls() for p in params ]) # flatten predecls = reduce(lambda x,y:x+y, predecls, []) # remove redundant lines @@ -395,11 +460,14 @@ class MetaSimObject(type): code += "\n\n"; if base: - code += '%%import "python/m5/swig/%sParams.i"\n\n' % base + code += '%%import "params/%s.i"\n\n' % base - code += '%{\n' - code += '#include "params/%s.hh"\n' % cls - code += '%}\n\n' + for ptype in ptypes: + if issubclass(ptype, Enum): + code += '%%import "enums/%s.hh"\n' % ptype.__name__ + code += "\n\n" + + code += '%%import "params/%s_type.hh"\n\n' % cls code += '%%include "params/%s.hh"\n\n' % cls return code @@ -412,6 +480,7 @@ class SimObject(object): # get this metaclass. __metaclass__ = MetaSimObject type = 'SimObject' + abstract = True name = Param.String("Object name") @@ -440,6 +509,7 @@ class SimObject(object): self._parent = None self._children = {} self._ccObject = None # pointer to C++ object + self._ccParams = None self._instantiated = False # really "cloned" # Inherit parameter values from class using multidict so @@ -577,8 +647,11 @@ class SimObject(object): value._maybe_set_parent(self, attr) elif isSimObjectSequence(value): value = SimObjVector(value) - [v._maybe_set_parent(self, "%s%d" % (attr, i)) - for i,v in enumerate(value)] + if len(value) == 1: + value[0]._maybe_set_parent(self, attr) + else: + for i,v in enumerate(value): + v._maybe_set_parent(self, "%s%d" % (attr, i)) self._values[attr] = value @@ -680,25 +753,66 @@ 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): - self.getCCObject() # force creation - for child in self._children.itervalues(): - child.createCCObject() + def getCCParams(self): + if self._ccParams: + return self._ccParams + + cc_params_struct = eval('m5.objects.params.%sParams' % self.type) + cc_params = cc_params_struct() + cc_params.object = self + cc_params.name = str(self) + + param_names = self._params.keys() + param_names.sort() + for param in param_names: + value = self._values.get(param) + if value is None: + continue + + value = value.getValue() + if isinstance(self._params[param], VectorParamDesc): + assert isinstance(value, list) + vec = getattr(cc_params, param) + assert not len(vec) + for v in value: + vec.append(v) + else: + setattr(cc_params, param, value) + + port_names = self._ports.keys() + port_names.sort() + for port_name in port_names: + port = self._port_refs.get(port_name, None) + if port != None: + setattr(cc_params, port_name, port) + self._ccParams = cc_params + return self._ccParams # Get C++ object corresponding to this object, calling C++ if # necessary to construct it. Does *not* recursively create # children. def getCCObject(self): + import internal + params = self.getCCParams() if not self._ccObject: self._ccObject = -1 # flag to catch cycles in recursion - self._ccObject = internal.sim_object.createSimObject(self.path()) + self._ccObject = params.create() elif self._ccObject == -1: raise RuntimeError, "%s: recursive call to getCCObject()" \ % self.path() return self._ccObject + # Call C++ to create C++ object corresponding to this object and + # (recursively) all its children + def createCCObject(self): + self.getCCParams() + self.getCCObject() # force creation + for child in self._children.itervalues(): + child.createCCObject() + + def getValue(self): + return self.getCCObject() + # Create C++ port connections corresponding to the connections in # _port_refs (& recursively for all children) def connectPorts(self): @@ -730,6 +844,7 @@ class SimObject(object): return system_ptr.getMemoryMode() def changeTiming(self, mode): + import internal if isinstance(self, m5.objects.System): # i don't know if there's a better way to do this - calling # setMemoryMode directly from self._ccObject results in calling @@ -740,6 +855,7 @@ class SimObject(object): child.changeTiming(mode) def takeOverFrom(self, old_cpu): + import internal cpu_ptr = internal.sim_object.convertToBaseCPUPtr(old_cpu._ccObject) self._ccObject.takeOverFrom(cpu_ptr) @@ -794,9 +910,4 @@ def resolveSimObject(name): # __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. -__all__ = ['SimObject'] - -# see comment on imports at end of __init__.py. -import proxy -import internal -import m5 +__all__ = [ 'SimObject' ] diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py index a9206a474..36f2eba61 100644 --- a/src/python/m5/__init__.py +++ b/src/python/m5/__init__.py @@ -27,19 +27,10 @@ # Authors: Nathan Binkert # Steve Reinhardt -import atexit import os import sys -# import the SWIG-wrapped main C++ functions -import internal -# import a few SWIG-wrapped items (those that are likely to be used -# directly by user scripts) completely into this module for -# convenience -import event - -# import the m5 compile options -import defines +import smartdict # define a MaxTick parameter MaxTick = 2**63 - 1 @@ -69,178 +60,12 @@ def AddToPath(path): sys.path.insert(1, path) # make a SmartDict out of the build options for our local use -import smartdict build_env = smartdict.SmartDict() -build_env.update(defines.m5_build_env) # make a SmartDict out of the OS environment too env = smartdict.SmartDict() env.update(os.environ) -# The final hook to generate .ini files. Called from the user script -# once the config is built. -def instantiate(root): - # we need to fix the global frequency - ticks.fixGlobalFrequency() - - root.unproxy_all() - # ugly temporary hack to get output to config.ini - sys.stdout = file(os.path.join(options.outdir, 'config.ini'), 'w') - root.print_ini() - sys.stdout.close() # close config.ini - sys.stdout = sys.__stdout__ # restore to original - - # load config.ini into C++ - internal.core.loadIniFile(resolveSimObject) - - # Initialize the global statistics - internal.stats.initSimStats() - - # Create the C++ sim objects and connect ports - root.createCCObject() - root.connectPorts() - - # Do a second pass to finish initializing the sim objects - internal.sim_object.initAll() - - # Do a third pass to initialize statistics - internal.sim_object.regAllStats() - - # Check to make sure that the stats package is properly initialized - internal.stats.check() - - # Reset to put the stats in a consistent state. - internal.stats.reset() - -def doDot(root): - 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") - -need_resume = [] -need_startup = True -def simulate(*args, **kwargs): - global need_resume, need_startup - - if need_startup: - internal.core.SimStartup() - need_startup = False - - for root in need_resume: - resume(root) - need_resume = [] - - return internal.event.simulate(*args, **kwargs) - -# Export curTick to user script. -def curTick(): - return internal.core.cvar.curTick - -# Python exit handlers happen in reverse order. We want to dump stats last. -atexit.register(internal.stats.dump) - -# register our C++ exit callback function with Python -atexit.register(internal.core.doExitCleanup) - -# This loops until all objects have been fully drained. -def doDrain(root): - all_drained = drain(root) - while (not all_drained): - all_drained = drain(root) - -# Tries to drain all objects. Draining might not be completed unless -# all objects return that they are drained on the first call. This is -# because as objects drain they may cause other objects to no longer -# be drained. -def drain(root): - all_drained = False - drain_event = internal.event.createCountedDrain() - unready_objects = root.startDrain(drain_event, True) - # If we've got some objects that can't drain immediately, then simulate - if unready_objects > 0: - drain_event.setCount(unready_objects) - simulate() - else: - all_drained = True - internal.event.cleanupCountedDrain(drain_event) - return all_drained - -def resume(root): - root.resume() - -def checkpoint(root, dir): - if not isinstance(root, objects.Root): - raise TypeError, "Checkpoint must be called on a root object." - doDrain(root) - print "Writing checkpoint" - internal.sim_object.serializeAll(dir) - resume(root) - -def restoreCheckpoint(root, dir): - print "Restoring from checkpoint" - internal.sim_object.unserializeAll(dir) - need_resume.append(root) - -def changeToAtomic(system): - if not isinstance(system, (objects.Root, objects.System)): - raise TypeError, "Parameter of type '%s'. Must be type %s or %s." % \ - (type(system), objects.Root, objects.System) - if system.getMemoryMode() != internal.sim_object.SimObject.Atomic: - doDrain(system) - print "Changing memory mode to atomic" - system.changeTiming(internal.sim_object.SimObject.Atomic) - -def changeToTiming(system): - if not isinstance(system, (objects.Root, objects.System)): - raise TypeError, "Parameter of type '%s'. Must be type %s or %s." % \ - (type(system), objects.Root, objects.System) - - if system.getMemoryMode() != internal.sim_object.SimObject.Timing: - doDrain(system) - print "Changing memory mode to timing" - system.changeTiming(internal.sim_object.SimObject.Timing) - -def switchCpus(cpuList): - print "switching cpus" - if not isinstance(cpuList, list): - raise RuntimeError, "Must pass a list to this function" - for i in cpuList: - if not isinstance(i, tuple): - raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" - - [old_cpus, new_cpus] = zip(*cpuList) - - for cpu in old_cpus: - if not isinstance(cpu, objects.BaseCPU): - raise TypeError, "%s is not of type BaseCPU" % cpu - for cpu in new_cpus: - if not isinstance(cpu, objects.BaseCPU): - raise TypeError, "%s is not of type BaseCPU" % cpu - - # Drain all of the individual CPUs - drain_event = internal.event.createCountedDrain() - unready_cpus = 0 - for old_cpu in old_cpus: - unready_cpus += old_cpu.startDrain(drain_event, False) - # If we've got some objects that can't drain immediately, then simulate - if unready_cpus > 0: - drain_event.setCount(unready_cpus) - simulate() - internal.event.cleanupCountedDrain(drain_event) - # Now all of the CPUs are ready to be switched out - for old_cpu in old_cpus: - old_cpu._ccObject.switchOut() - index = 0 - for new_cpu in new_cpus: - new_cpu.takeOverFrom(old_cpus[index]) - new_cpu._ccObject.resume() - index += 1 - # Since we have so many mutual imports in this package, we should: # 1. Put all intra-package imports at the *bottom* of the file, unless # they're absolutely needed before that (for top-level statements @@ -250,7 +75,24 @@ def switchCpus(cpuList): # you can get the wrong result if foo is only partially imported # at the point you do that (i.e., because foo is in the middle of # importing *you*). -from main import options -import objects +try: + import internal + running_m5 = True +except ImportError: + running_m5 = False + +if running_m5: + from event import * + from simulate import * + from main import options + +if running_m5: + import defines + build_env.update(defines.m5_build_env) +else: + import __scons + build_env.update(__scons.m5_build_env) + +import SimObject import params -from SimObject import resolveSimObject +import objects diff --git a/src/python/m5/environment.py b/src/python/m5/environment.py new file mode 100644 index 000000000..bea0bc1d0 --- /dev/null +++ b/src/python/m5/environment.py @@ -0,0 +1,43 @@ +# Copyright (c) 2005 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# 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: Nathan Binkert +# Steve Reinhardt + +import os + +# import the m5 compile options +import defines + +# make a SmartDict out of the build options for our local use +import smartdict +build_env = smartdict.SmartDict() +build_env.update(defines.m5_build_env) + +# make a SmartDict out of the OS environment too +env = smartdict.SmartDict() +env.update(os.environ) + diff --git a/src/python/m5/params.py b/src/python/m5/params.py index 88b162874..3fca4e97a 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -47,6 +47,7 @@ import copy import datetime import inspect +import re import sys import time @@ -55,6 +56,17 @@ import proxy import ticks from util import * +import SimObject + +def isSimObject(*args, **kwargs): + return SimObject.isSimObject(*args, **kwargs) + +def isSimObjectSequence(*args, **kwargs): + return SimObject.isSimObjectSequence(*args, **kwargs) + +def isSimObjectClass(*args, **kwargs): + return SimObject.isSimObjectClass(*args, **kwargs) + # Dummy base class to identify types that are legitimate for SimObject # parameters. class ParamValue(object): @@ -108,14 +120,15 @@ class ParamDesc(object): def __getattr__(self, attr): if attr == 'ptype': try: - ptype = eval(self.ptype_str, objects.__dict__) + ptype = SimObject.allClasses[self.ptype_str] if not isinstance(ptype, type): raise NameError self.ptype = ptype return ptype except NameError: - raise TypeError, \ - "Param qualifier '%s' is not a type" % self.ptype_str + raise + #raise TypeError, \ + # "Param qualifier '%s' is not a type" % self.ptype_str raise AttributeError, "'%s' object has no attribute '%s'" % \ (type(self).__name__, attr) @@ -150,6 +163,9 @@ class VectorParamValue(list): def ini_str(self): return ' '.join([v.ini_str() for v in self]) + def getValue(self): + return [ v.getValue() for v in self ] + def unproxy(self, base): return [v.unproxy(base) for v in self] @@ -165,20 +181,26 @@ 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 isSimObjectSequence(tmp_list): - return SimObjVector(tmp_list) - else: - return VectorParamValue(tmp_list) else: - # singleton: leave it be (could coerce to a single-element - # list here, but for some historical reason we don't... - return ParamDesc.convert(self, value) + # singleton: coerce to a single-element list + tmp_list = [ ParamDesc.convert(self, value) ] - def cxx_predecls(self): - return ['#include '] + self.ptype.cxx_predecls + if isSimObjectSequence(tmp_list): + return SimObjVector(tmp_list) + else: + return VectorParamValue(tmp_list) def swig_predecls(self): - return ['%include "std_vector.i"'] + self.ptype.swig_predecls + return ['%%include "%s_vptype.i"' % self.ptype_str] + + def swig_decl(self): + cxx_type = re.sub('std::', '', self.ptype.cxx_type) + vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \ + (self.ptype_str, cxx_type) + return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl] + + def cxx_predecls(self): + return ['#include '] + self.ptype.cxx_predecls def cxx_decl(self): return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) @@ -232,7 +254,10 @@ class String(ParamValue,str): cxx_predecls = ['#include '] swig_predecls = ['%include "std_string.i"\n' + '%apply const std::string& {std::string *};'] - pass + swig_predecls = ['%include "std_string.i"' ] + + def getValue(self): + return self # superclass for "numeric" parameter values, to emulate math # operations in a type-safe way. e.g., a Latency times an int returns @@ -291,7 +316,7 @@ class CheckedIntType(type): if not cls.swig_predecls: # most derived types require this, so we just do it here once - cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + cls.swig_predecls = ['%import "stdint.i"\n' + '%import "sim/host.hh"'] if not (hasattr(cls, 'min') and hasattr(cls, 'max')): @@ -328,6 +353,9 @@ class CheckedInt(NumericParamValue): % type(value).__name__ self._check() + def getValue(self): + return long(self.value) + class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True @@ -350,6 +378,9 @@ class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 class Float(ParamValue, float): cxx_type = 'double' + def getValue(self): + return float(self.value) + class MemorySize(CheckedInt): cxx_type = 'uint64_t' size = 64 @@ -374,7 +405,7 @@ class MemorySize32(CheckedInt): class Addr(CheckedInt): cxx_type = 'Addr' - cxx_predecls = ['#include "targetarch/isa_traits.hh"'] + cxx_predecls = ['#include "arch/isa_traits.hh"'] size = 64 unsigned = True def __init__(self, value): @@ -443,9 +474,27 @@ class Range(ParamValue): class AddrRange(Range): type = Addr + swig_predecls = ['%include "python/swig/range.i"'] + + def getValue(self): + from m5.objects.params import AddrRange + + value = AddrRange() + value.start = long(self.first) + value.end = long(self.second) + return value class TickRange(Range): type = Tick + swig_predecls = ['%include "python/swig/range.i"'] + + def getValue(self): + from m5.objects.params import TickRange + + value = TickRange() + value.start = long(self.first) + value.end = long(self.second) + return value # Boolean parameter type. Python doesn't let you subclass bool, since # it doesn't want to let you create multiple instances of True and @@ -458,6 +507,9 @@ class Bool(ParamValue): except TypeError: self.value = bool(value) + def getValue(self): + return bool(self.value) + def __str__(self): return str(self.value) @@ -489,7 +541,7 @@ def NextEthernetAddr(): class EthernetAddr(ParamValue): cxx_type = 'Net::EthAddr' cxx_predecls = ['#include "base/inet.hh"'] - swig_predecls = ['class Net::EthAddr;'] + swig_predecls = ['%include "python/swig/inet.i"'] def __init__(self, value): if value == NextEthernetAddr: self.value = value @@ -513,6 +565,10 @@ class EthernetAddr(ParamValue): return EthernetAddr(self.value()) return self + def getValue(self): + from m5.objects.params import EthAddr + return EthAddr(self.value) + def ini_str(self): return self.value @@ -555,13 +611,40 @@ def parse_time(value): raise ValueError, "Could not parse '%s' as a time" % value class Time(ParamValue): - cxx_type = 'time_t' + cxx_type = 'tm' + cxx_predecls = [ '#include ' ] + swig_predecls = [ '%include "python/swig/time.i"' ] def __init__(self, value): self.value = parse_time(value) + def getValue(self): + from m5.objects.params import tm + + c_time = tm() + py_time = self.value + + # UNIX is years since 1900 + c_time.tm_year = py_time.tm_year - 1900; + + # Python starts at 1, UNIX starts at 0 + c_time.tm_mon = py_time.tm_mon - 1; + c_time.tm_mday = py_time.tm_mday; + c_time.tm_hour = py_time.tm_hour; + c_time.tm_min = py_time.tm_min; + c_time.tm_sec = py_time.tm_sec; + + # Python has 0 as Monday, UNIX is 0 as sunday + c_time.tm_wday = py_time.tm_wday + 1 + if c_time.tm_wday > 6: + c_time.tm_wday -= 7; + + # Python starts at 1, Unix starts at 0 + c_time.tm_yday = py_time.tm_yday - 1; + + return c_time + def __str__(self): - tm = self.value - return ' '.join([ str(tm[i]) for i in xrange(8)]) + return time.asctime(self.value) def ini_str(self): return str(self) @@ -580,9 +663,16 @@ class Time(ParamValue): # classes (_ListEnum and _DictEnum) to serve as base classes, then # derive the new type from the appropriate base class on the fly. - +allEnums = {} # Metaclass for Enum types class MetaEnum(type): + def __new__(mcls, name, bases, dict): + assert name not in allEnums + + cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) + allEnums[name] = cls + return cls + def __init__(cls, name, bases, init_dict): if init_dict.has_key('map'): if not isinstance(cls.map, dict): @@ -603,18 +693,42 @@ class MetaEnum(type): raise TypeError, "Enum-derived class must define "\ "attribute 'map' or 'vals'" - cls.cxx_type = name + '::Enum' + cls.cxx_type = 'Enums::%s' % name super(MetaEnum, cls).__init__(name, bases, init_dict) + def __str__(cls): + return cls.__name__ + # Generate C++ class declaration for this enum type. # Note that we wrap the enum in a class/struct to act as a namespace, # so that the enum strings can be brief w/o worrying about collisions. def cxx_decl(cls): - s = 'struct %s {\n enum Enum {\n ' % cls.__name__ - s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) - s += '\n };\n};\n' - return s + code = "#ifndef __ENUM__%s\n" % cls + code += '#define __ENUM__%s\n' % cls + code += '\n' + code += 'namespace Enums {\n' + code += ' enum %s {\n' % cls + for val in cls.vals: + code += ' %s = %d,\n' % (val, cls.map[val]) + code += ' Num_%s = %d,\n' % (cls, len(cls.vals)) + code += ' };\n' + code += ' extern const char *%sStrings[Num_%s];\n' % (cls, cls) + code += '}\n' + code += '\n' + code += '#endif\n' + return code + + def cxx_def(cls): + code = '#include "enums/%s.hh"\n' % cls + code += 'namespace Enums {\n' + code += ' const char *%sStrings[Num_%s] =\n' % (cls, cls) + code += ' {\n' + for val in cls.vals: + code += ' "%s",\n' % val + code += ' };\n' + code += '}\n' + return code # Base class for enum types. class Enum(ParamValue): @@ -627,6 +741,9 @@ class Enum(ParamValue): % (value, self.vals) self.value = value + def getValue(self): + return int(self.map[self.value]) + def __str__(self): return self.value @@ -636,9 +753,12 @@ frequency_tolerance = 0.001 # 0.1% class TickParamValue(NumericParamValue): cxx_type = 'Tick' cxx_predecls = ['#include "sim/host.hh"'] - swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + swig_predecls = ['%import "stdint.i"\n' + '%import "sim/host.hh"'] + def getValue(self): + return long(self.value) + class Latency(TickParamValue): def __init__(self, value): if isinstance(value, (Latency, Clock)): @@ -661,12 +781,16 @@ class Latency(TickParamValue): return Frequency(self) raise AttributeError, "Latency object has no attribute '%s'" % attr - # convert latency to ticks - def ini_str(self): + def getValue(self): if self.ticks or self.value == 0: - return '%d' % self.value + value = self.value else: - return '%d' % (ticks.fromSeconds(self.value)) + value = ticks.fromSeconds(self.value) + return long(value) + + # convert latency to ticks + def ini_str(self): + return '%d' % self.getValue() class Frequency(TickParamValue): def __init__(self, value): @@ -691,11 +815,15 @@ class Frequency(TickParamValue): raise AttributeError, "Frequency object has no attribute '%s'" % attr # convert latency to ticks - def ini_str(self): + def getValue(self): if self.ticks or self.value == 0: - return '%d' % self.value + value = self.value else: - return '%d' % (ticks.fromSeconds(1.0 / self.value)) + value = ticks.fromSeconds(1.0 / self.value) + return long(value) + + def ini_str(self): + return '%d' % self.getValue() # A generic frequency and/or Latency value. Value is stored as a latency, # but to avoid ambiguity this object does not support numeric ops (* or /). @@ -703,7 +831,7 @@ class Frequency(TickParamValue): class Clock(ParamValue): cxx_type = 'Tick' cxx_predecls = ['#include "sim/host.hh"'] - swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + swig_predecls = ['%import "stdint.i"\n' + '%import "sim/host.hh"'] def __init__(self, value): if isinstance(value, (Latency, Clock)): @@ -726,6 +854,9 @@ class Clock(ParamValue): return Latency(self) raise AttributeError, "Frequency object has no attribute '%s'" % attr + def getValue(self): + return self.period.getValue() + def ini_str(self): return self.period.ini_str() @@ -739,11 +870,15 @@ class NetworkBandwidth(float,ParamValue): def __str__(self): return str(self.val) - def ini_str(self): + def getValue(self): # convert to seconds per byte value = 8.0 / float(self) # convert to ticks per byte - return '%f' % (ticks.fromSeconds(value)) + value = ticks.fromSeconds(value) + return float(value) + + def ini_str(self): + return '%f' % self.getValue() class MemoryBandwidth(float,ParamValue): cxx_type = 'float' @@ -755,11 +890,15 @@ class MemoryBandwidth(float,ParamValue): def __str__(self): return str(self.val) - def ini_str(self): + def getValue(self): # convert to seconds per byte value = 1.0 / float(self) # convert to ticks per byte - return '%f' % (ticks.fromSeconds(value)) + value = ticks.fromSeconds(value) + return float(value) + + def ini_str(self): + return '%f' % self.getValue() # # "Constants"... handy aliases for various values. @@ -786,9 +925,13 @@ class NullSimObject(object): def set_path(self, parent, name): pass + def __str__(self): return 'Null' + def getValue(self): + return None + # The only instance you'll ever need... NULL = NullSimObject() @@ -882,6 +1025,8 @@ class PortRef(object): # Call C++ to create corresponding port connection between C++ objects def ccConnect(self): + import internal + if self.ccConnected: # already done this return peer = self.peer @@ -1006,7 +1151,6 @@ class PortParamDesc(object): ptype_str = 'Port' ptype = Port - __all__ = ['Param', 'VectorParam', 'Enum', 'Bool', 'String', 'Float', 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', @@ -1021,8 +1165,3 @@ __all__ = ['Param', 'VectorParam', 'Time', 'NextEthernetAddr', 'NULL', 'Port', 'VectorPort'] - -# see comment on imports at end of __init__.py. -from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass -import objects -import internal diff --git a/src/python/m5/simulate.py b/src/python/m5/simulate.py new file mode 100644 index 000000000..1ef78d6cb --- /dev/null +++ b/src/python/m5/simulate.py @@ -0,0 +1,199 @@ +# Copyright (c) 2005 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# 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: Nathan Binkert +# Steve Reinhardt + +import atexit +import os +import sys + +# import the SWIG-wrapped main C++ functions +import internal +from main import options +import SimObject +import ticks + +# The final hook to generate .ini files. Called from the user script +# once the config is built. +def instantiate(root): + # we need to fix the global frequency + ticks.fixGlobalFrequency() + + root.unproxy_all() + # ugly temporary hack to get output to config.ini + sys.stdout = file(os.path.join(options.outdir, 'config.ini'), 'w') + root.print_ini() + sys.stdout.close() # close config.ini + sys.stdout = sys.__stdout__ # restore to original + + # Initialize the global statistics + internal.stats.initSimStats() + + # Create the C++ sim objects and connect ports + root.createCCObject() + root.connectPorts() + + # Do a second pass to finish initializing the sim objects + internal.sim_object.initAll() + + # Do a third pass to initialize statistics + internal.sim_object.regAllStats() + + # Check to make sure that the stats package is properly initialized + internal.stats.check() + + # Reset to put the stats in a consistent state. + internal.stats.reset() + +def doDot(root): + 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") + +need_resume = [] +need_startup = True +def simulate(*args, **kwargs): + global need_resume, need_startup + + if need_startup: + internal.core.SimStartup() + need_startup = False + + for root in need_resume: + resume(root) + need_resume = [] + + return internal.event.simulate(*args, **kwargs) + +# Export curTick to user script. +def curTick(): + return internal.core.cvar.curTick + +# Python exit handlers happen in reverse order. We want to dump stats last. +atexit.register(internal.stats.dump) + +# register our C++ exit callback function with Python +atexit.register(internal.core.doExitCleanup) + +# This loops until all objects have been fully drained. +def doDrain(root): + all_drained = drain(root) + while (not all_drained): + all_drained = drain(root) + +# Tries to drain all objects. Draining might not be completed unless +# all objects return that they are drained on the first call. This is +# because as objects drain they may cause other objects to no longer +# be drained. +def drain(root): + all_drained = False + drain_event = internal.event.createCountedDrain() + unready_objects = root.startDrain(drain_event, True) + # If we've got some objects that can't drain immediately, then simulate + if unready_objects > 0: + drain_event.setCount(unready_objects) + simulate() + else: + all_drained = True + internal.event.cleanupCountedDrain(drain_event) + return all_drained + +def resume(root): + root.resume() + +def checkpoint(root, dir): + if not isinstance(root, objects.Root): + raise TypeError, "Checkpoint must be called on a root object." + doDrain(root) + print "Writing checkpoint" + internal.sim_object.serializeAll(dir) + resume(root) + +def restoreCheckpoint(root, dir): + print "Restoring from checkpoint" + internal.sim_object.unserializeAll(dir) + need_resume.append(root) + +def changeToAtomic(system): + if not isinstance(system, (objects.Root, objects.System)): + raise TypeError, "Parameter of type '%s'. Must be type %s or %s." % \ + (type(system), objects.Root, objects.System) + if system.getMemoryMode() != internal.sim_object.SimObject.Atomic: + doDrain(system) + print "Changing memory mode to atomic" + system.changeTiming(internal.sim_object.SimObject.Atomic) + +def changeToTiming(system): + if not isinstance(system, (objects.Root, objects.System)): + raise TypeError, "Parameter of type '%s'. Must be type %s or %s." % \ + (type(system), objects.Root, objects.System) + + if system.getMemoryMode() != internal.sim_object.SimObject.Timing: + doDrain(system) + print "Changing memory mode to timing" + system.changeTiming(internal.sim_object.SimObject.Timing) + +def switchCpus(cpuList): + print "switching cpus" + if not isinstance(cpuList, list): + raise RuntimeError, "Must pass a list to this function" + for i in cpuList: + if not isinstance(i, tuple): + raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" + + [old_cpus, new_cpus] = zip(*cpuList) + + for cpu in old_cpus: + if not isinstance(cpu, objects.BaseCPU): + raise TypeError, "%s is not of type BaseCPU" % cpu + for cpu in new_cpus: + if not isinstance(cpu, objects.BaseCPU): + raise TypeError, "%s is not of type BaseCPU" % cpu + + # Drain all of the individual CPUs + drain_event = internal.event.createCountedDrain() + unready_cpus = 0 + for old_cpu in old_cpus: + unready_cpus += old_cpu.startDrain(drain_event, False) + # If we've got some objects that can't drain immediately, then simulate + if unready_cpus > 0: + drain_event.setCount(unready_cpus) + simulate() + internal.event.cleanupCountedDrain(drain_event) + # Now all of the CPUs are ready to be switched out + for old_cpu in old_cpus: + old_cpu._ccObject.switchOut() + index = 0 + for new_cpu in new_cpus: + new_cpu.takeOverFrom(old_cpus[index]) + new_cpu._ccObject.resume() + index += 1 diff --git a/src/python/m5/ticks.py b/src/python/m5/ticks.py index e91b470ff..91834c9c8 100644 --- a/src/python/m5/ticks.py +++ b/src/python/m5/ticks.py @@ -28,14 +28,12 @@ import sys -import convert -import internal - tps = 1.0e12 # default to 1 THz (1 Tick == 1 ps) tps_fixed = False # once set to true, can't be changed # fix the global frequency and tell C++ about it def fixGlobalFrequency(): + import internal global tps, tps_fixed if not tps_fixed: tps_fixed = True @@ -43,6 +41,8 @@ def fixGlobalFrequency(): print "Global frequency set at %d ticks per second" % int(tps) def setGlobalFrequency(ticksPerSecond): + import convert + global tps, tps_fixed if tps_fixed: -- cgit v1.2.3