blob: 78be6f683f8725fed7e7491d77f56ccc4e0381fb [file] [log] [blame]
# Copyright (c) 2012-2014, 2017-2019, 2021 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
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#####################################################################
#
# Parameter description classes
#
# The _params dictionary in each class maps parameter names to either
# a Param or a VectorParam object. These objects contain the
# parameter description string, the parameter type, and the default
# value (if any). The convert() method on these objects is used to
# force whatever value is assigned to the parameter to the appropriate
# type.
#
# Note that the default values are loaded into the class's attribute
# space when the parameter dictionary is initialized (in
# MetaSimObject._new_param()); after that point they aren't used.
#
#####################################################################
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)
if name in allParams:
warn("%s already exists in allParams. This may be caused by the " \
"Python 2.7 compatibility layer." % (name, ))
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
@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.ptype_str = ptype_str
# remember ptype only if it is provided
if ptype != None:
self.ptype = ptype
if args:
if len(args) == 1:
self.desc = args[0]
elif len(args) == 2:
self.default = args[0]
self.desc = args[1]
else:
raise TypeError('too many arguments')
if 'desc' in kwargs:
assert(not hasattr(self, 'desc'))
self.desc = kwargs['desc']
del kwargs['desc']
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)
if not hasattr(self, 'desc'):
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))
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 '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
if isinstance(value, self.ptype):
return value
if isNullPointer(value) and isSimObjectClass(self.ptype):
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 <cstddef>')
self.ptype.cxx_predecls(code)
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):
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])
def clear_parent(self, old_parent):
for v in self:
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
# 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) ]
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) ]
return tmp_list
# 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):
self.param_desc_class = param_desc_class
self.ptype_str = ptype_str
def __getattr__(self, attr):
if self.ptype_str:
attr = self.ptype_str + '.' + attr
return ParamFactory(self.param_desc_class, attr)
# E.g., Param.Int(5, "number of widgets")
def __call__(self, *args, **kwargs):
ptype = None
try:
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
return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
Param = ParamFactory(ParamDesc)
VectorParam = ParamFactory(VectorParamDesc)
#####################################################################
#
# Parameter Types
#
# Though native Python types could be used to specify parameter types
# (the 'ptype' field of the Param and VectorParam classes), it's more
# flexible to define our own set of types. This gives us more control
# over how Python expressions are converted to values (via the
# __init__() constructor) and how these values are printed out (via
# the __str__() conversion method).
#
#####################################################################
# String-valued parameter. Just mixin the ParamValue class with the
# built-in str class.
class String(ParamValue,str):
cxx_type = 'std::string'
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 __int__(self):
return int(self.value)
# hook for bounds checking
def _check(self):
return
def __mul__(self, other):
newobj = self.__class__(self)
newobj.value *= NumericParamValue.unwrap(other)
newobj._check()
return newobj
__rmul__ = __mul__
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 += NumericParamValue.unwrap(other)
newobj._check()
return newobj
def __sub__(self, other):
newobj = self.__class__(self)
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)
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):
super(CheckedIntType, cls).__init__(name, bases, dict)
# CheckedInt is an abstract base class, so we actually don't
# want to do any processing on it... the rest of this code is
# just for classes that derive from CheckedInt.
if name == 'CheckedInt':
return
if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
panic("CheckedInt subclass %s must define either\n" \
" 'min' and 'max' or 'size' and 'unsigned'\n",
name);
if cls.unsigned:
cls.min = 0
cls.max = 2 ** cls.size - 1
else:
cls.min = -(2 ** (cls.size - 1))
cls.max = (2 ** (cls.size - 1)) - 1
# Abstract superclass for bounds-checked integer parameters. This
# class is subclassed to generate parameter classes with specific
# bounds. Initialization of the min and max bounds is done in the
# metaclass CheckedIntType.__init__.
class CheckedInt(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))
def __init__(self, value):
if isinstance(value, str):
self.value = convert.toInteger(value)
elif isinstance(value, (int, float, NumericParamValue)):
self.value = int(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 int(self.value)
class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
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, 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 = '512MiB'
size = 64
unsigned = True
def __init__(self, value):
if isinstance(value, MemorySize):
self.value = value.value
else:
self.value = convert.toMemorySize(value)
self._check()
class MemorySize32(CheckedInt):
cxx_type = 'uint32_t'
ex_str = '512MiB'
size = 32
unsigned = True
def __init__(self, value):
if isinstance(value, MemorySize):
self.value = value.value
else:
self.value = convert.toMemorySize(value)
self._check()
class Addr(CheckedInt):
cxx_type = 'Addr'
size = 64
unsigned = True
def __init__(self, value):
if isinstance(value, Addr):
self.value = value.value
else:
try:
# Often addresses are referred to with sizes. Ex: A device
# base address is at "512MiB". 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, ValueError):
# Convert number to string and use long() to do automatic
# base conversion (requires base=0 for auto-conversion)
self.value = int(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 = int(value)
return "0x%x" % int(val)
class AddrRange(ParamValue):
cxx_type = 'AddrRange'
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.end = Addr(kwargs.pop('end'))
elif 'size' in kwargs:
self.end = self.start + Addr(kwargs.pop('size'))
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 = [ int(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.start = Addr(kwargs.pop('start'))
handle_kwargs(self, kwargs)
elif len(args) == 1:
if kwargs:
self.start = Addr(args[0])
handle_kwargs(self, kwargs)
elif isinstance(args[0], (list, tuple)):
self.start = Addr(args[0][0])
self.end = Addr(args[0][1])
else:
self.start = Addr(0)
self.end = Addr(args[0])
elif len(args) == 2:
self.start = Addr(args[0])
self.end = Addr(args[1])
else:
raise TypeError("Too many arguments specified")
if kwargs:
raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
def __str__(self):
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 (int(self.end) - int(self.start)) >> 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(int(self.start), int(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 = [ 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)
bytes[i] = rem
if val == 0:
break
bytes[i - 1] += val
assert(bytes[0] <= 255)
return ':'.join(map(lambda x: '%02x' % x, bytes))
_NextEthernetAddr = "00:90:00:00:00:01"
def NextEthernetAddr():
global _NextEthernetAddr
value = _NextEthernetAddr
_NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
return value
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"')
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")
bytes = value.split(':')
if len(bytes) != 6:
raise TypeError('invalid ethernet address %s' % value)
for byte in bytes:
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.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"')
def __init__(self, value):
if isinstance(value, IpAddress):
self.ip = value.ip
else:
try:
self.ip = convert.toIpAddress(value)
except TypeError:
self.ip = int(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:
(self.ip, self.netmask) = convert.toIpNetmask(args[0])
elif len(args) == 2:
self.ip = args[0]
self.netmask = 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(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):
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
# alternatives (typically strings, but not necessarily so). (In the
# long run, the integer value of the parameter will be the list index
# or the corresponding dictionary value. For now, since we only check
# that the alternative is valid and then spit it into a .ini file,
# there's not much point in using the dictionary.)
# What Enum() must do is generate a new type encapsulating the
# provided list/dictionary so that specific values of the parameter
# can be instances of that type. We define two hidden internal
# 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(MetaParamValue):
def __new__(mcls, name, bases, dict):
cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
allEnums[name] = cls
return cls
def __init__(cls, name, bases, init_dict):
if 'map' in init_dict:
if not isinstance(cls.map, dict):
raise TypeError("Enum-derived class attribute 'map' " \
"must be of type dict")
# build list of value strings from map
cls.vals = list(cls.map.keys())
cls.vals.sort()
elif 'vals' in init_dict:
if not isinstance(cls.vals, 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'")
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, 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})')
if not cls.is_class:
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))
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
# This param will generate a scoped c++ enum and its python bindings.
class ScopedEnum(Enum):
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
class ByteOrder(ScopedEnum):
"""Enum representing component's byte order (endianness)"""
vals = [
'big',
'little',
]
# 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"')
def __call__(self, value):
self.__init__(value)
return value
def getValue(self):
return int(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"
def __init__(self, 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)
def getValue(self):
if self.ticks or self.value == 0:
value = self.value
else:
value = ticks.fromSeconds(self.value)
return int(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:
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)
# 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 int(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, just like Latency and Frequency.
class Clock(TickParamValue):
def __init__(self, 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)
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 Temperature(ParamValue):
cxx_type = 'Temperature'
cmd_line_settable = True
ex_str = "1C"
def __init__(self, value):
self.value = convert.toTemperature(value)
def __call__(self, value):
self.__init__(value)
return value
def __str__(self):
return str(self.value)
def getValue(self):
from _m5.core import Temperature
return Temperature.from_kelvin(self.value)
def config_value(self):
return self.value
@classmethod
def cxx_predecls(cls, code):
code('#include "base/temperature.hh"')
@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(self, code, src, dest, ret):
code('double _temp;')
code('bool _ret = to_number(%s, _temp);' % src)
code('if (_ret)')
code(' %s = Temperature(_temp);' % dest)
code('%s _ret;' % ret)
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)
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' % 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'
ex_str = "1GiB/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 __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' % 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.
#
# Special class for NULL pointers. Note the special check in
# make_param_value() above that lets these be assigned where a
# SimObject is required.
# only one copy of a particular node
class NullSimObject(object, metaclass=Singleton):
_name = 'Null'
def __call__(cls):
return cls
def _instantiate(self, parent = None, path = ''):
pass
def ini_str(self):
return 'Null'
def unproxy(self, base):
return self
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 self._name
def config_value(self):
return None
def getValue(self):
return None
# The only instance you'll ever need...
NULL = NullSimObject()
def isNullPointer(value):
return isinstance(value, NullSimObject)
# Some memory range specifications use this as a default upper bound.
MaxAddr = Addr.max
MaxTick = Tick.max
AllMemory = AddrRange(0, MaxAddr)
#####################################################################
#
# Port objects
#
# Ports are used to interconnect objects in the memory system.
#
#####################################################################
# Port reference: encapsulates a reference to a particular port on a
# particular SimObject.
class PortRef(object):
def __init__(self, simobj, name, 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
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))
# 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
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))
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
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
# 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):
# 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, 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 REQUESTOR', 'GEM5 RESPONDER')
class RequestPort(Port):
# RequestPort("description")
def __init__(self, desc):
super(RequestPort, self).__init__(
'GEM5 REQUESTOR', 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 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 REQUESTOR', 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
# make sense.
class PortParamDesc(object, metaclass=Singleton):
ptype_str = 'Port'
ptype = Port
class DeprecatedParam(object):
"""A special type for deprecated parameter variable names.
There are times when we need to change the name of parameter, but this
breaks the external-facing python API used in configuration files. Using
this "type" for a parameter will warn users that they are using the old
name, but allow for backwards compatibility.
Usage example:
In the following example, the `time` parameter is changed to `delay`.
```
class SomeDevice(SimObject):
delay = Param.Latency('1ns', 'The time to wait before something')
time = DeprecatedParam(delay, '`time` is now called `delay`')
```
"""
def __init__(self, new_param, message=''):
"""new_param: the new parameter variable that users should be using
instead of this parameter variable.
message: an optional message to print when warning the user
"""
self.message = message
self.newParam = new_param
# Note: We won't know the string variable names until later in the
# SimObject initialization process. Note: we expect that the setters
# will be called when the SimObject type (class) is initialized so
# these variables should be filled in before the instance of the
# SimObject with this parameter is constructed
self._oldName = ''
self._newName = ''
@property
def oldName(self):
assert(self._oldName != '') # should already be set
return self._oldName
@oldName.setter
def oldName(self, name):
assert(self._oldName == '') # Cannot "re-set" this value
self._oldName = name
@property
def newName(self):
assert(self._newName != '') # should already be set
return self._newName
@newName.setter
def newName(self, name):
assert(self._newName == '') # Cannot "re-set" this value
self._newName = name
def printWarning(self, instance_name, simobj_name):
"""Issue a warning that this variable name should not be used.
instance_name: str, the name of the instance used in python
simobj_name: str, the name of the SimObject type
"""
if not self.message:
self.message = "See {} for more information".format(simobj_name)
warn('{}.{} is deprecated. {}'.format(
instance_name, self._oldName, self.message))
baseEnums = allEnums.copy()
baseParams = allParams.copy()
def clear():
global allEnums, allParams
allEnums = baseEnums.copy()
allParams = baseParams.copy()
__all__ = ['Param', 'VectorParam',
'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', 'Voltage', 'Current', 'Energy',
'Temperature',
'NetworkBandwidth', 'MemoryBandwidth',
'AddrRange',
'MaxAddr', 'MaxTick', 'AllMemory',
'Time',
'NextEthernetAddr', 'NULL',
'Port', 'RequestPort', 'ResponsePort', 'MasterPort', 'SlavePort',
'VectorPort', 'VectorRequestPort', 'VectorResponsePort',
'VectorMasterPort', 'VectorSlavePort',
'DeprecatedParam',
]