summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorAli Saidi <saidi@eecs.umich.edu>2005-03-14 15:38:26 -0500
committerAli Saidi <saidi@eecs.umich.edu>2005-03-14 15:38:26 -0500
commit76e6dd01ae4a534adad1d34398fefc819771a781 (patch)
treea678ca355625b9199cbb0f64ca42d247e520accb /python
parentc1f5b983f0c8cece7a8387b05b40889c9520fb39 (diff)
parentbc2923f78d739ad5ff42dee402c5ba27c02004f1 (diff)
downloadgem5-76e6dd01ae4a534adad1d34398fefc819771a781.tar.xz
Merge zizzer:/bk/m5 into zeep.eecs.umich.edu:/z/saidi/work/m5
--HG-- extra : convert_revision : 9eed6f31249ff099464044b32b882b3cc041b57a
Diffstat (limited to 'python')
-rw-r--r--python/SConscript39
-rw-r--r--python/m5/__init__.py34
-rw-r--r--python/m5/config.py236
-rw-r--r--python/m5/objects/BaseCPU.mpy2
-rw-r--r--python/m5/smartdict.py23
5 files changed, 208 insertions, 126 deletions
diff --git a/python/SConscript b/python/SConscript
index 81bc52286..9c15c6d50 100644
--- a/python/SConscript
+++ b/python/SConscript
@@ -26,7 +26,11 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import os, os.path, re
+import os, os.path, re, sys
+
+Import('env')
+
+import m5scons
def WriteEmbeddedPyFile(target, source, path, name, ext, filename):
if isinstance(source, str):
@@ -89,7 +93,6 @@ def splitpath(path):
path.insert(0, base)
return path, file
-Import('env')
def MakeEmbeddedPyFile(target, source, env):
target = file(str(target[0]), 'w')
@@ -145,27 +148,11 @@ def MakeEmbeddedPyFile(target, source, env):
WriteEmbeddedPyFile(target, pyfile, path, name, ext, filename)
def MakeDefinesPyFile(target, source, env):
- target = file(str(target[0]), 'w')
-
- print >>target, "import os"
- defines = env['CPPDEFINES']
- if isinstance(defines, list):
- for var in defines:
- if isinstance(var, tuple):
- key,val = var
- else:
- key,val = var,'True'
-
- if not isinstance(key, basestring):
- panic("invalid type for define: %s" % type(key))
-
- print >>target, "os.environ['%s'] = '%s'" % (key, val)
-
- elif isinstance(defines, dict):
- for key,val in defines.iteritems():
- print >>target, "os.environ['%s'] = '%s'" % (key, val)
- else:
- panic("invalid type for defines: %s" % type(defines))
+ f = file(str(target[0]), 'w')
+ print >>f, "import __main__"
+ print >>f, "__main__.m5_build_env = ",
+ print >>f, m5scons.flatten_defines(env['CPPDEFINES'])
+ f.close()
CFileCounter = 0
def MakePythonCFile(target, source, env):
@@ -193,8 +180,10 @@ EmbedMap %(name)s("%(fname)s",
/* namespace */ }
'''
-embedded_py_files = [ 'mpy_importer.py', '../util/pbs/jobfile.py' ]
-objpath = os.path.join(env['SRCDIR'], 'python/m5')
+# base list of .py files to embed
+embedded_py_files = [ '../util/pbs/jobfile.py' ]
+# add all .py and .mpy files in python/m5
+objpath = os.path.join(env['SRCDIR'], 'python', 'm5')
for root, dirs, files in os.walk(objpath, topdown=True):
for i,dir in enumerate(dirs):
if dir == 'SCCS':
diff --git a/python/m5/__init__.py b/python/m5/__init__.py
index 7cb3a32c6..16f48dba3 100644
--- a/python/m5/__init__.py
+++ b/python/m5/__init__.py
@@ -1,7 +1,37 @@
-from mpy_importer import *
+import sys, os
+
+# the mpy import code is added to the global import meta_path as a
+# side effect of this import
+from mpy_importer import AddToPath, LoadMpyFile
+
+# define this here so we can use it right away if necessary
+def panic(string):
+ print >>sys.stderr, 'panic:', string
+ sys.exit(1)
+
+# find the m5 compile options: must be specified as a dict in
+# __main__.m5_build_env.
+import __main__
+if not hasattr(__main__, 'm5_build_env'):
+ panic("__main__ must define m5_build_env")
+
+# make a SmartDict out of the build options for our local use
+import smartdict
+build_env = smartdict.SmartDict()
+build_env.update(__main__.m5_build_env)
+
+# make a SmartDict out of the OS environment too
+env = smartdict.SmartDict()
+env.update(os.environ)
+
+# import the main m5 config code
from config import *
+config.add_param_types(config)
+
+# import the built-in object definitions
from objects import *
+config.add_param_types(objects)
-cpp_classes = MetaSimObject.cpp_classes
+cpp_classes = config.MetaSimObject.cpp_classes
cpp_classes.sort()
diff --git a/python/m5/config.py b/python/m5/config.py
index 2c5a70b25..a9d7a2f41 100644
--- a/python/m5/config.py
+++ b/python/m5/config.py
@@ -27,8 +27,6 @@
from __future__ import generators
import os, re, sys, types, inspect
-from mpy_importer import AddToPath, LoadMpyFile
-from smartdict import SmartDict
from convert import *
noDot = False
@@ -37,13 +35,6 @@ try:
except:
noDot = True
-env = SmartDict()
-env.update(os.environ)
-
-def panic(string):
- print >>sys.stderr, 'panic:', string
- sys.exit(1)
-
def issequence(value):
return isinstance(value, tuple) or isinstance(value, list)
@@ -210,6 +201,28 @@ class_decorator = 'M5M5_SIMOBJECT_'
expr_decorator = 'M5M5_EXPRESSION_'
dot_decorator = '_M5M5_DOT_'
+# 'Global' map of legitimate types for SimObject parameters.
+param_types = {}
+
+# Dummy base class to identify types that are legitimate for SimObject
+# parameters.
+class ParamType(object):
+ pass
+
+# Add types defined in given context (dict or module) that are derived
+# from ParamType to param_types map.
+def add_param_types(ctx):
+ if isinstance(ctx, types.DictType):
+ source_dict = ctx
+ elif isinstance(ctx, types.ModuleType):
+ source_dict = ctx.__dict__
+ else:
+ raise TypeError, \
+ "m5.config.add_param_types requires dict or module as arg"
+ for key,val in source_dict.iteritems():
+ if isinstance(val, type) and issubclass(val, ParamType):
+ param_types[key] = val
+
# 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
@@ -247,7 +260,7 @@ class MetaConfigNode(type):
# initialize required attributes
cls._params = {}
cls._values = {}
- cls._enums = {}
+ cls._param_types = {}
cls._bases = [c for c in cls.__mro__ if isConfigNode(c)]
cls._anon_subclass_counter = 0
@@ -270,19 +283,26 @@ class MetaConfigNode(type):
elif isNullPointer(val):
cls._values[key] = val
- # now process _init_dict items
+ # process param types from _init_dict, as these may be needed
+ # by param descriptions also in _init_dict
for key,val in cls._init_dict.items():
+ if isinstance(val, type) and issubclass(val, ParamType):
+ cls._param_types[key] = val
+ if not issubclass(val, ConfigNode):
+ del cls._init_dict[key]
+
+ # now process remaining _init_dict items
+ for key,val in cls._init_dict.items():
+ # param descriptions
if isinstance(val, _Param):
cls._params[key] = val
+ # try to resolve local param types in local param_types scope
+ val.maybe_resolve_type(cls._param_types)
# init-time-only keywords
elif cls.init_keywords.has_key(key):
cls._set_keyword(key, val, cls.init_keywords[key])
- # enums
- elif isinstance(val, type) and issubclass(val, Enum):
- cls._enums[key] = val
-
# See description of decorators in the importer.py file.
# We just strip off the expr_decorator now since we don't
# need from this point on.
@@ -419,7 +439,11 @@ class MetaConfigNode(type):
# It's ok: set attribute by delegating to 'object' class.
# Note the use of param.make_value() to verify/canonicalize
# the assigned value
- param.valid(value)
+ try:
+ param.valid(value)
+ except:
+ panic("Error setting param %s.%s to %s\n" % \
+ (cls.__name__, attr, value))
cls._setvalue(attr, value)
elif isConfigNode(value) or isSimObjSequence(value):
cls._setvalue(attr, value)
@@ -562,7 +586,7 @@ class MetaSimObject(MetaConfigNode):
def _cpp_decl(cls):
name = cls.__name__
code = ""
- code += "\n".join([e.cpp_declare() for e in cls._enums.values()])
+ code += "\n".join([e.cpp_declare() for e in cls._param_types.values()])
code += "\n"
param_names = cls._params.keys()
param_names.sort()
@@ -854,13 +878,24 @@ class _Param(object):
if not hasattr(self, 'desc'):
raise TypeError, 'desc attribute missing'
+ def maybe_resolve_type(self, context):
+ # check if already resolved... don't use hasattr(),
+ # as that calls __getattr__()
+ if self.__dict__.has_key('ptype'):
+ return
+ try:
+ self.ptype = context[self.ptype_string]
+ except KeyError:
+ # no harm in trying... we'll try again later using global scope
+ pass
+
def __getattr__(self, attr):
if attr == 'ptype':
try:
- self.ptype = eval(self.ptype_string)
+ self.ptype = param_types[self.ptype_string]
return self.ptype
except:
- raise TypeError, 'Param.%s: undefined type' % self.ptype_string
+ panic("undefined Param type %s" % self.ptype_string)
else:
raise AttributeError, "'%s' object has no attribute '%s'" % \
(type(self).__name__, attr)
@@ -887,19 +922,10 @@ class _ParamProxy(object):
# E.g., Param.Int(5, "number of widgets")
def __call__(self, *args, **kwargs):
- # Param type could be defined only in context of caller (e.g.,
- # for locally defined Enum subclass). Need to go look up the
- # type in that enclosing scope.
- caller_frame = inspect.stack()[1][0]
- ptype = caller_frame.f_locals.get(self.ptype, None)
- if not ptype: ptype = caller_frame.f_globals.get(self.ptype, None)
- if not ptype: ptype = globals().get(self.ptype, None)
- # ptype could still be None due to circular references... we'll
- # try one more time to evaluate lazily when ptype is first needed.
- # In the meantime we'll save the type name as a string.
- if not ptype: ptype = self.ptype
- return _Param(ptype, *args, **kwargs)
+ return _Param(self.ptype, *args, **kwargs)
+ # Strange magic to theoretically allow dotted names as Param classes,
+ # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
def __getattr__(self, attr):
if attr == '__bases__':
raise AttributeError, ''
@@ -975,8 +1001,33 @@ VectorParam = _VectorParamProxy(None)
# to correspond to distinct C++ types as well.
#
#####################################################################
-# Integer parameter type.
-class _CheckedInt(object):
+
+
+# Metaclass for bounds-checked integer parameters. See CheckedInt.
+class CheckedIntType(type):
+ def __init__(cls, name, bases, dict):
+ super(CheckedIntType, cls).__init__(name, bases, dict)
+
+ # CheckedInt is an abstract base class, so we actually don't
+ # want to do any processing on it... the rest of this code is
+ # just for classes that derive from CheckedInt.
+ if name == 'CheckedInt':
+ return
+
+ if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
+ if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
+ panic("CheckedInt subclass %s must define either\n" \
+ " 'min' and 'max' or 'size' and 'unsigned'\n" \
+ % name);
+ if cls.unsigned:
+ cls.min = 0
+ cls.max = 2 ** cls.size - 1
+ else:
+ cls.min = -(2 ** (cls.size - 1))
+ cls.max = (2 ** (cls.size - 1)) - 1
+
+ cls._cpp_param_decl = cls.cppname
+
def _convert(cls, value):
if isinstance(value, bool):
return int(value)
@@ -987,86 +1038,72 @@ class _CheckedInt(object):
if isinstance(value, (str, float)):
value = long(float(value))
- if not cls._min <= value <= cls._max:
+ if not cls.min <= value <= cls.max:
raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
- (cls._min, value, cls._max)
+ (cls.min, value, cls.max)
return value
- _convert = classmethod(_convert)
def _string(cls, value):
return str(value)
- _string = classmethod(_string)
-
-class CheckedInt(type):
- def __new__(cls, cppname, min, max):
- # New class derives from _CheckedInt base with proper bounding
- # parameters
- dict = { '_cpp_param_decl' : cppname, '_min' : min, '_max' : max }
- return type.__new__(cls, cppname, (_CheckedInt, ), dict)
-
-class CheckedIntType(CheckedInt):
- def __new__(cls, cppname, size, unsigned):
- dict = {}
- if unsigned:
- min = 0
- max = 2 ** size - 1
- else:
- min = -(2 ** (size - 1))
- max = (2 ** (size - 1)) - 1
- return super(cls, CheckedIntType).__new__(cls, cppname, min, max)
+# Abstract superclass for bounds-checked integer parameters. This
+# class is subclassed to generate parameter classes with specific
+# bounds. Initialization of the min and max bounds is done in the
+# metaclass CheckedIntType.__init__.
+class CheckedInt(ParamType):
+ __metaclass__ = CheckedIntType
-Int = CheckedIntType('int', 32, False)
-Unsigned = CheckedIntType('unsigned', 32, True)
+class Int(CheckedInt): cppname = 'int'; size = 32; unsigned = False
+class Unsigned(CheckedInt): cppname = 'unsigned'; size = 32; unsigned = True
-Int8 = CheckedIntType('int8_t', 8, False)
-UInt8 = CheckedIntType('uint8_t', 8, True)
-Int16 = CheckedIntType('int16_t', 16, False)
-UInt16 = CheckedIntType('uint16_t', 16, True)
-Int32 = CheckedIntType('int32_t', 32, False)
-UInt32 = CheckedIntType('uint32_t', 32, True)
-Int64 = CheckedIntType('int64_t', 64, False)
-UInt64 = CheckedIntType('uint64_t', 64, True)
+class Int8(CheckedInt): cppname = 'int8_t'; size = 8; unsigned = False
+class UInt8(CheckedInt): cppname = 'uint8_t'; size = 8; unsigned = True
+class Int16(CheckedInt): cppname = 'int16_t'; size = 16; unsigned = False
+class UInt16(CheckedInt): cppname = 'uint16_t'; size = 16; unsigned = True
+class Int32(CheckedInt): cppname = 'int32_t'; size = 32; unsigned = False
+class UInt32(CheckedInt): cppname = 'uint32_t'; size = 32; unsigned = True
+class Int64(CheckedInt): cppname = 'int64_t'; size = 64; unsigned = False
+class UInt64(CheckedInt): cppname = 'uint64_t'; size = 64; unsigned = True
-Counter = CheckedIntType('Counter', 64, True)
-Addr = CheckedIntType('Addr', 64, True)
-Tick = CheckedIntType('Tick', 64, True)
+class Counter(CheckedInt): cppname = 'Counter'; size = 64; unsigned = True
+class Addr(CheckedInt): cppname = 'Addr'; size = 64; unsigned = True
+class Tick(CheckedInt): cppname = 'Tick'; size = 64; unsigned = True
-Percent = CheckedInt('int', 0, 100)
+class Percent(CheckedInt): cppname = 'int'; min = 0; max = 100
class Pair(object):
def __init__(self, first, second):
self.first = first
self.second = second
-class _Range(object):
+class MetaRange(type):
+ def __init__(cls, name, bases, dict):
+ super(MetaRange, cls).__init__(name, bases, dict)
+ if name == 'Range':
+ return
+ cls._cpp_param_decl = 'Range<%s>' % cls.type._cpp_param_decl
+
def _convert(cls, value):
if not isinstance(value, Pair):
raise TypeError, 'value %s is not a Pair' % value
- return Pair(cls._type._convert(value.first),
- cls._type._convert(value.second))
- _convert = classmethod(_convert)
+ return Pair(cls.type._convert(value.first),
+ cls.type._convert(value.second))
def _string(cls, value):
- return '%s:%s' % (cls._type._string(value.first),
- cls._type._string(value.second))
- _string = classmethod(_string)
+ return '%s:%s' % (cls.type._string(value.first),
+ cls.type._string(value.second))
+
+class Range(ParamType):
+ __metaclass__ = MetaRange
def RangeSize(start, size):
return Pair(start, start + size - 1)
-class Range(type):
- def __new__(cls, type):
- dict = { '_cpp_param_decl' : 'Range<%s>' % type._cpp_param_decl,
- '_type' : type }
- clsname = 'Range_' + type.__name__
- return super(cls, Range).__new__(cls, clsname, (_Range, ), dict)
-
-AddrRange = Range(Addr)
+class AddrRange(Range): type = Addr
# Boolean parameter type.
-class Bool(object):
+class Bool(ParamType):
_cpp_param_decl = 'bool'
def _convert(value):
t = type(value)
@@ -1094,7 +1131,7 @@ class Bool(object):
_string = staticmethod(_string)
# String-valued parameter.
-class String(object):
+class String(ParamType):
_cpp_param_decl = 'string'
# Constructor. Value must be Python string.
@@ -1134,7 +1171,7 @@ class NextEthernetAddr(object):
self.value = self.addr
self.addr = IncEthernetAddr(self.addr, inc)
-class EthernetAddr(object):
+class EthernetAddr(ParamType):
_cpp_param_decl = 'EthAddr'
def _convert(cls, value):
@@ -1241,7 +1278,7 @@ class MetaEnum(type):
return s
# Base class for enum types.
-class Enum(object):
+class Enum(ParamType):
__metaclass__ = MetaEnum
vals = []
@@ -1261,8 +1298,8 @@ class Enum(object):
#
# Some memory range specifications use this as a default upper bound.
-MAX_ADDR = Addr._max
-MaxTick = Tick._max
+MAX_ADDR = Addr.max
+MaxTick = Tick.max
# For power-of-two sizing, e.g. 64*K gives an integer value 65536.
K = 1024
@@ -1274,9 +1311,6 @@ G = K*M
# The final hook to generate .ini files. Called from configuration
# script once config is built.
def instantiate(root):
- if not issubclass(root, Root):
- raise AttributeError, 'Can only instantiate the Root of the tree'
-
instance = root.instantiate('root')
instance.fixup()
instance.display()
@@ -1297,8 +1331,20 @@ def instantiate(root):
# parameters to be set by keyword in the constructor. Note that most
# of the heavy lifting for the SimObject param handling is done in the
# MetaConfigNode metaclass.
-class SimObject(ConfigNode):
+class SimObject(ConfigNode, ParamType):
__metaclass__ = MetaSimObject
type = 'SimObject'
-from objects import *
+
+# __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__ = ['issequence',
+ 'ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam',
+ 'Super', 'Enum',
+ 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
+ 'Int32', 'UInt32', 'Int64', 'UInt64',
+ 'Counter', 'Addr', 'Tick', 'Percent',
+ 'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M',
+ 'NextEthernetAddr',
+ 'instantiate']
diff --git a/python/m5/objects/BaseCPU.mpy b/python/m5/objects/BaseCPU.mpy
index be93e8ad1..5d8305d88 100644
--- a/python/m5/objects/BaseCPU.mpy
+++ b/python/m5/objects/BaseCPU.mpy
@@ -4,7 +4,7 @@ simobj BaseCPU(SimObject):
icache = Param.BaseMem(NULL, "L1 instruction cache object")
dcache = Param.BaseMem(NULL, "L1 data cache object")
- if env.get('FULL_SYSTEM', 'False'):
+ if build_env['FULL_SYSTEM']:
dtb = Param.AlphaDTB("Data TLB")
itb = Param.AlphaITB("Instruction TLB")
mem = Param.FunctionalMemory("memory")
diff --git a/python/m5/smartdict.py b/python/m5/smartdict.py
index e282bc07b..4ea8210d3 100644
--- a/python/m5/smartdict.py
+++ b/python/m5/smartdict.py
@@ -1,6 +1,23 @@
+# The SmartDict class fixes a couple of issues with using the content
+# of os.environ or similar dicts of strings as Python variables:
+#
+# 1) Undefined variables should return False rather than raising KeyError.
+#
+# 2) String values of 'False', '0', etc., should evaluate to False
+# (not just the empty string).
+#
+# #1 is solved by overriding __getitem__, and #2 is solved by using a
+# proxy class for values and overriding __nonzero__ on the proxy.
+# Everything else is just to (a) make proxies behave like normal
+# values otherwise, (b) make sure any dict operation returns a proxy
+# rather than a normal value, and (c) coerce values written to the
+# dict to be strings.
+
+
from convert import *
class SmartDict(dict):
+
class Proxy(str):
def __int__(self):
return int(to_integer(str(self)))
@@ -58,7 +75,7 @@ class SmartDict(dict):
def __getitem__(self, key):
- return self.Proxy(dict.__getitem__(self, key))
+ return self.Proxy(dict.get(self, key, 'False'))
def __setitem__(self, key, item):
dict.__setitem__(self, key, str(item))
@@ -77,9 +94,9 @@ class SmartDict(dict):
for key,value in dict.iteritems(self):
yield key, self.Proxy(value)
- def get(self, key, default=''):
+ def get(self, key, default='False'):
return self.Proxy(dict.get(self, key, str(default)))
- def setdefault(self, key, default=''):
+ def setdefault(self, key, default='False'):
return self.Proxy(dict.setdefault(self, key, str(default)))