X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fpython%2Fm5%2Fparams.py;h=b9afff2a1dccf4e1e1904949a298472ad7b74e95;hb=24b56f92731091fb9a6c989affb04cca3109b29b;hp=4dd8797836e1168dcfd54e7e00cb0eb4d128e11d;hpb=9751a1d3e78cbbcd17835ab967f036945ee2cec2;p=gem5.git diff --git a/src/python/m5/params.py b/src/python/m5/params.py index 4dd879783..b9afff2a1 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -1,5 +1,17 @@ +# 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 Advanced Micro Devices, Inc. +# Copyright (c) 2010-2011 Advanced Micro Devices, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -28,6 +40,7 @@ # Authors: Steve Reinhardt # Nathan Binkert # Gabe Black +# Andreas Hansson ##################################################################### # @@ -46,6 +59,11 @@ # ##################################################################### +from __future__ import print_function +import six +if six.PY3: + long = int + import copy import datetime import re @@ -53,17 +71,20 @@ import sys import time import math -import proxy -import ticks -from util import * +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 = {} @@ -80,29 +101,53 @@ class MetaParamValue(type): # 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 @classmethod - def swig_predecls(cls, code): - pass + 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): - file_ext = 'ptype' - def __init__(self, ptype_str, ptype, *args, **kwargs): self.ptype_str = ptype_str # remember ptype only if it is provided @@ -116,39 +161,53 @@ class ParamDesc(object): 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': + 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) + 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 @@ -158,11 +217,19 @@ class ParamDesc(object): return value return self.ptype(value) + 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 cxx_predecls(self, code): + code('#include ') self.ptype.cxx_predecls(code) - def swig_predecls(self, code): - self.ptype.swig_predecls(code) + def pybind_predecls(self, code): + self.ptype.pybind_predecls(code) def cxx_decl(self, code): code('${{self.ptype.cxx_type}} ${{self.name}};') @@ -174,8 +241,11 @@ class ParamDesc(object): class VectorParamValue(list): __metaclass__ = MetaParamValue def __setattr__(self, attr, value): - raise AttributeError, \ - "Not allowed to set %s on '%s'" % (attr, type(self).__name__) + 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]) @@ -184,10 +254,12 @@ class VectorParamValue(list): return [ v.getValue() for v in self ] def unproxy(self, base): - if len(self) == 1 and isinstance(self[0], AllProxy): + 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] + return [v.unproxy(base) for v in self] class SimObjectVector(VectorParamValue): # support clone operation @@ -207,7 +279,7 @@ class SimObjectVector(VectorParamValue): v.set_parent(parent, "%s%0*d" % (name, width, i)) def has_parent(self): - return reduce(lambda x,y: x and y, [v.has_parent() for v in 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): @@ -222,15 +294,56 @@ class SimObjectVector(VectorParamValue): for obj in v.descendants(): yield obj -class VectorParamDesc(ParamDesc): - file_ext = 'vptype' + 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 # list or tuple, it generates a single-element list. def convert(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) ] @@ -240,48 +353,48 @@ class VectorParamDesc(ParamDesc): else: return VectorParamValue(tmp_list) - def swig_predecls(self, code): - code('%import "vptype_${{self.ptype_str}}.i"') + # 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 - def swig_decl(self, code): - code('%{') - self.ptype.cxx_predecls(code) - code('%}') - code() - self.ptype.swig_predecls(code) - code() - code('%include "std_vector.i"') - code() + # 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) ] - ptype = self.ptype_str - cxx_type = self.ptype.cxx_type + return tmp_list - code('''\ -%typemap(in) std::vector< $cxx_type >::value_type { - if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) { - if (SWIG_ConvertPtr($$input, (void **)&$$1, - $$descriptor($cxx_type), 0) == -1) { - return NULL; - } - } -} - -%typemap(in) std::vector< $cxx_type >::value_type * { - if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) { - if (SWIG_ConvertPtr($$input, (void **)&$$1, - $$descriptor($cxx_type *), 0) == -1) { - return NULL; - } - } -} -''') + # 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) ] - code('%template(vector_$ptype) std::vector< $cxx_type >;') + return VectorParamValue(tmp_list) def cxx_predecls(self, code): code('#include ') self.ptype.cxx_predecls(code) + def pybind_predecls(self, code): + code('#include ') + self.ptype.pybind_predecls(code) + def cxx_decl(self, code): code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};') @@ -326,14 +439,20 @@ VectorParam = ParamFactory(VectorParamDesc) # built-in str class. class String(ParamValue,str): cxx_type = 'std::string' + cmd_line_settable = True @classmethod def cxx_predecls(self, code): code('#include ') + def __call__(self, value): + self = value + return value + @classmethod - def swig_predecls(cls, code): - code('%include "std_string.i"') + def cxx_ini_parse(self, code, src, dest, ret): + code('%s = %s;' % (dest, src)) + code('%s true;' % ret) def getValue(self): return self @@ -342,6 +461,10 @@ class String(ParamValue,str): # 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) @@ -360,24 +483,86 @@ class NumericParamValue(ParamValue): 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 /= 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 /= other + 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(MetaParamValue): def __init__(cls, name, bases, dict): @@ -407,11 +592,12 @@ class CheckedIntType(MetaParamValue): # 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): @@ -419,21 +605,22 @@ class CheckedInt(NumericParamValue): 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__ + 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"') - @classmethod - def swig_predecls(cls, code): - # most derived types require this, so we just do it here once - code('%import "stdint.i"') - code('%import "base/types.hh"') - def getValue(self): return long(self.value) @@ -456,21 +643,61 @@ class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; 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): cxx_type = 'double' + cmd_line_settable = True def __init__(self, value): - if isinstance(value, (int, long, float, NumericParamValue, Float)): + 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__ + 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 ') + + @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): @@ -482,6 +709,7 @@ class MemorySize(CheckedInt): class MemorySize32(CheckedInt): cxx_type = 'uint32_t' + ex_str = '512MB' size = 32 unsigned = True def __init__(self, value): @@ -500,122 +728,212 @@ class Addr(CheckedInt): 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) + 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 AddrRange(ParamValue): + cxx_type = 'AddrRange' -class MetaRange(MetaParamValue): - 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 - -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") + + # 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: - raise TypeError, "Either end or size must be specified" + 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.first = self.type(args[0][0]) - self.second = self.type(args[0][1]) + 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) + 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): - cls.type.cxx_predecls(code) - code('#include "base/range.hh"') + Addr.cxx_predecls(code) + code('#include "base/addr_range.hh"') @classmethod - def swig_predecls(cls, code): - cls.type.swig_predecls(code) - code('%import "python/swig/range.i"') - -class AddrRange(Range): - type = Addr + def pybind_predecls(cls, code): + Addr.pybind_predecls(code) + code('#include "base/addr_range.hh"') - def getValue(self): - from m5.internal.range import AddrRange - - value = AddrRange() - value.start = long(self.first) - value.end = long(self.second) - return value + @classmethod + def cxx_ini_predecls(cls, code): + code('#include ') + code('#include ') + code('#include "base/types.hh"') -class TickRange(Range): - type = Tick + @classmethod + def cxx_ini_parse(cls, code, src, dest, ret): + code('bool _ret = true;') + code('uint64_t _start, _end, _intlvMatch = 0;') + code('std::vector _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): - from m5.internal.range import TickRange + # Go from the Python class to the wrapped C++ class + from _m5.range import AddrRange - value = TickRange() - value.start = long(self.first) - value.end = long(self.second) - return value + 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) @@ -636,58 +954,66 @@ def NextEthernetAddr(): class EthernetAddr(ParamValue): cxx_type = '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"') - @classmethod - def swig_predecls(cls, code): - code('%include "python/swig/inet.i"') - 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) <= 0xff: - 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: return EthernetAddr(self.value()) return self def getValue(self): - from m5.internal.params import EthAddr + from _m5.net import EthAddr return EthAddr(self.value) + def __str__(self): + 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"') - @classmethod - def swig_predecls(cls, code): - code('%include "python/swig/inet.i"') - def __init__(self, value): if isinstance(value, IpAddress): self.ip = value.ip @@ -698,31 +1024,48 @@ class IpAddress(ParamValue): 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 + raise TypeError("invalid ip address %#08x" % self.ip) def getValue(self): - from m5.internal.params import IpAddress + from _m5.net import IpAddress return IpAddress(self.ip) - def ini_str(self): - return 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"') - @classmethod - def swig_predecls(cls, code): - code('%include "python/swig/inet.i"') - def __init__(self, *args, **kwargs): def handle_kwarg(self, kwargs, key, elseVal = None): if key in kwargs: @@ -730,7 +1073,7 @@ class IpNetmask(IpAddress): elif elseVal: setattr(self, key, elseVal) else: - raise TypeError, "No value set for %s" % key + raise TypeError("No value set for %s" % key) if len(args) == 0: handle_kwarg(self, kwargs, 'ip') @@ -739,7 +1082,7 @@ class IpNetmask(IpAddress): elif len(args) == 1: if kwargs: if not 'ip' in kwargs and not 'netmask' in kwargs: - raise TypeError, "Invalid arguments" + raise TypeError("Invalid arguments") handle_kwarg(self, kwargs, 'ip', args[0]) handle_kwarg(self, kwargs, 'netmask', args[0]) elif isinstance(args[0], IpNetmask): @@ -752,38 +1095,51 @@ class IpNetmask(IpAddress): self.ip = args[0] self.netmask = 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())) 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 + raise TypeError("invalid netmask %d" % netmask) def getValue(self): - from m5.internal.params import IpNetmask + from _m5.net import IpNetmask return IpNetmask(self.ip, self.netmask) - def ini_str(self): - return "%08x/%d" % (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"') - @classmethod - def swig_predecls(cls, code): - code('%include "python/swig/inet.i"') - def __init__(self, *args, **kwargs): def handle_kwarg(self, kwargs, key, elseVal = None): if key in kwargs: @@ -791,7 +1147,7 @@ class IpWithPort(IpAddress): elif elseVal: setattr(self, key, elseVal) else: - raise TypeError, "No value set for %s" % key + raise TypeError("No value set for %s" % key) if len(args) == 0: handle_kwarg(self, kwargs, 'ip') @@ -800,7 +1156,7 @@ class IpWithPort(IpAddress): elif len(args) == 1: if kwargs: if not 'ip' in kwargs and not 'port' in kwargs: - raise TypeError, "Invalid arguments" + raise TypeError("Invalid arguments") handle_kwarg(self, kwargs, 'ip', args[0]) handle_kwarg(self, kwargs, 'port', args[0]) elif isinstance(args[0], IpWithPort): @@ -813,27 +1169,42 @@ class IpWithPort(IpAddress): self.ip = args[0] self.port = 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())) 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 + raise TypeError("invalid port %d" % self.port) def getValue(self): - from m5.internal.params import IpWithPort + from _m5.net import IpWithPort return IpWithPort(self.ip, self.port) - def ini_str(self): - return "%08x:%d" % (self.ip, self.port) - time_formats = [ "%a %b %d %H:%M:%S %Z %Y", - "%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", @@ -868,7 +1239,7 @@ def parse_time(value): except ValueError: pass - raise ValueError, "Could not parse '%s' as a time" % value + raise ValueError("Could not parse '%s' as a time" % value) class Time(ParamValue): cxx_type = 'tm' @@ -877,38 +1248,18 @@ class Time(ParamValue): def cxx_predecls(cls, code): code('#include ') - @classmethod - def swig_predecls(cls, code): - code('%include "python/swig/time.i"') - def __init__(self, value): self.value = parse_time(value) - def getValue(self): - from m5.internal.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; + def __call__(self, value): + self.__init__(value) + return value - # Python starts at 1, Unix starts at 0 - c_time.tm_yday = py_time.tm_yday - 1; + def getValue(self): + from _m5.core import tm + import calendar - return c_time + return tm.gmtime(calendar.timegm(self.value)) def __str__(self): return time.asctime(self.value) @@ -916,6 +1267,20 @@ class Time(ParamValue): 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 ') + + @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 # alternatives (typically strings, but not necessarily so). (In the @@ -941,26 +1306,29 @@ class MetaEnum(MetaParamValue): 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 = 'Enums::%s' % name + if cls.is_class: + cls.cxx_type = '%s' % name + else: + cls.cxx_type = 'Enums::%s' % name super(MetaEnum, cls).__init__(name, bases, init_dict) @@ -968,88 +1336,222 @@ class MetaEnum(MetaParamValue): # 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, code): - name = cls.__name__ + 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 __ENUM__${name}__ -#define __ENUM__${name}__ +#ifndef $idem_macro +#define $idem_macro -namespace Enums { +''') + if cls.is_class: + code('''\ +enum class $name { +''') + else: + code('''\ +$wrapper $wrapper_name { enum $name { ''') - code.indent(2) + code.indent(1) + code.indent(1) for val in cls.vals: code('$val = ${{cls.map[val]}},') - code('Num_$name = ${{len(cls.vals)}},') - code.dedent(2) - code('''\ - }; -extern const char *${name}Strings[Num_${name}]; -} + code('Num_$name = ${{len(cls.vals)}}') + code.dedent(1) + code('};') -#endif // __ENUM__${name}__ + if cls.is_class: + code('''\ +extern const char *${name}Strings[static_cast(${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): - name = cls.__name__ - code('''\ -#include "enums/$name.hh" -namespace Enums { - const char *${name}Strings[Num_${name}] = - { + 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(${name}::Num_${name})] = ''') - code.indent(2) + 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(2) - code(''' - }; -} // namespace Enums + 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 + +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 swig_predecls(cls, code): - code('%import "python/m5/internal/enum_$0.i"', cls.__name__) + 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): - return int(self.map[self.value]) + 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 +# 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% class TickParamValue(NumericParamValue): cxx_type = 'Tick' + ex_str = "1MHz" + cmd_line_settable = True @classmethod def cxx_predecls(cls, code): code('#include "base/types.hh"') - @classmethod - def swig_predecls(cls, code): - code('%import "stdint.i"') - code('%import "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 ') + + # 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" + def __init__(self, value): if isinstance(value, (Latency, Clock)): self.ticks = value.ticks @@ -1064,12 +1566,16 @@ class Latency(TickParamValue): 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: @@ -1078,11 +1584,16 @@ class Latency(TickParamValue): value = ticks.fromSeconds(self.value) return long(value) + def config_value(self): + return self.getValue() + # convert latency to ticks def ini_str(self): return '%d' % self.getValue() class Frequency(TickParamValue): + ex_str = "1GHz" + def __init__(self, value): if isinstance(value, (Latency, Clock)): if value.value == 0: @@ -1097,12 +1608,16 @@ class Frequency(TickParamValue): 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): @@ -1112,24 +1627,15 @@ class Frequency(TickParamValue): value = ticks.fromSeconds(1.0 / self.value) return long(value) + def config_value(self): + return self.getValue() + 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 /). -# An explicit conversion to a Latency or Frequency must be made first. -class Clock(ParamValue): - cxx_type = 'Tick' - - @classmethod - def cxx_predecls(cls, code): - code('#include "base/types.hh"') - - @classmethod - def swig_predecls(cls, code): - code('%import "stdint.i"') - code('%import "base/types.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): if isinstance(value, (Latency, Clock)): self.ticks = value.ticks @@ -1144,21 +1650,67 @@ class Clock(ParamValue): 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 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) + + def __init__(self, value): + value = convert.toVoltage(value) + super(Voltage, self).__init__(value) + +class Current(Float): + ex_str = "1mA" + + 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): # convert to bits per second val = convert.toNetworkBandwidth(value) @@ -1167,6 +1719,11 @@ class NetworkBandwidth(float,ParamValue): 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) @@ -1177,15 +1734,31 @@ class NetworkBandwidth(float,ParamValue): def ini_str(self): return '%f' % self.getValue() + def config_value(self): + return '%f' % self.getValue() + + @classmethod + def cxx_ini_predecls(cls, code): + code('#include ') + + @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' + 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 @@ -1199,6 +1772,17 @@ class MemoryBandwidth(float,ParamValue): def ini_str(self): return '%f' % self.getValue() + def config_value(self): + return '%f' % self.getValue() + + @classmethod + def cxx_ini_predecls(cls, code): + code('#include ') + + @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. # @@ -1209,6 +1793,7 @@ class MemoryBandwidth(float,ParamValue): # only one copy of a particular node class NullSimObject(object): __metaclass__ = Singleton + _name = 'Null' def __call__(cls): return cls @@ -1225,8 +1810,24 @@ class NullSimObject(object): 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 @@ -1254,10 +1855,12 @@ AllMemory = AddrRange(0, MaxAddr) # Port reference: encapsulates a reference to a particular port on a # particular SimObject. class PortRef(object): - def __init__(self, simobj, name): + 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.peer = None # not associated with another port yet self.ccConnected = False # C++ port connection done? self.index = -1 # always -1 for non-vector ports @@ -1265,16 +1868,26 @@ class PortRef(object): 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)} + 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) + 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., @@ -1285,22 +1898,57 @@ class PortRef(object): # reference to plain VectorPort is implicit append other = other._get_next() if self.peer and not proxy.isproxy(self.peer): - print "warning: overwriting port", self, \ - "value", self.peer, "with", other - self.peer.peer = None + fatal("Port %s is already connected to %s, cannot connect %s\n", + self, self.peer, other); self.peer = other + if proxy.isproxy(other): other.set_param_desc(PortParamDesc()) - elif isinstance(other, PortRef): - if other.peer is not self: - other.connect(self) + 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: - raise TypeError, \ - "assigning non-port reference '%s' to port '%s'" \ - % (other, self) + 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 memo.has_key(self): + if self in memo: return memo[self] newRef = copy.copy(self) memo[self] = newRef @@ -1318,36 +1966,31 @@ class PortRef(object): try: realPeer = self.peer.unproxy(self.simobj) except: - print "Error in unproxying port '%s' of %s" % \ - (self.name, self.simobj.path()) + 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): - from m5.internal.params import connectPorts - if self.ccConnected: # already done this return + peer = self.peer if not self.peer: # nothing to connect to return - try: - connectPorts(self.simobj.getCCObject(), self.name, self.index, - peer.simobj.getCCObject(), peer.name, peer.index) - except: - print "Error connecting port %s.%s to %s.%s" % \ - (self.simobj.path(), self.name, - peer.simobj.path(), peer.name) - raise + + 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, index): - PortRef.__init__(self, simobj, name) + def __init__(self, simobj, name, role, is_source, index): + PortRef.__init__(self, simobj, name, role, is_source) self.index = index def __str__(self): @@ -1356,25 +1999,39 @@ class VectorPortElementRef(PortRef): # 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): + 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" + raise TypeError("VectorPort index must be integer") if key >= len(self.elements): # need to extend list - ext = [VectorPortElementRef(self.simobj, self.name, i) + 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] @@ -1384,7 +2041,7 @@ class VectorPortRef(object): def __setitem__(self, key, value): if not isinstance(key, int): - raise TypeError, "VectorPort index must be integer" + raise TypeError("VectorPort index must be integer") self[key].connect(value) def connect(self, other): @@ -1399,7 +2056,7 @@ class VectorPortRef(object): self._get_next().connect(other) def clone(self, simobj, memo): - if memo.has_key(self): + if self in memo: return memo[self] newRef = copy.copy(self) memo[self] = newRef @@ -1418,37 +2075,86 @@ class VectorPortRef(object): # logical port in the SimObject class, not a particular port on a # SimObject instance. The latter are represented by PortRef objects. class Port(object): - # Port("description") or Port(default, "description") - def __init__(self, *args): - if len(args) == 1: - self.desc = args[0] - elif len(args) == 2: - self.default = args[0] - self.desc = args[1] - else: - raise TypeError, 'wrong number of arguments' - # self.name is set by SimObject class on assignment - # e.g., pio_port = Port("blah") sets self.name to 'pio_port' + # 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.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): - return PortRef(simobj, self.name) + 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, 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 __init__(self, *args): - Port.__init__(self, *args) - self.isVec = True - def makeRef(self, simobj): - return VectorPortRef(simobj, self.name) + 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): + 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 @@ -1469,19 +2175,19 @@ def clear(): 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', 'Clock', + 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy', 'NetworkBandwidth', 'MemoryBandwidth', - 'Range', 'AddrRange', 'TickRange', + 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'Time', 'NextEthernetAddr', 'NULL', - 'Port', 'VectorPort'] - -import SimObject + 'Port', 'RequestPort', 'ResponsePort', 'MasterPort', 'SlavePort', + 'VectorPort', 'VectorRequestPort', 'VectorResponsePort', + 'VectorMasterPort', 'VectorSlavePort']