+# Copyright (c) 2012-2014, 2017-2019 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
# Copyright (c) 2004-2006 The Regents of The University of Michigan
+# Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
#
# Authors: Steve Reinhardt
# Nathan Binkert
+# Gabe Black
+# Andreas Hansson
#####################################################################
#
#
#####################################################################
-import sys, inspect, copy
-import convert
-from util import *
+from __future__ import print_function
+import six
+if six.PY3:
+ long = int
+
+import copy
+import datetime
+import re
+import sys
+import time
+import math
+
+from . import proxy
+from . import ticks
+from .util import *
+
+def isSimObject(*args, **kwargs):
+ from . import SimObject
+ return SimObject.isSimObject(*args, **kwargs)
+
+def isSimObjectSequence(*args, **kwargs):
+ from . import SimObject
+ return SimObject.isSimObjectSequence(*args, **kwargs)
+
+def isSimObjectClass(*args, **kwargs):
+ from . import SimObject
+ return SimObject.isSimObjectClass(*args, **kwargs)
+
+allParams = {}
+
+class MetaParamValue(type):
+ def __new__(mcls, name, bases, dct):
+ cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
+ assert name not in allParams
+ allParams[name] = cls
+ return cls
+
# Dummy base class to identify types that are legitimate for SimObject
# parameters.
class ParamValue(object):
+ __metaclass__ = MetaParamValue
+ cmd_line_settable = False
+
+ # Generate the code needed as a prerequisite for declaring a C++
+ # object of this type. Typically generates one or more #include
+ # statements. Used when declaring parameters of this type.
+ @classmethod
+ def cxx_predecls(cls, code):
+ pass
- cxx_predecls = []
- swig_predecls = []
+ @classmethod
+ def pybind_predecls(cls, code):
+ cls.cxx_predecls(code)
# default for printing to .ini file is regular string conversion.
# will be overridden in some cases
def ini_str(self):
return str(self)
+ # default for printing to .json file is regular string conversion.
+ # will be overridden in some cases, mostly to use native Python
+ # types where there are similar JSON types
+ def config_value(self):
+ return str(self)
+
+ # Prerequisites for .ini parsing with cxx_ini_parse
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ pass
+
+ # parse a .ini file entry for this param from string expression
+ # src into lvalue dest (of the param's C++ type)
+ @classmethod
+ def cxx_ini_parse(cls, code, src, dest, ret):
+ code('// Unhandled param type: %s' % cls.__name__)
+ code('%s false;' % ret)
+
# allows us to blithely call unproxy() on things without checking
# if they're really proxies or not
def unproxy(self, base):
return self
+ # Produce a human readable version of the stored value
+ def pretty_print(self, value):
+ return str(value)
+
# Regular parameter description.
class ParamDesc(object):
def __init__(self, ptype_str, ptype, *args, **kwargs):
self.default = args[0]
self.desc = args[1]
else:
- raise TypeError, 'too many arguments'
+ raise TypeError('too many arguments')
- if kwargs.has_key('desc'):
+ if 'desc' in kwargs:
assert(not hasattr(self, 'desc'))
self.desc = kwargs['desc']
del kwargs['desc']
- if kwargs.has_key('default'):
+ if 'default' in kwargs:
assert(not hasattr(self, 'default'))
self.default = kwargs['default']
del kwargs['default']
if kwargs:
- raise TypeError, 'extra unknown kwargs %s' % kwargs
+ raise TypeError('extra unknown kwargs %s' % kwargs)
if not hasattr(self, 'desc'):
- raise TypeError, 'desc attribute missing'
+ raise TypeError('desc attribute missing')
def __getattr__(self, attr):
if attr == 'ptype':
- try:
- ptype = eval(self.ptype_str, objects.__dict__)
- 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 AttributeError, "'%s' object has no attribute '%s'" % \
- (type(self).__name__, attr)
+ from . import SimObject
+ ptype = SimObject.allClasses[self.ptype_str]
+ assert isSimObjectClass(ptype)
+ self.ptype = ptype
+ return ptype
+
+ raise AttributeError("'%s' object has no attribute '%s'" % \
+ (type(self).__name__, attr))
+
+ def example_str(self):
+ if hasattr(self.ptype, "ex_str"):
+ return self.ptype.ex_str
+ else:
+ return self.ptype_str
+
+ # Is the param available to be exposed on the command line
+ def isCmdLineSettable(self):
+ if hasattr(self.ptype, "cmd_line_settable"):
+ return self.ptype.cmd_line_settable
+ else:
+ return False
def convert(self, value):
if isinstance(value, proxy.BaseProxy):
value.set_param_desc(self)
return value
- if not hasattr(self, 'ptype') and isNullPointer(value):
+ if 'ptype' not in self.__dict__ and isNullPointer(value):
# deferred evaluation of SimObject; continue to defer if
# we're just assigning a null pointer
return value
return value
return self.ptype(value)
- def cxx_predecls(self):
- return self.ptype.cxx_predecls
+ def pretty_print(self, value):
+ if isinstance(value, proxy.BaseProxy):
+ return str(value)
+ if isNullPointer(value):
+ return NULL
+ return self.ptype(value).pretty_print(value)
- def swig_predecls(self):
- return self.ptype.swig_predecls
+ def cxx_predecls(self, code):
+ code('#include <cstddef>')
+ self.ptype.cxx_predecls(code)
- def cxx_decl(self):
- return '%s %s;' % (self.ptype.cxx_type, self.name)
+ def pybind_predecls(self, code):
+ self.ptype.pybind_predecls(code)
+
+ def cxx_decl(self, code):
+ code('${{self.ptype.cxx_type}} ${{self.name}};')
# Vector-valued parameter description. Just like ParamDesc, except
# that the value is a vector (list) of the specified type instead of a
# single value.
class VectorParamValue(list):
+ __metaclass__ = MetaParamValue
+ def __setattr__(self, attr, value):
+ raise AttributeError("Not allowed to set %s on '%s'" % \
+ (attr, type(self).__name__))
+
+ def config_value(self):
+ return [v.config_value() for v in self]
+
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]
+ if len(self) == 1 and isinstance(self[0], proxy.BaseProxy):
+ # The value is a proxy (e.g. Parent.any, Parent.all or
+ # Parent.x) therefore try resolve it
+ return self[0].unproxy(base)
+ else:
+ return [v.unproxy(base) for v in self]
+
+class SimObjectVector(VectorParamValue):
+ # support clone operation
+ def __call__(self, **kwargs):
+ return SimObjectVector([v(**kwargs) for v in self])
-class SimObjVector(VectorParamValue):
- def print_ini(self):
+ def clear_parent(self, old_parent):
for v in self:
- v.print_ini()
+ v.clear_parent(old_parent)
+
+ def set_parent(self, parent, name):
+ if len(self) == 1:
+ self[0].set_parent(parent, name)
+ else:
+ width = int(math.ceil(math.log(len(self))/math.log(10)))
+ for i,v in enumerate(self):
+ v.set_parent(parent, "%s%0*d" % (name, width, i))
+
+ def has_parent(self):
+ return any([e.has_parent() for e in self if not isNullPointer(e)])
+
+ # return 'cpu0 cpu1' etc. for print_ini()
+ def get_name(self):
+ return ' '.join([v._name for v in self])
+
+ # By iterating through the constituent members of the vector here
+ # we can nicely handle iterating over all a SimObject's children
+ # without having to provide lots of special functions on
+ # SimObjectVector directly.
+ def descendants(self):
+ for v in self:
+ for obj in v.descendants():
+ yield obj
+
+ def get_config_as_dict(self):
+ a = []
+ for v in self:
+ a.append(v.get_config_as_dict())
+ return a
+
+ # If we are replacing an item in the vector, make sure to set the
+ # parent reference of the new SimObject to be the same as the parent
+ # of the SimObject being replaced. Useful to have if we created
+ # a SimObjectVector of temporary objects that will be modified later in
+ # configuration scripts.
+ def __setitem__(self, key, value):
+ val = self[key]
+ if value.has_parent():
+ warn("SimObject %s already has a parent" % value.get_name() +\
+ " that is being overwritten by a SimObjectVector")
+ value.set_parent(val.get_parent(), val._name)
+ super(SimObjectVector, self).__setitem__(key, value)
+
+ # Enumerate the params of each member of the SimObject vector. Creates
+ # strings that will allow indexing into the vector by the python code and
+ # allow it to be specified on the command line.
+ def enumerateParams(self, flags_dict = {},
+ cmd_line_str = "",
+ access_str = ""):
+ if hasattr(self, "_paramEnumed"):
+ print("Cycle detected enumerating params at %s?!" % (cmd_line_str))
+ else:
+ x = 0
+ for vals in self:
+ # Each entry in the SimObjectVector should be an
+ # instance of a SimObject
+ flags_dict = vals.enumerateParams(flags_dict,
+ cmd_line_str + "%d." % x,
+ access_str + "[%d]." % x)
+ x = x + 1
+
+ return flags_dict
class VectorParamDesc(ParamDesc):
# Convert assigned value to appropriate type. If the RHS is not a
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)
+ elif isinstance(value, str):
+ # If input is a csv string
+ tmp_list = [ ParamDesc.convert(self, v) \
+ for v in value.strip('[').strip(']').split(',') ]
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 <vector>'] + self.ptype.cxx_predecls
+ if isSimObjectSequence(tmp_list):
+ return SimObjectVector(tmp_list)
+ else:
+ return VectorParamValue(tmp_list)
+
+ # Produce a human readable example string that describes
+ # how to set this vector parameter in the absence of a default
+ # value.
+ def example_str(self):
+ s = super(VectorParamDesc, self).example_str()
+ help_str = "[" + s + "," + s + ", ...]"
+ return help_str
+
+ # Produce a human readable representation of the value of this vector param.
+ def pretty_print(self, value):
+ if isinstance(value, (list, tuple)):
+ tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ]
+ elif isinstance(value, str):
+ tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ]
+ else:
+ tmp_list = [ ParamDesc.pretty_print(self, value) ]
- def swig_predecls(self):
- return ['%include "std_vector.i"'] + self.ptype.swig_predecls
+ return tmp_list
- def cxx_decl(self):
- return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
+ # This is a helper function for the new config system
+ def __call__(self, value):
+ if isinstance(value, (list, tuple)):
+ # list: coerce each element into new list
+ tmp_list = [ ParamDesc.convert(self, v) for v in value ]
+ elif isinstance(value, str):
+ # If input is a csv string
+ tmp_list = [ ParamDesc.convert(self, v) \
+ for v in value.strip('[').strip(']').split(',') ]
+ else:
+ # singleton: coerce to a single-element list
+ tmp_list = [ ParamDesc.convert(self, value) ]
+
+ return VectorParamValue(tmp_list)
+
+ def cxx_predecls(self, code):
+ code('#include <vector>')
+ self.ptype.cxx_predecls(code)
+
+ def pybind_predecls(self, code):
+ code('#include <vector>')
+ self.ptype.pybind_predecls(code)
+
+ def cxx_decl(self, code):
+ code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
class ParamFactory(object):
def __init__(self, param_desc_class, ptype_str = None):
# E.g., Param.Int(5, "number of widgets")
def __call__(self, *args, **kwargs):
- caller_frame = inspect.currentframe().f_back
ptype = None
try:
- ptype = eval(self.ptype_str,
- caller_frame.f_globals, caller_frame.f_locals)
- if not isinstance(ptype, type):
- raise TypeError, \
- "Param qualifier is not a type: %s" % ptype
- except NameError:
+ ptype = allParams[self.ptype_str]
+ except KeyError:
# if name isn't defined yet, assume it's a SimObject, and
# try to resolve it later
pass
# built-in str class.
class String(ParamValue,str):
cxx_type = 'std::string'
- cxx_predecls = ['#include <string>']
- swig_predecls = ['%include "std_string.i"\n' +
- '%apply const std::string& {std::string *};']
- pass
+ cmd_line_settable = True
+
+ @classmethod
+ def cxx_predecls(self, code):
+ code('#include <string>')
+
+ def __call__(self, value):
+ self = value
+ return value
+
+ @classmethod
+ def cxx_ini_parse(self, code, src, dest, ret):
+ code('%s = %s;' % (dest, src))
+ code('%s true;' % ret)
+
+ 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
# a new Latency object.
class NumericParamValue(ParamValue):
+ @staticmethod
+ def unwrap(v):
+ return v.value if isinstance(v, NumericParamValue) else v
+
def __str__(self):
return str(self.value)
def __float__(self):
return float(self.value)
+ def __long__(self):
+ return long(self.value)
+
+ def __int__(self):
+ return int(self.value)
+
# hook for bounds checking
def _check(self):
return
def __mul__(self, other):
newobj = self.__class__(self)
- newobj.value *= other
+ newobj.value *= NumericParamValue.unwrap(other)
newobj._check()
return newobj
__rmul__ = __mul__
- def __div__(self, other):
+ def __truediv__(self, other):
newobj = self.__class__(self)
- newobj.value /= other
+ newobj.value /= NumericParamValue.unwrap(other)
+ newobj._check()
+ return newobj
+
+ def __floordiv__(self, other):
+ newobj = self.__class__(self)
+ newobj.value //= NumericParamValue.unwrap(other)
+ newobj._check()
+ return newobj
+
+
+ def __add__(self, other):
+ newobj = self.__class__(self)
+ newobj.value += NumericParamValue.unwrap(other)
newobj._check()
return newobj
def __sub__(self, other):
newobj = self.__class__(self)
- newobj.value -= other
+ newobj.value -= NumericParamValue.unwrap(other)
newobj._check()
return newobj
+ def __iadd__(self, other):
+ self.value += NumericParamValue.unwrap(other)
+ self._check()
+ return self
+
+ def __isub__(self, other):
+ self.value -= NumericParamValue.unwrap(other)
+ self._check()
+ return self
+
+ def __imul__(self, other):
+ self.value *= NumericParamValue.unwrap(other)
+ self._check()
+ return self
+
+ def __itruediv__(self, other):
+ self.value /= NumericParamValue.unwrap(other)
+ self._check()
+ return self
+
+ def __ifloordiv__(self, other):
+ self.value //= NumericParamValue.unwrap(other)
+ self._check()
+ return self
+
+ def __lt__(self, other):
+ return self.value < NumericParamValue.unwrap(other)
+
+ # Python 2.7 pre __future__.division operators
+ # TODO: Remove these when after "import division from __future__"
+ __div__ = __truediv__
+ __idiv__ = __itruediv__
+
+ def config_value(self):
+ return self.value
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ # Assume that base/str.hh will be included anyway
+ # code('#include "base/str.hh"')
+ pass
+
+ # The default for parsing PODs from an .ini entry is to extract from an
+ # istringstream and let overloading choose the right type according to
+ # the dest type.
+ @classmethod
+ def cxx_ini_parse(self, code, src, dest, ret):
+ code('%s to_number(%s, %s);' % (ret, src, dest))
+
# Metaclass for bounds-checked integer parameters. See CheckedInt.
-class CheckedIntType(type):
+class CheckedIntType(MetaParamValue):
def __init__(cls, name, bases, dict):
super(CheckedIntType, cls).__init__(name, bases, dict)
if name == 'CheckedInt':
return
- if not cls.cxx_predecls:
- # most derived types require this, so we just do it here once
- cls.cxx_predecls = ['#include "sim/host.hh"']
-
- 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' +
- '%import "sim/host.hh"']
-
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);
+ " 'min' and 'max' or 'size' and 'unsigned'\n",
+ name);
if cls.unsigned:
cls.min = 0
cls.max = 2 ** cls.size - 1
# metaclass CheckedIntType.__init__.
class CheckedInt(NumericParamValue):
__metaclass__ = CheckedIntType
+ cmd_line_settable = True
def _check(self):
if not self.min <= self.value <= self.max:
- raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
- (self.min, self.value, self.max)
+ raise TypeError('Integer param out of bounds %d < %d < %d' % \
+ (self.min, self.value, self.max))
def __init__(self, value):
if isinstance(value, str):
self.value = convert.toInteger(value)
- elif isinstance(value, (int, long, float)):
+ elif isinstance(value, (int, long, float, NumericParamValue)):
self.value = long(value)
+ else:
+ raise TypeError("Can't convert object of type %s to CheckedInt" \
+ % type(value).__name__)
self._check()
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def __index__(self):
+ return int(self.value)
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ # most derived types require this, so we just do it here once
+ code('#include "base/types.hh"')
+
+ 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
class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
+class Cycles(CheckedInt):
+ cxx_type = 'Cycles'
+ size = 64
+ unsigned = True
+
+ def getValue(self):
+ from _m5.core import Cycles
+ return Cycles(self.value)
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ # Assume that base/str.hh will be included anyway
+ # code('#include "base/str.hh"')
+ pass
+
+ @classmethod
+ def cxx_ini_parse(cls, code, src, dest, ret):
+ code('uint64_t _temp;')
+ code('bool _ret = to_number(%s, _temp);' % src)
+ code('if (_ret)')
+ code(' %s = Cycles(_temp);' % dest)
+ code('%s _ret;' % ret)
+
class Float(ParamValue, float):
- pass
+ cxx_type = 'double'
+ cmd_line_settable = True
+
+ def __init__(self, value):
+ if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
+ self.value = float(value)
+ else:
+ raise TypeError("Can't convert object of type %s to Float" \
+ % type(value).__name__)
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def getValue(self):
+ return float(self.value)
+
+ def config_value(self):
+ return self
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ code('#include <sstream>')
+
+ @classmethod
+ def cxx_ini_parse(self, code, src, dest, ret):
+ code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
class MemorySize(CheckedInt):
cxx_type = 'uint64_t'
+ ex_str = '512MB'
size = 64
unsigned = True
def __init__(self, value):
self._check()
class MemorySize32(CheckedInt):
+ cxx_type = 'uint32_t'
+ ex_str = '512MB'
size = 32
unsigned = True
def __init__(self, value):
class Addr(CheckedInt):
cxx_type = 'Addr'
- cxx_predecls = ['#include "targetarch/isa_traits.hh"']
size = 64
unsigned = True
def __init__(self, value):
self.value = value.value
else:
try:
+ # Often addresses are referred to with sizes. Ex: A device
+ # base address is at "512MB". Use toMemorySize() to convert
+ # these into addresses. If the address is not specified with a
+ # "size", an exception will occur and numeric translation will
+ # proceed below.
self.value = convert.toMemorySize(value)
- except TypeError:
- self.value = long(value)
- self._check()
+ except (TypeError, ValueError):
+ # Convert number to string and use long() to do automatic
+ # base conversion (requires base=0 for auto-conversion)
+ self.value = long(str(value), base=0)
+ self._check()
+ def __add__(self, other):
+ if isinstance(other, Addr):
+ return self.value + other.value
+ else:
+ return self.value + other
+ def pretty_print(self, value):
+ try:
+ val = convert.toMemorySize(value)
+ except TypeError:
+ val = long(value)
+ return "0x%x" % long(val)
-class MetaRange(type):
- def __init__(cls, name, bases, dict):
- super(MetaRange, cls).__init__(name, bases, dict)
- if name == 'Range':
- return
- cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
- cls.cxx_predecls = \
- ['#include "base/range.hh"'] + cls.type.cxx_predecls
+class AddrRange(ParamValue):
+ cxx_type = 'AddrRange'
-class Range(ParamValue):
- __metaclass__ = MetaRange
- type = Int # default; can be overridden in subclasses
def __init__(self, *args, **kwargs):
+ # Disable interleaving and hashing by default
+ self.intlvBits = 0
+ self.intlvMatch = 0
+ self.masks = []
+
def handle_kwargs(self, kwargs):
+ # An address range needs to have an upper limit, specified
+ # either explicitly with an end, or as an offset using the
+ # size keyword.
if 'end' in kwargs:
- self.second = self.type(kwargs.pop('end'))
+ self.end = Addr(kwargs.pop('end'))
elif 'size' in kwargs:
- self.second = self.first + self.type(kwargs.pop('size')) - 1
+ self.end = self.start + Addr(kwargs.pop('size')) - 1
else:
- raise TypeError, "Either end or size must be specified"
+ raise TypeError("Either end or size must be specified")
+
+ # Now on to the optional bit
+ if 'intlvMatch' in kwargs:
+ self.intlvMatch = int(kwargs.pop('intlvMatch'))
+
+ if 'masks' in kwargs:
+ self.masks = [ long(x) for x in list(kwargs.pop('masks')) ]
+ self.intlvBits = len(self.masks)
+ else:
+ if 'intlvBits' in kwargs:
+ self.intlvBits = int(kwargs.pop('intlvBits'))
+ self.masks = [0] * self.intlvBits
+ if 'intlvHighBit' not in kwargs:
+ raise TypeError("No interleave bits specified")
+ intlv_high_bit = int(kwargs.pop('intlvHighBit'))
+ xor_high_bit = 0
+ if 'xorHighBit' in kwargs:
+ xor_high_bit = int(kwargs.pop('xorHighBit'))
+ for i in range(0, self.intlvBits):
+ bit1 = intlv_high_bit - i
+ mask = 1 << bit1
+ if xor_high_bit != 0:
+ bit2 = xor_high_bit - i
+ mask |= 1 << bit2
+ self.masks[self.intlvBits - i - 1] = mask
if len(args) == 0:
- self.first = self.type(kwargs.pop('start'))
+ self.start = Addr(kwargs.pop('start'))
handle_kwargs(self, kwargs)
elif len(args) == 1:
if kwargs:
- self.first = self.type(args[0])
+ self.start = Addr(args[0])
handle_kwargs(self, kwargs)
- elif isinstance(args[0], Range):
- self.first = self.type(args[0].first)
- self.second = self.type(args[0].second)
+ elif isinstance(args[0], (list, tuple)):
+ self.start = Addr(args[0][0])
+ self.end = Addr(args[0][1])
else:
- self.first = self.type(0)
- self.second = self.type(args[0]) - 1
+ self.start = Addr(0)
+ self.end = Addr(args[0]) - 1
elif len(args) == 2:
- self.first = self.type(args[0])
- self.second = self.type(args[1])
+ self.start = Addr(args[0])
+ self.end = Addr(args[1])
else:
- raise TypeError, "Too many arguments specified"
+ raise TypeError("Too many arguments specified")
if kwargs:
- raise TypeError, "too many keywords: %s" % kwargs.keys()
+ raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
def __str__(self):
- return '%s:%s' % (self.first, self.second)
-
-class AddrRange(Range):
- type = Addr
-
-class TickRange(Range):
- type = Tick
+ if len(self.masks) == 0:
+ return '%s:%s' % (self.start, self.end)
+ else:
+ return '%s:%s:%s:%s' % (self.start, self.end, self.intlvMatch,
+ ':'.join(str(m) for m in self.masks))
+
+ def size(self):
+ # Divide the size by the size of the interleaving slice
+ return (long(self.end) - long(self.start) + 1) >> self.intlvBits
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ Addr.cxx_predecls(code)
+ code('#include "base/addr_range.hh"')
+
+ @classmethod
+ def pybind_predecls(cls, code):
+ Addr.pybind_predecls(code)
+ code('#include "base/addr_range.hh"')
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ code('#include <sstream>')
+ code('#include <vector>')
+ code('#include "base/types.hh"')
+
+ @classmethod
+ def cxx_ini_parse(cls, code, src, dest, ret):
+ code('bool _ret = true;')
+ code('uint64_t _start, _end, _intlvMatch = 0;')
+ code('std::vector<Addr> _masks;')
+ code('char _sep;')
+ code('std::istringstream _stream(${src});')
+ code('_stream >> _start;')
+ code('_stream.get(_sep);')
+ code('_ret = _sep == \':\';')
+ code('_stream >> _end;')
+ code('if (!_stream.fail() && !_stream.eof()) {')
+ code(' _stream.get(_sep);')
+ code(' _ret = ret && _sep == \':\';')
+ code(' _stream >> _intlvMatch;')
+ code(' while (!_stream.fail() && !_stream.eof()) {')
+ code(' _stream.get(_sep);')
+ code(' _ret = ret && _sep == \':\';')
+ code(' Addr mask;')
+ code(' _stream >> mask;')
+ code(' _masks.push_back(mask);')
+ code(' }')
+ code('}')
+ code('_ret = _ret && !_stream.fail() && _stream.eof();')
+ code('if (_ret)')
+ code(' ${dest} = AddrRange(_start, _end, _masks, _intlvMatch);')
+ code('${ret} _ret;')
+
+ def getValue(self):
+ # Go from the Python class to the wrapped C++ class
+ from _m5.range import AddrRange
+
+ return AddrRange(long(self.start), long(self.end),
+ self.masks, int(self.intlvMatch))
# Boolean parameter type. Python doesn't let you subclass bool, since
# it doesn't want to let you create multiple instances of True and
# False. Thus this is a little more complicated than String.
class Bool(ParamValue):
cxx_type = 'bool'
+ cmd_line_settable = True
+
def __init__(self, value):
try:
self.value = convert.toBool(value)
except TypeError:
self.value = bool(value)
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def getValue(self):
+ return bool(self.value)
+
def __str__(self):
return str(self.value)
+ # implement truth value testing for Bool parameters so that these params
+ # evaluate correctly during the python configuration phase
+ def __bool__(self):
+ return bool(self.value)
+
+ # Python 2.7 uses __nonzero__ instead of __bool__
+ __nonzero__ = __bool__
+
def ini_str(self):
if self.value:
return 'true'
return 'false'
+ def config_value(self):
+ return self.value
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ # Assume that base/str.hh will be included anyway
+ # code('#include "base/str.hh"')
+ pass
+
+ @classmethod
+ def cxx_ini_parse(cls, code, src, dest, ret):
+ code('%s to_bool(%s, %s);' % (ret, src, dest))
+
def IncEthernetAddr(addr, val = 1):
- bytes = map(lambda x: int(x, 16), addr.split(':'))
+ bytes = [ int(x, 16) for x in addr.split(':') ]
bytes[5] += val
for i in (5, 4, 3, 2, 1):
val,rem = divmod(bytes[i], 256)
assert(bytes[0] <= 255)
return ':'.join(map(lambda x: '%02x' % x, bytes))
-class NextEthernetAddr(object):
- addr = "00:90:00:00:00:01"
+_NextEthernetAddr = "00:90:00:00:00:01"
+def NextEthernetAddr():
+ global _NextEthernetAddr
- def __init__(self, inc = 1):
- self.value = NextEthernetAddr.addr
- NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
+ value = _NextEthernetAddr
+ _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
+ return value
class EthernetAddr(ParamValue):
cxx_type = 'Net::EthAddr'
- cxx_predecls = ['#include "base/inet.hh"']
- swig_predecls = ['class Net::EthAddr;']
+ ex_str = "00:90:00:00:00:01"
+ cmd_line_settable = True
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ code('#include "base/inet.hh"')
+
def __init__(self, value):
if value == NextEthernetAddr:
self.value = value
return
if not isinstance(value, str):
- raise TypeError, "expected an ethernet address and didn't get one"
+ raise TypeError("expected an ethernet address and didn't get one")
bytes = value.split(':')
if len(bytes) != 6:
- raise TypeError, 'invalid ethernet address %s' % value
+ raise TypeError('invalid ethernet address %s' % value)
for byte in bytes:
- if not 0 <= int(byte) <= 256:
- raise TypeError, 'invalid ethernet address %s' % value
+ if not 0 <= int(byte, base=16) <= 0xff:
+ raise TypeError('invalid ethernet address %s' % value)
self.value = value
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
def unproxy(self, base):
if self.value == NextEthernetAddr:
- self.addr = self.value().value
+ return EthernetAddr(self.value())
return self
+ def getValue(self):
+ from _m5.net import EthAddr
+ return EthAddr(self.value)
+
def __str__(self):
- if self.value == NextEthernetAddr:
- if hasattr(self, 'addr'):
- return self.addr
+ return self.value
+
+ def ini_str(self):
+ return self.value
+
+ @classmethod
+ def cxx_ini_parse(self, code, src, dest, ret):
+ code('%s = Net::EthAddr(%s);' % (dest, src))
+ code('%s true;' % ret)
+
+# When initializing an IpAddress, pass in an existing IpAddress, a string of
+# the form "a.b.c.d", or an integer representing an IP.
+class IpAddress(ParamValue):
+ cxx_type = 'Net::IpAddress'
+ ex_str = "127.0.0.1"
+ cmd_line_settable = True
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ code('#include "base/inet.hh"')
+
+ def __init__(self, value):
+ if isinstance(value, IpAddress):
+ self.ip = value.ip
+ else:
+ try:
+ self.ip = convert.toIpAddress(value)
+ except TypeError:
+ self.ip = long(value)
+ self.verifyIp()
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def __str__(self):
+ tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)]
+ return '%d.%d.%d.%d' % tuple(tup)
+
+ def __eq__(self, other):
+ if isinstance(other, IpAddress):
+ return self.ip == other.ip
+ elif isinstance(other, str):
+ try:
+ return self.ip == convert.toIpAddress(other)
+ except:
+ return False
+ else:
+ return self.ip == other
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def verifyIp(self):
+ if self.ip < 0 or self.ip >= (1 << 32):
+ raise TypeError("invalid ip address %#08x" % self.ip)
+
+ def getValue(self):
+ from _m5.net import IpAddress
+ return IpAddress(self.ip)
+
+# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
+# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
+# positional or keyword arguments.
+class IpNetmask(IpAddress):
+ cxx_type = 'Net::IpNetmask'
+ ex_str = "127.0.0.0/24"
+ cmd_line_settable = True
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ code('#include "base/inet.hh"')
+
+ def __init__(self, *args, **kwargs):
+ def handle_kwarg(self, kwargs, key, elseVal = None):
+ if key in kwargs:
+ setattr(self, key, kwargs.pop(key))
+ elif elseVal:
+ setattr(self, key, elseVal)
+ else:
+ raise TypeError("No value set for %s" % key)
+
+ if len(args) == 0:
+ handle_kwarg(self, kwargs, 'ip')
+ handle_kwarg(self, kwargs, 'netmask')
+
+ elif len(args) == 1:
+ if kwargs:
+ if not 'ip' in kwargs and not 'netmask' in kwargs:
+ raise TypeError("Invalid arguments")
+ handle_kwarg(self, kwargs, 'ip', args[0])
+ handle_kwarg(self, kwargs, 'netmask', args[0])
+ elif isinstance(args[0], IpNetmask):
+ self.ip = args[0].ip
+ self.netmask = args[0].netmask
else:
- return "NextEthernetAddr (unresolved)"
+ (self.ip, self.netmask) = convert.toIpNetmask(args[0])
+
+ elif len(args) == 2:
+ self.ip = args[0]
+ self.netmask = args[1]
else:
- return self.value
+ raise TypeError("Too many arguments specified")
+
+ if kwargs:
+ raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
+
+ self.verify()
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def __str__(self):
+ return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
+
+ def __eq__(self, other):
+ if isinstance(other, IpNetmask):
+ return self.ip == other.ip and self.netmask == other.netmask
+ elif isinstance(other, str):
+ try:
+ return (self.ip, self.netmask) == convert.toIpNetmask(other)
+ except:
+ return False
+ else:
+ return False
+
+ def verify(self):
+ self.verifyIp()
+ if self.netmask < 0 or self.netmask > 32:
+ raise TypeError("invalid netmask %d" % netmask)
+
+ def getValue(self):
+ from _m5.net import IpNetmask
+ return IpNetmask(self.ip, self.netmask)
+
+# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
+# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
+class IpWithPort(IpAddress):
+ cxx_type = 'Net::IpWithPort'
+ ex_str = "127.0.0.1:80"
+ cmd_line_settable = True
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ code('#include "base/inet.hh"')
+
+ def __init__(self, *args, **kwargs):
+ def handle_kwarg(self, kwargs, key, elseVal = None):
+ if key in kwargs:
+ setattr(self, key, kwargs.pop(key))
+ elif elseVal:
+ setattr(self, key, elseVal)
+ else:
+ raise TypeError("No value set for %s" % key)
+
+ if len(args) == 0:
+ handle_kwarg(self, kwargs, 'ip')
+ handle_kwarg(self, kwargs, 'port')
+
+ elif len(args) == 1:
+ if kwargs:
+ if not 'ip' in kwargs and not 'port' in kwargs:
+ raise TypeError("Invalid arguments")
+ handle_kwarg(self, kwargs, 'ip', args[0])
+ handle_kwarg(self, kwargs, 'port', args[0])
+ elif isinstance(args[0], IpWithPort):
+ self.ip = args[0].ip
+ self.port = args[0].port
+ else:
+ (self.ip, self.port) = convert.toIpWithPort(args[0])
+
+ elif len(args) == 2:
+ self.ip = args[0]
+ self.port = args[1]
+ else:
+ raise TypeError("Too many arguments specified")
+
+ if kwargs:
+ raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
+
+ self.verify()
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def __str__(self):
+ return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
+
+ def __eq__(self, other):
+ if isinstance(other, IpWithPort):
+ return self.ip == other.ip and self.port == other.port
+ elif isinstance(other, str):
+ try:
+ return (self.ip, self.port) == convert.toIpWithPort(other)
+ except:
+ return False
+ else:
+ return False
+
+ def verify(self):
+ self.verifyIp()
+ if self.port < 0 or self.port > 0xffff:
+ raise TypeError("invalid port %d" % self.port)
+
+ def getValue(self):
+ from _m5.net import IpWithPort
+ return IpWithPort(self.ip, self.port)
+
+time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
+ "%a %b %d %H:%M:%S %Y",
+ "%Y/%m/%d %H:%M:%S",
+ "%Y/%m/%d %H:%M",
+ "%Y/%m/%d",
+ "%m/%d/%Y %H:%M:%S",
+ "%m/%d/%Y %H:%M",
+ "%m/%d/%Y",
+ "%m/%d/%y %H:%M:%S",
+ "%m/%d/%y %H:%M",
+ "%m/%d/%y"]
+
+
+def parse_time(value):
+ from time import gmtime, strptime, struct_time, time
+ from datetime import datetime, date
+
+ if isinstance(value, struct_time):
+ return value
+
+ if isinstance(value, (int, long)):
+ return gmtime(value)
+
+ if isinstance(value, (datetime, date)):
+ return value.timetuple()
+
+ if isinstance(value, str):
+ if value in ('Now', 'Today'):
+ return time.gmtime(time.time())
+
+ for format in time_formats:
+ try:
+ return strptime(value, format)
+ except ValueError:
+ pass
+
+ raise ValueError("Could not parse '%s' as a time" % value)
+
+class Time(ParamValue):
+ cxx_type = 'tm'
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ code('#include <time.h>')
+
+ def __init__(self, value):
+ self.value = parse_time(value)
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def getValue(self):
+ from _m5.core import tm
+ import calendar
+
+ return tm.gmtime(calendar.timegm(self.value))
+
+ def __str__(self):
+ return time.asctime(self.value)
+
+ def ini_str(self):
+ return str(self)
+
+ def get_config_as_dict(self):
+ assert false
+ return str(self)
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ code('#include <time.h>')
+
+ @classmethod
+ def cxx_ini_parse(cls, code, src, dest, ret):
+ code('char *_parse_ret = strptime((${src}).c_str(),')
+ code(' "%a %b %d %H:%M:%S %Y", &(${dest}));')
+ code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
# Enumerated types are a little more complex. The user specifies the
# type as Enum(foo) where foo is either a list or dictionary of
# 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):
+class MetaEnum(MetaParamValue):
+ 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 'map' in init_dict:
if not isinstance(cls.map, dict):
- raise TypeError, "Enum-derived class attribute 'map' " \
- "must be of type dict"
+ raise TypeError("Enum-derived class attribute 'map' " \
+ "must be of type dict")
# build list of value strings from map
- cls.vals = cls.map.keys()
+ cls.vals = list(cls.map.keys())
cls.vals.sort()
- elif init_dict.has_key('vals'):
+ elif 'vals' in init_dict:
if not isinstance(cls.vals, list):
- raise TypeError, "Enum-derived class attribute 'vals' " \
- "must be of type list"
+ raise TypeError("Enum-derived class attribute 'vals' " \
+ "must be of type list")
# build string->value map from vals sequence
cls.map = {}
for idx,val in enumerate(cls.vals):
cls.map[val] = idx
else:
- raise TypeError, "Enum-derived class must define "\
- "attribute 'map' or 'vals'"
+ raise TypeError("Enum-derived class must define "\
+ "attribute 'map' or 'vals'")
- cls.cxx_type = name + '::Enum'
+ if cls.is_class:
+ cls.cxx_type = '%s' % name
+ else:
+ cls.cxx_type = 'Enums::%s' % name
super(MetaEnum, cls).__init__(name, bases, init_dict)
# 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
+ def cxx_decl(cls, code):
+ wrapper_name = cls.wrapper_name
+ wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
+ name = cls.__name__ if cls.enum_name is None else cls.enum_name
+ idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
+
+ code('''\
+#ifndef $idem_macro
+#define $idem_macro
+
+''')
+ if cls.is_class:
+ code('''\
+enum class $name {
+''')
+ else:
+ code('''\
+$wrapper $wrapper_name {
+ enum $name {
+''')
+ code.indent(1)
+ code.indent(1)
+ for val in cls.vals:
+ code('$val = ${{cls.map[val]}},')
+ code('Num_$name = ${{len(cls.vals)}}')
+ code.dedent(1)
+ code('};')
+
+ if cls.is_class:
+ code('''\
+extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
+''')
+ elif cls.wrapper_is_struct:
+ code('static const char *${name}Strings[Num_${name}];')
+ else:
+ code('extern const char *${name}Strings[Num_${name}];')
+
+ if not cls.is_class:
+ code.dedent(1)
+ code('};')
+
+ code()
+ code('#endif // $idem_macro')
+
+ def cxx_def(cls, code):
+ wrapper_name = cls.wrapper_name
+ file_name = cls.__name__
+ name = cls.__name__ if cls.enum_name is None else cls.enum_name
+
+ code('#include "enums/$file_name.hh"')
+ if cls.wrapper_is_struct:
+ code('const char *${wrapper_name}::${name}Strings'
+ '[Num_${name}] =')
+ else:
+ if cls.is_class:
+ code('''\
+const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
+''')
+ else:
+ code('namespace Enums {')
+ code.indent(1)
+ code('const char *${name}Strings[Num_${name}] =')
+
+ code('{')
+ code.indent(1)
+ for val in cls.vals:
+ code('"$val",')
+ code.dedent(1)
+ code('};')
+
+ if not cls.wrapper_is_struct and not cls.is_class:
+ code.dedent(1)
+ code('} // namespace $wrapper_name')
+
+
+ def pybind_def(cls, code):
+ name = cls.__name__
+ enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
+ wrapper_name = enum_name if cls.is_class else cls.wrapper_name
+
+ code('''#include "pybind11/pybind11.h"
+#include "pybind11/stl.h"
+
+#include <sim/init.hh>
+
+namespace py = pybind11;
+
+static void
+module_init(py::module &m_internal)
+{
+ py::module m = m_internal.def_submodule("enum_${name}");
+
+''')
+ if cls.is_class:
+ code('py::enum_<${enum_name}>(m, "enum_${name}")')
+ else:
+ code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
+
+ code.indent()
+ code.indent()
+ for val in cls.vals:
+ code('.value("${val}", ${wrapper_name}::${val})')
+ code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
+ code('.export_values()')
+ code(';')
+ code.dedent()
+
+ code('}')
+ code.dedent()
+ code()
+ code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
+
# Base class for enum types.
class Enum(ParamValue):
__metaclass__ = MetaEnum
vals = []
+ cmd_line_settable = True
+
+ # The name of the wrapping namespace or struct
+ wrapper_name = 'Enums'
+
+ # If true, the enum is wrapped in a struct rather than a namespace
+ wrapper_is_struct = False
+
+ is_class = False
+
+ # If not None, use this as the enum name rather than this class name
+ enum_name = None
def __init__(self, value):
if value not in self.map:
- raise TypeError, "Enum param got bad value '%s' (not in %s)" \
- % (value, self.vals)
+ raise TypeError("Enum param got bad value '%s' (not in %s)" \
+ % (value, self.vals))
self.value = value
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ @classmethod
+ def cxx_predecls(cls, code):
+ code('#include "enums/$0.hh"', cls.__name__)
+
+ @classmethod
+ def cxx_ini_parse(cls, code, src, dest, ret):
+ code('if (false) {')
+ for elem_name in cls.map.keys():
+ code('} else if (%s == "%s") {' % (src, elem_name))
+ code.indent()
+ name = cls.__name__ if cls.enum_name is None else cls.enum_name
+ code('%s = %s::%s;' % (dest, name if cls.is_class else 'Enums',
+ elem_name))
+ code('%s true;' % ret)
+ code.dedent()
+ code('} else {')
+ code(' %s false;' % ret)
+ code('}')
+
+ def getValue(self):
+ import m5.internal.params
+ e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
+ return e(self.map[self.value])
+
def __str__(self):
return self.value
-ticks_per_sec = None
+# This param will generate a scoped c++ enum and its python bindings.
+class ScopedEnum(Enum):
+ __metaclass__ = MetaEnum
+ vals = []
+ cmd_line_settable = True
+
+ # The name of the wrapping namespace or struct
+ wrapper_name = None
+
+ # If true, the enum is wrapped in a struct rather than a namespace
+ wrapper_is_struct = False
+
+ # If true, the generated enum is a scoped enum
+ is_class = True
+
+ # If not None, use this as the enum name rather than this class name
+ enum_name = None
# how big does a rounding error need to be before we warn about it?
frequency_tolerance = 0.001 # 0.1%
-# convert a floting-point # of ticks to integer, and warn if rounding
-# discards too much precision
-def tick_check(float_ticks):
- if float_ticks == 0:
- return 0
- int_ticks = int(round(float_ticks))
- err = (float_ticks - int_ticks) / float_ticks
- if err > frequency_tolerance:
- print >> sys.stderr, "Warning: rounding error > tolerance"
- print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
- #raise ValueError
- return int_ticks
-
-def getLatency(value):
- if isinstance(value, Latency) or isinstance(value, Clock):
- return value.value
- elif isinstance(value, Frequency) or isinstance(value, RootClock):
- return 1 / value.value
- elif isinstance(value, str):
- try:
- return convert.toLatency(value)
- except ValueError:
- try:
- return 1 / convert.toFrequency(value)
- except ValueError:
- pass # fall through
- raise ValueError, "Invalid Frequency/Latency value '%s'" % value
+class TickParamValue(NumericParamValue):
+ cxx_type = 'Tick'
+ ex_str = "1MHz"
+ cmd_line_settable = True
+ @classmethod
+ def cxx_predecls(cls, code):
+ code('#include "base/types.hh"')
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def getValue(self):
+ return long(self.value)
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ code('#include <sstream>')
+
+ # Ticks are expressed in seconds in JSON files and in plain
+ # Ticks in .ini files. Switch based on a config flag
+ @classmethod
+ def cxx_ini_parse(self, code, src, dest, ret):
+ code('${ret} to_number(${src}, ${dest});')
+
+class Latency(TickParamValue):
+ ex_str = "100ns"
-class Latency(NumericParamValue):
- cxx_type = 'Tick'
- cxx_predecls = ['#include "sim/host.hh"']
- swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
- '%import "sim/host.hh"']
def __init__(self, value):
- self.value = getLatency(value)
+ if isinstance(value, (Latency, Clock)):
+ self.ticks = value.ticks
+ self.value = value.value
+ elif isinstance(value, Frequency):
+ self.ticks = value.ticks
+ self.value = 1.0 / value.value
+ elif value.endswith('t'):
+ self.ticks = True
+ self.value = int(value[:-1])
+ else:
+ self.ticks = False
+ self.value = convert.toLatency(value)
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
def __getattr__(self, attr):
if attr in ('latency', 'period'):
return self
if attr == 'frequency':
return Frequency(self)
- raise AttributeError, "Latency object has no attribute '%s'" % attr
+ raise AttributeError("Latency object has no attribute '%s'" % attr)
+
+ def getValue(self):
+ if self.ticks or self.value == 0:
+ value = self.value
+ else:
+ value = ticks.fromSeconds(self.value)
+ return long(value)
+
+ def config_value(self):
+ return self.getValue()
# convert latency to ticks
def ini_str(self):
- return str(tick_check(self.value * ticks_per_sec))
+ return '%d' % self.getValue()
+
+class Frequency(TickParamValue):
+ ex_str = "1GHz"
-class Frequency(NumericParamValue):
- cxx_type = 'Tick'
- cxx_predecls = ['#include "sim/host.hh"']
- swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
- '%import "sim/host.hh"']
def __init__(self, value):
- self.value = 1 / getLatency(value)
+ if isinstance(value, (Latency, Clock)):
+ if value.value == 0:
+ self.value = 0
+ else:
+ self.value = 1.0 / value.value
+ self.ticks = value.ticks
+ elif isinstance(value, Frequency):
+ self.value = value.value
+ self.ticks = value.ticks
+ else:
+ self.ticks = False
+ self.value = convert.toFrequency(value)
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
def __getattr__(self, attr):
if attr == 'frequency':
return self
if attr in ('latency', 'period'):
return Latency(self)
- raise AttributeError, "Frequency object has no attribute '%s'" % attr
+ raise AttributeError("Frequency object has no attribute '%s'" % attr)
+
+ # convert latency to ticks
+ def getValue(self):
+ if self.ticks or self.value == 0:
+ value = self.value
+ else:
+ value = ticks.fromSeconds(1.0 / self.value)
+ return long(value)
+
+ def config_value(self):
+ return self.getValue()
- # convert frequency to ticks per period
def ini_str(self):
- return self.period.ini_str()
+ return '%d' % self.getValue()
-# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
-# We can't inherit from Frequency because we don't want it to be directly
-# assignable to a regular Frequency parameter.
-class RootClock(ParamValue):
- cxx_type = 'Tick'
- cxx_predecls = ['#include "sim/host.hh"']
- swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
- '%import "sim/host.hh"']
+# A generic Frequency and/or Latency value. Value is stored as a
+# latency, just like Latency and Frequency.
+class Clock(TickParamValue):
def __init__(self, value):
- self.value = 1 / getLatency(value)
+ if isinstance(value, (Latency, Clock)):
+ self.ticks = value.ticks
+ self.value = value.value
+ elif isinstance(value, Frequency):
+ self.ticks = value.ticks
+ self.value = 1.0 / value.value
+ elif value.endswith('t'):
+ self.ticks = True
+ self.value = int(value[:-1])
+ else:
+ self.ticks = False
+ self.value = convert.anyToLatency(value)
+
+ def __call__(self, value):
+ self.__init__(value)
+ return value
+
+ def __str__(self):
+ return "%s" % Latency(self)
def __getattr__(self, attr):
if attr == 'frequency':
return Frequency(self)
if attr in ('latency', 'period'):
return Latency(self)
- raise AttributeError, "Frequency object has no attribute '%s'" % attr
+ raise AttributeError("Frequency object has no attribute '%s'" % attr)
+
+ def getValue(self):
+ return self.period.getValue()
+
+ def config_value(self):
+ return self.period.config_value()
def ini_str(self):
- return str(tick_check(self.value))
+ return self.period.ini_str()
+
+class Voltage(Float):
+ ex_str = "1V"
+
+ def __new__(cls, value):
+ value = convert.toVoltage(value)
+ return super(cls, Voltage).__new__(cls, value)
-# 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 /).
-# An explicit conversion to a Latency or Frequency must be made first.
-class Clock(ParamValue):
- cxx_type = 'Tick'
- cxx_predecls = ['#include "sim/host.hh"']
- swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
- '%import "sim/host.hh"']
def __init__(self, value):
- self.value = getLatency(value)
+ value = convert.toVoltage(value)
+ super(Voltage, self).__init__(value)
- def __getattr__(self, attr):
- if attr == 'frequency':
- return Frequency(self)
- if attr in ('latency', 'period'):
- return Latency(self)
- raise AttributeError, "Frequency object has no attribute '%s'" % attr
+class Current(Float):
+ ex_str = "1mA"
- def ini_str(self):
- return self.period.ini_str()
+ def __new__(cls, value):
+ value = convert.toCurrent(value)
+ return super(cls, Current).__new__(cls, value)
+
+ def __init__(self, value):
+ value = convert.toCurrent(value)
+ super(Current, self).__init__(value)
+
+class Energy(Float):
+ ex_str = "1pJ"
+
+ def __new__(cls, value):
+ value = convert.toEnergy(value)
+ return super(cls, Energy).__new__(cls, value)
+
+ def __init__(self, value):
+ value = convert.toEnergy(value)
+ super(Energy, self).__init__(value)
class NetworkBandwidth(float,ParamValue):
cxx_type = 'float'
+ ex_str = "1Gbps"
+ cmd_line_settable = True
+
def __new__(cls, value):
- val = convert.toNetworkBandwidth(value) / 8.0
+ # convert to bits per second
+ val = convert.toNetworkBandwidth(value)
return super(cls, NetworkBandwidth).__new__(cls, val)
def __str__(self):
return str(self.val)
+ def __call__(self, value):
+ val = convert.toNetworkBandwidth(value)
+ self.__init__(val)
+ return value
+
+ def getValue(self):
+ # convert to seconds per byte
+ value = 8.0 / float(self)
+ # convert to ticks per byte
+ value = ticks.fromSeconds(value)
+ return float(value)
+
def ini_str(self):
- return '%f' % (ticks_per_sec / float(self))
+ return '%f' % self.getValue()
+
+ def config_value(self):
+ return '%f' % self.getValue()
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ code('#include <sstream>')
+
+ @classmethod
+ def cxx_ini_parse(self, code, src, dest, ret):
+ code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
class MemoryBandwidth(float,ParamValue):
cxx_type = 'float'
- def __new__(self, value):
+ ex_str = "1GB/s"
+ cmd_line_settable = True
+
+ def __new__(cls, value):
+ # convert to bytes per second
val = convert.toMemoryBandwidth(value)
return super(cls, MemoryBandwidth).__new__(cls, val)
- def __str__(self):
- return str(self.val)
+ def __call__(self, value):
+ val = convert.toMemoryBandwidth(value)
+ self.__init__(val)
+ return value
+
+ def getValue(self):
+ # convert to seconds per byte
+ value = float(self)
+ if value:
+ value = 1.0 / float(self)
+ # convert to ticks per byte
+ value = ticks.fromSeconds(value)
+ return float(value)
def ini_str(self):
- return '%f' % (ticks_per_sec / float(self))
+ return '%f' % self.getValue()
+
+ def config_value(self):
+ return '%f' % self.getValue()
+
+ @classmethod
+ def cxx_ini_predecls(cls, code):
+ code('#include <sstream>')
+
+ @classmethod
+ def cxx_ini_parse(self, code, src, dest, ret):
+ code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
#
# "Constants"... handy aliases for various values.
# only one copy of a particular node
class NullSimObject(object):
__metaclass__ = Singleton
+ _name = 'Null'
def __call__(cls):
return cls
def set_path(self, parent, name):
pass
+
+ def set_parent(self, parent, name):
+ pass
+
+ def clear_parent(self, old_parent):
+ pass
+
+ def descendants(self):
+ return
+ yield None
+
+ def get_config_as_dict(self):
+ return {}
+
def __str__(self):
- return 'Null'
+ return self._name
+
+ def config_value(self):
+ return None
+
+ def getValue(self):
+ return None
# The only instance you'll ever need...
NULL = NullSimObject()
# Port reference: encapsulates a reference to a particular port on a
# particular SimObject.
class PortRef(object):
- def __init__(self, simobj, name, isVec):
- assert(isSimObject(simobj))
+ def __init__(self, simobj, name, role, is_source):
+ assert(isSimObject(simobj) or isSimObjectClass(simobj))
self.simobj = simobj
self.name = name
- self.index = -1
- self.isVec = isVec # is this a vector port?
+ self.role = role
+ self.is_source = is_source
self.peer = None # not associated with another port yet
self.ccConnected = False # C++ port connection done?
+ self.index = -1 # always -1 for non-vector ports
+
+ def __str__(self):
+ return '%s.%s' % (self.simobj, self.name)
+
+ def __len__(self):
+ # Return the number of connected ports, i.e. 0 is we have no
+ # peer and 1 if we do.
+ return int(self.peer != None)
+
+ # for config.ini, print peer's name (not ours)
+ def ini_str(self):
+ return str(self.peer)
+
+ # for config.json
+ def get_config_as_dict(self):
+ return {'role' : self.role, 'peer' : str(self.peer),
+ 'is_source' : str(self.is_source)}
- # 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
+ def __getattr__(self, attr):
+ if attr == 'peerObj':
+ # shorthand for proxies
+ return self.peer.simobj
+ raise AttributeError("'%s' object has no attribute '%s'" % \
+ (self.__class__.__name__, attr))
+
+ # Full connection is symmetric (both ways). Called via
+ # SimObject.__setattr__ as a result of a port assignment, e.g.,
+ # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
+ # e.g., "obj1.portA[3] = obj2.portB".
+ def connect(self, other):
+ if isinstance(other, VectorPortRef):
+ # reference to plain VectorPort is implicit append
+ other = other._get_next()
+ if self.peer and not proxy.isproxy(self.peer):
+ fatal("Port %s is already connected to %s, cannot connect %s\n",
+ self, self.peer, other);
self.peer = other
- def clone(self, memo):
+ if proxy.isproxy(other):
+ other.set_param_desc(PortParamDesc())
+ return
+ elif not isinstance(other, PortRef):
+ raise TypeError("assigning non-port reference '%s' to port '%s'" \
+ % (other, self))
+
+ if not Port.is_compat(self, other):
+ fatal("Ports %s and %s with roles '%s' and '%s' "
+ "are not compatible", self, other, self.role, other.role)
+
+ if other.peer is not self:
+ other.connect(self)
+
+ # Allow a compatible port pair to be spliced between a port and its
+ # connected peer. Useful operation for connecting instrumentation
+ # structures into a system when it is necessary to connect the
+ # instrumentation after the full system has been constructed.
+ def splice(self, new_1, new_2):
+ if not self.peer or proxy.isproxy(self.peer):
+ fatal("Port %s not connected, cannot splice in new peers\n", self)
+
+ if not isinstance(new_1, PortRef) or not isinstance(new_2, PortRef):
+ raise TypeError(
+ "Splicing non-port references '%s','%s' to port '%s'" % \
+ (new_1, new_2, self))
+
+ old_peer = self.peer
+
+ if Port.is_compat(old_peer, new_1) and Port.is_compat(self, new_2):
+ old_peer.peer = new_1
+ new_1.peer = old_peer
+ self.peer = new_2
+ new_2.peer = self
+ elif Port.is_compat(old_peer, new_2) and Port.is_compat(self, new_1):
+ old_peer.peer = new_2
+ new_2.peer = old_peer
+ self.peer = new_1
+ new_1.peer = self
+ else:
+ fatal("Ports %s(%s) and %s(%s) can't be compatibly spliced with "
+ "%s(%s) and %s(%s)", self, self.role,
+ old_peer, old_peer.role, new_1, new_1.role,
+ new_2, new_2.role)
+
+ def clone(self, simobj, memo):
+ if self in memo:
+ return memo[self]
newRef = copy.copy(self)
+ memo[self] = newRef
+ newRef.simobj = simobj
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
+ if self.peer and not proxy.isproxy(self.peer):
+ peerObj = self.peer.simobj(_memo=memo)
+ newRef.peer = self.peer.clone(peerObj, memo)
+ assert(not isinstance(newRef.peer, VectorPortRef))
return newRef
+ def unproxy(self, simobj):
+ assert(simobj is self.simobj)
+ if proxy.isproxy(self.peer):
+ try:
+ realPeer = self.peer.unproxy(self.simobj)
+ except:
+ print("Error in unproxying port '%s' of %s" %
+ (self.name, self.simobj.path()))
+ raise
+ self.connect(realPeer)
+
# Call C++ to create corresponding port connection between C++ objects
def ccConnect(self):
if self.ccConnected: # already done this
return
+
peer = self.peer
- cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
- peer.simobj.getCCObject(), peer.name, peer.index)
+ if not self.peer: # nothing to connect to
+ return
+
+ port = self.simobj.getPort(self.name, self.index)
+ peer_port = peer.simobj.getPort(peer.name, peer.index)
+ port.bind(peer_port)
+
self.ccConnected = True
- peer.ccConnected = True
+
+# A reference to an individual element of a VectorPort... much like a
+# PortRef, but has an index.
+class VectorPortElementRef(PortRef):
+ def __init__(self, simobj, name, role, is_source, index):
+ PortRef.__init__(self, simobj, name, role, is_source)
+ self.index = index
+
+ def __str__(self):
+ return '%s.%s[%d]' % (self.simobj, self.name, self.index)
+
+# A reference to a complete vector-valued port (not just a single element).
+# Can be indexed to retrieve individual VectorPortElementRef instances.
+class VectorPortRef(object):
+ def __init__(self, simobj, name, role, is_source):
+ assert(isSimObject(simobj) or isSimObjectClass(simobj))
+ self.simobj = simobj
+ self.name = name
+ self.role = role
+ self.is_source = is_source
+ self.elements = []
+
+ def __str__(self):
+ return '%s.%s[:]' % (self.simobj, self.name)
+
+ def __len__(self):
+ # Return the number of connected peers, corresponding the the
+ # length of the elements.
+ return len(self.elements)
+
+ # for config.ini, print peer's name (not ours)
+ def ini_str(self):
+ return ' '.join([el.ini_str() for el in self.elements])
+
+ # for config.json
+ def get_config_as_dict(self):
+ return {'role' : self.role,
+ 'peer' : [el.ini_str() for el in self.elements],
+ 'is_source' : str(self.is_source)}
+
+ def __getitem__(self, key):
+ if not isinstance(key, int):
+ raise TypeError("VectorPort index must be integer")
+ if key >= len(self.elements):
+ # need to extend list
+ ext = [VectorPortElementRef(
+ self.simobj, self.name, self.role, self.is_source, i)
+ for i in range(len(self.elements), key+1)]
+ self.elements.extend(ext)
+ return self.elements[key]
+
+ def _get_next(self):
+ return self[len(self.elements)]
+
+ def __setitem__(self, key, value):
+ if not isinstance(key, int):
+ raise TypeError("VectorPort index must be integer")
+ self[key].connect(value)
+
+ def connect(self, other):
+ if isinstance(other, (list, tuple)):
+ # Assign list of port refs to vector port.
+ # For now, append them... not sure if that's the right semantics
+ # or if it should replace the current vector.
+ for ref in other:
+ self._get_next().connect(ref)
+ else:
+ # scalar assignment to plain VectorPort is implicit append
+ self._get_next().connect(other)
+
+ def clone(self, simobj, memo):
+ if self in memo:
+ return memo[self]
+ newRef = copy.copy(self)
+ memo[self] = newRef
+ newRef.simobj = simobj
+ assert(isSimObject(newRef.simobj))
+ newRef.elements = [el.clone(simobj, memo) for el in self.elements]
+ return newRef
+
+ def unproxy(self, simobj):
+ [el.unproxy(simobj) for el in self.elements]
+
+ def ccConnect(self):
+ [el.ccConnect() for el in self.elements]
# 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):
+ # Port("role", "description")
+
+ _compat_dict = { }
+
+ @classmethod
+ def compat(cls, role, peer):
+ cls._compat_dict.setdefault(role, set()).add(peer)
+ cls._compat_dict.setdefault(peer, set()).add(role)
+
+ @classmethod
+ def is_compat(cls, one, two):
+ for port in one, two:
+ if not port.role in Port._compat_dict:
+ fatal("Unrecognized role '%s' for port %s\n", port.role, port)
+ return one.role in Port._compat_dict[two.role]
+
+ def __init__(self, role, desc, is_source=False):
self.desc = desc
- self.isVec = False
+ self.role = role
+ self.is_source = is_source
# 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)
+ def makeRef(self, simobj):
+ return PortRef(simobj, self.name, self.role, self.is_source)
# 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)
+ def connect(self, simobj, ref):
+ self.makeRef(simobj).connect(ref)
+
+ # No need for any pre-declarations at the moment as we merely rely
+ # on an unsigned int.
+ def cxx_predecls(self, code):
+ pass
+
+ def pybind_predecls(self, code):
+ cls.cxx_predecls(self, code)
+
+ # Declare an unsigned int with the same name as the port, that
+ # will eventually hold the number of connected ports (and thus the
+ # number of elements for a VectorPort).
+ def cxx_decl(self, code):
+ code('unsigned int port_${{self.name}}_connection_count;')
+
+Port.compat('GEM5 REQUESTER', 'GEM5 RESPONDER')
+
+class RequestPort(Port):
+ # RequestPort("description")
+ def __init__(self, desc):
+ super(RequestPort, self).__init__(
+ 'GEM5 REQUESTER', desc, is_source=True)
+
+class ResponsePort(Port):
+ # ResponsePort("description")
+ def __init__(self, desc):
+ super(ResponsePort, self).__init__('GEM5 RESPONDER', desc)
# VectorPort description object. Like Port, but represents a vector
-# of connections (e.g., as on a Bus).
+# of connections (e.g., as on a XBar).
class VectorPort(Port):
+ def makeRef(self, simobj):
+ return VectorPortRef(simobj, self.name, self.role, self.is_source)
+
+class VectorRequestPort(VectorPort):
+ # VectorRequestPort("description")
+ def __init__(self, desc):
+ super(VectorRequestPort, self).__init__(
+ 'GEM5 REQUESTER', desc, is_source=True)
+
+class VectorResponsePort(VectorPort):
+ # VectorResponsePort("description")
def __init__(self, desc):
- Port.__init__(self, desc)
- self.isVec = True
+ super(VectorResponsePort, self).__init__('GEM5 RESPONDER', desc)
+
+# Old names, maintained for compatibility.
+MasterPort = RequestPort
+SlavePort = ResponsePort
+VectorMasterPort = VectorRequestPort
+VectorSlavePort = VectorResponsePort
+
+# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
+# proxy objects (via set_param_desc()) so that proxy error messages
+# make sense.
+class PortParamDesc(object):
+ __metaclass__ = Singleton
+
+ ptype_str = 'Port'
+ ptype = Port
+baseEnums = allEnums.copy()
+baseParams = allParams.copy()
+
+def clear():
+ global allEnums, allParams
+
+ allEnums = baseEnums.copy()
+ allParams = baseParams.copy()
__all__ = ['Param', 'VectorParam',
- 'Enum', 'Bool', 'String', 'Float',
+ 'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
'Int32', 'UInt32', 'Int64', 'UInt64',
'Counter', 'Addr', 'Tick', 'Percent',
'TcpPort', 'UdpPort', 'EthernetAddr',
+ 'IpAddress', 'IpNetmask', 'IpWithPort',
'MemorySize', 'MemorySize32',
- 'Latency', 'Frequency', 'RootClock', 'Clock',
+ 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
'NetworkBandwidth', 'MemoryBandwidth',
- 'Range', 'AddrRange', 'TickRange',
+ 'AddrRange',
'MaxAddr', 'MaxTick', 'AllMemory',
+ 'Time',
'NextEthernetAddr', 'NULL',
- 'Port', 'VectorPort']
-
-# see comment on imports at end of __init__.py.
-from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
-import proxy
-import objects
-import cc_main
+ 'Port', 'RequestPort', 'ResponsePort', 'MasterPort', 'SlavePort',
+ 'VectorPort', 'VectorRequestPort', 'VectorResponsePort',
+ 'VectorMasterPort', 'VectorSlavePort']