gem5/src/python/m5/params.py
Andreas Hansson 66df7b7fd4 config: Add the ability to read a config file using C++ and Python
This patch adds the ability to load in config.ini files generated from
gem5 into another instance of gem5 built without Python configuration
support. The intended use case is for configuring gem5 when it is a
library embedded in another simulation system.

A parallel config file reader is also provided purely in Python to
demonstrate the approach taken and to provided similar functionality
for as-yet-unknown use models. The Python configuration file reader
can read both .ini and .json files.

C++ configuration file reading:

A command line option has been added for scons to enable C++ configuration
file reading: --with-cxx-config

There is an example in util/cxx_config that shows C++ configuration in action.
util/cxx_config/README explains how to build the example.

Configuration is achieved by the object CxxConfigManager. It handles
reading object descriptions from a CxxConfigFileBase object which
wraps a config file reader. The wrapper class CxxIniFile is provided
which wraps an IniFile for reading .ini files. Reading .json files
from C++ would be possible with a similar wrapper and a JSON parser.

After reading object descriptions, CxxConfigManager creates
SimObjectParam-derived objects from the classes in the (generated with this
patch) directory build/ARCH/cxx_config

CxxConfigManager can then build SimObjects from those SimObjectParams (in an
order dictated by the SimObject-value parameters on other objects) and bind
ports of the produced SimObjects.

A minimal set of instantiate-replacing member functions are provided by
CxxConfigManager and few of the member functions of SimObject (such as drain)
are extended onto CxxConfigManager.

Python configuration file reading (configs/example/read_config.py):

A Python version of the reader is also supplied with a similar interface to
CxxConfigFileBase (In Python: ConfigFile) to config file readers.

The Python config file reading will handle both .ini and .json files.

The object construction strategy is slightly different in Python from the C++
reader as you need to avoid objects prematurely becoming the children of other
objects when setting parameters.

Port binding also needs to be strictly in the same port-index order as the
original instantiation.
2014-10-16 05:49:37 -04:00

2112 lines
67 KiB
Python

# Copyright (c) 2012-2014 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.
#
# Authors: Steve Reinhardt
# Nathan Binkert
# Gabe Black
# Andreas Hansson
#####################################################################
#
# 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
import proxy
import ticks
from util import *
def isSimObject(*args, **kwargs):
return SimObject.isSimObject(*args, **kwargs)
def isSimObjectSequence(*args, **kwargs):
return SimObject.isSimObjectSequence(*args, **kwargs)
def isSimObjectClass(*args, **kwargs):
return SimObject.isSimObjectClass(*args, **kwargs)
allParams = {}
class MetaParamValue(type):
def __new__(mcls, name, bases, dct):
cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
assert name not in allParams
allParams[name] = cls
return cls
# Dummy base class to identify types that are legitimate for SimObject
# parameters.
class ParamValue(object):
__metaclass__ = MetaParamValue
cmd_line_settable = False
# Generate the code needed as a prerequisite for declaring a C++
# object of this type. Typically generates one or more #include
# statements. Used when declaring parameters of this type.
@classmethod
def cxx_predecls(cls, code):
pass
# Generate the code needed as a prerequisite for including a
# reference to a C++ object of this type in a SWIG .i file.
# Typically generates one or more %import or %include statements.
@classmethod
def swig_predecls(cls, code):
pass
# 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 kwargs.has_key('desc'):
assert(not hasattr(self, 'desc'))
self.desc = kwargs['desc']
del kwargs['desc']
if kwargs.has_key('default'):
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':
ptype = SimObject.allClasses[self.ptype_str]
assert isSimObjectClass(ptype)
self.ptype = ptype
return ptype
raise AttributeError, "'%s' object has no attribute '%s'" % \
(type(self).__name__, attr)
def example_str(self):
if hasattr(self.ptype, "ex_str"):
return self.ptype.ex_str
else:
return self.ptype_str
# Is the param available to be exposed on the command line
def isCmdLineSettable(self):
if hasattr(self.ptype, "cmd_line_settable"):
return self.ptype.cmd_line_settable
else:
return False
def convert(self, value):
if isinstance(value, proxy.BaseProxy):
value.set_param_desc(self)
return value
if not hasattr(self, 'ptype') and isNullPointer(value):
# 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 swig_predecls(self, code):
self.ptype.swig_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.AllProxy):
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 reduce(lambda x,y: x and y, [v.has_parent() for v in self])
# 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 swig_module_name(self):
return "%s_vector" % self.ptype_str
def swig_predecls(self, code):
code('%import "${{self.swig_module_name()}}.i"')
def swig_decl(self, code):
code('%module(package="m5.internal") ${{self.swig_module_name()}}')
code('%{')
self.ptype.cxx_predecls(code)
code('%}')
code()
# Make sure the SWIGPY_SLICE_ARG is defined through this inclusion
code('%include "std_container.i"')
code()
self.ptype.swig_predecls(code)
code()
code('%include "std_vector.i"')
code()
ptype = self.ptype_str
cxx_type = self.ptype.cxx_type
code('%template(vector_$ptype) std::vector< $cxx_type >;')
def cxx_predecls(self, code):
code('#include <vector>')
self.ptype.cxx_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>')
@classmethod
def swig_predecls(cls, code):
code('%include "std_string.i"')
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):
def __str__(self):
return str(self.value)
def __float__(self):
return float(self.value)
def __long__(self):
return long(self.value)
def __int__(self):
return int(self.value)
# hook for bounds checking
def _check(self):
return
def __mul__(self, other):
newobj = self.__class__(self)
newobj.value *= other
newobj._check()
return newobj
__rmul__ = __mul__
def __div__(self, other):
newobj = self.__class__(self)
newobj.value /= other
newobj._check()
return newobj
def __sub__(self, other):
newobj = self.__class__(self)
newobj.value -= other
newobj._check()
return newobj
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, long, float, NumericParamValue)):
self.value = long(value)
else:
raise TypeError, "Can't convert object of type %s to CheckedInt" \
% type(value).__name__
self._check()
def __call__(self, value):
self.__init__(value)
return value
@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)
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.internal.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'
cmdLineSettable = True
def __init__(self, value):
if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
self.value = float(value)
else:
raise TypeError, "Can't convert object of type %s to Float" \
% type(value).__name__
def __call__(self, value):
self.__init__(value)
return value
def getValue(self):
return float(self.value)
def config_value(self):
return self
@classmethod
def cxx_ini_predecls(cls, code):
code('#include <sstream>')
@classmethod
def cxx_ini_parse(self, code, src, dest, ret):
code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
class MemorySize(CheckedInt):
cxx_type = 'uint64_t'
ex_str = '512MB'
size = 64
unsigned = True
def __init__(self, value):
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 = '512MB'
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 "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, 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'
def __init__(self, *args, **kwargs):
# Disable interleaving by default
self.intlvHighBit = 0
self.intlvBits = 0
self.intlvMatch = 0
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')) - 1
else:
raise TypeError, "Either end or size must be specified"
# Now on to the optional bit
if 'intlvHighBit' in kwargs:
self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
if 'intlvBits' in kwargs:
self.intlvBits = int(kwargs.pop('intlvBits'))
if 'intlvMatch' in kwargs:
self.intlvMatch = int(kwargs.pop('intlvMatch'))
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]) - 1
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" % kwargs.keys()
def __str__(self):
return '%s:%s' % (self.start, self.end)
def size(self):
# Divide the size by the size of the interleaving slice
return (long(self.end) - long(self.start) + 1) >> self.intlvBits
@classmethod
def cxx_predecls(cls, code):
Addr.cxx_predecls(code)
code('#include "base/addr_range.hh"')
@classmethod
def swig_predecls(cls, code):
Addr.swig_predecls(code)
@classmethod
def cxx_ini_predecls(cls, code):
code('#include <sstream>')
@classmethod
def cxx_ini_parse(cls, code, src, dest, ret):
code('uint64_t _start, _end;')
code('char _sep;')
code('std::istringstream _stream(${src});')
code('_stream >> _start;')
code('_stream.get(_sep);')
code('_stream >> _end;')
code('bool _ret = !_stream.fail() &&'
'_stream.eof() && _sep == \':\';')
code('if (_ret)')
code(' ${dest} = AddrRange(_start, _end);')
code('${ret} _ret;')
def getValue(self):
# Go from the Python class to the wrapped C++ class generated
# by swig
from m5.internal.range import AddrRange
return AddrRange(long(self.start), long(self.end),
int(self.intlvHighBit), int(self.intlvBits),
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 __nonzero__(self):
return bool(self.value)
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[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"')
@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"
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.internal.params import EthAddr
return EthAddr(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
else:
try:
self.ip = convert.toIpAddress(value)
except TypeError:
self.ip = long(value)
self.verifyIp()
def __call__(self, value):
self.__init__(value)
return value
def __str__(self):
tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)]
return '%d.%d.%d.%d' % tuple(tup)
def __eq__(self, other):
if isinstance(other, IpAddress):
return self.ip == other.ip
elif isinstance(other, str):
try:
return self.ip == convert.toIpAddress(other)
except:
return False
else:
return self.ip == other
def __ne__(self, other):
return not (self == other)
def verifyIp(self):
if self.ip < 0 or self.ip >= (1 << 32):
raise TypeError, "invalid ip address %#08x" % self.ip
def getValue(self):
from m5.internal.params 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"')
@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:
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" % 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.internal.params 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"')
@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:
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" % 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.internal.params import IpWithPort
return IpWithPort(self.ip, self.port)
time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
"%a %b %d %H:%M:%S %Y",
"%Y/%m/%d %H:%M:%S",
"%Y/%m/%d %H:%M",
"%Y/%m/%d",
"%m/%d/%Y %H:%M:%S",
"%m/%d/%Y %H:%M",
"%m/%d/%Y",
"%m/%d/%y %H:%M:%S",
"%m/%d/%y %H:%M",
"%m/%d/%y"]
def parse_time(value):
from time import gmtime, strptime, struct_time, time
from datetime import datetime, date
if isinstance(value, struct_time):
return value
if isinstance(value, (int, long)):
return gmtime(value)
if isinstance(value, (datetime, date)):
return value.timetuple()
if isinstance(value, str):
if value in ('Now', 'Today'):
return time.gmtime(time.time())
for format in time_formats:
try:
return strptime(value, format)
except ValueError:
pass
raise ValueError, "Could not parse '%s' as a time" % value
class Time(ParamValue):
cxx_type = 'tm'
@classmethod
def cxx_predecls(cls, code):
code('#include <time.h>')
@classmethod
def swig_predecls(cls, code):
code('%include "python/swig/time.i"')
def __init__(self, value):
self.value = parse_time(value)
def __call__(self, value):
self.__init__(value)
return 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;
# Python starts at 1, Unix starts at 0
c_time.tm_yday = py_time.tm_yday - 1;
return c_time
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):
assert name not in allEnums
cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
allEnums[name] = cls
return cls
def __init__(cls, name, bases, init_dict):
if init_dict.has_key('map'):
if not isinstance(cls.map, dict):
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.sort()
elif init_dict.has_key('vals'):
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'"
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
$wrapper $wrapper_name {
enum $name {
''')
code.indent(2)
for val in cls.vals:
code('$val = ${{cls.map[val]}},')
code('Num_$name = ${{len(cls.vals)}}')
code.dedent(2)
code(' };')
if cls.wrapper_is_struct:
code(' static const char *${name}Strings[Num_${name}];')
code('};')
else:
code('extern const char *${name}Strings[Num_${name}];')
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:
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:
code('} // namespace $wrapper_name')
code.dedent(1)
def swig_decl(cls, code):
name = cls.__name__
code('''\
%module(package="m5.internal") enum_$name
%{
#include "enums/$name.hh"
%}
%include "enums/$name.hh"
''')
# 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
# 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 swig_predecls(cls, code):
code('%import "python/m5/internal/enum_$0.i"', cls.__name__)
@classmethod
def cxx_ini_parse(cls, code, src, dest, ret):
code('if (false) {')
for elem_name in cls.map.iterkeys():
code('} else if (%s == "%s") {' % (src, elem_name))
code.indent()
code('%s = Enums::%s;' % (dest, elem_name))
code('%s true;' % ret)
code.dedent()
code('} else {')
code(' %s false;' % ret)
code('}')
def getValue(self):
return int(self.map[self.value])
def __str__(self):
return self.value
# 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 <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 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:
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 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, 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,ParamValue):
cxx_type = 'double'
ex_str = "1V"
cmd_line_settable = False
def __new__(cls, value):
# convert to voltage
val = convert.toVoltage(value)
return super(cls, Voltage).__new__(cls, val)
def __call__(self, value):
val = convert.toVoltage(value)
self.__init__(val)
return value
def __str__(self):
return str(self.getValue())
def getValue(self):
value = float(self)
return value
def ini_str(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 Current(float, ParamValue):
cxx_type = 'double'
ex_str = "1mA"
cmd_line_settable = False
def __new__(cls, value):
# convert to current
val = convert.toCurrent(value)
return super(cls, Current).__new__(cls, val)
def __call__(self, value):
val = convert.toCurrent(value)
self.__init__(val)
return value
def __str__(self):
return str(self.getValue())
def getValue(self):
value = float(self)
return value
def ini_str(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 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 = "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 __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
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 __str__(self):
return 'Null'
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):
assert(isSimObject(simobj) or isSimObjectClass(simobj))
self.simobj = simobj
self.name = name
self.role = role
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)}
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())
elif isinstance(other, PortRef):
if other.peer is not self:
other.connect(self)
else:
raise TypeError, \
"assigning non-port reference '%s' to port '%s'" \
% (other, self)
# Allow a master/slave 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_master_peer, new_slave_peer):
if self.peer and not proxy.isproxy(self.peer):
if isinstance(new_master_peer, PortRef) and \
isinstance(new_slave_peer, PortRef):
old_peer = self.peer
if self.role == 'SLAVE':
self.peer = new_master_peer
old_peer.peer = new_slave_peer
new_master_peer.connect(self)
new_slave_peer.connect(old_peer)
elif self.role == 'MASTER':
self.peer = new_slave_peer
old_peer.peer = new_master_peer
new_slave_peer.connect(self)
new_master_peer.connect(old_peer)
else:
panic("Port %s has unknown role, "+\
"cannot splice in new peers\n", self)
else:
raise TypeError, \
"Splicing non-port references '%s','%s' to port '%s'"\
% (new_peer, peers_new_peer, self)
else:
fatal("Port %s not connected, cannot splice in new peers\n", self)
def clone(self, simobj, memo):
if memo.has_key(self):
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):
from m5.internal.pyobject import connectPorts
if self.role == 'SLAVE':
# do nothing and let the master take care of it
return
if self.ccConnected: # already done this
return
peer = self.peer
if not self.peer: # nothing to connect to
return
# check that we connect a master to a slave
if self.role == peer.role:
raise TypeError, \
"cannot connect '%s' and '%s' due to identical role '%s'" \
% (peer, self, self.role)
try:
# self is always the master and peer the slave
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
self.ccConnected = True
peer.ccConnected = True
# A reference to an individual element of a VectorPort... much like a
# PortRef, but has an index.
class VectorPortElementRef(PortRef):
def __init__(self, simobj, name, role, index):
PortRef.__init__(self, simobj, name, role)
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):
assert(isSimObject(simobj) or isSimObjectClass(simobj))
self.simobj = simobj
self.name = name
self.role = role
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]}
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, 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 memo.has_key(self):
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):
# 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)
# 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
# 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;')
class MasterPort(Port):
# MasterPort("description")
def __init__(self, *args):
if len(args) == 1:
self.desc = args[0]
self.role = 'MASTER'
else:
raise TypeError, 'wrong number of arguments'
class SlavePort(Port):
# SlavePort("description")
def __init__(self, *args):
if len(args) == 1:
self.desc = args[0]
self.role = 'SLAVE'
else:
raise TypeError, 'wrong number of arguments'
# VectorPort description object. Like Port, but represents a vector
# of connections (e.g., as on a XBar).
class VectorPort(Port):
def __init__(self, *args):
self.isVec = True
def makeRef(self, simobj):
return VectorPortRef(simobj, self.name, self.role)
class VectorMasterPort(VectorPort):
# VectorMasterPort("description")
def __init__(self, *args):
if len(args) == 1:
self.desc = args[0]
self.role = 'MASTER'
VectorPort.__init__(self, *args)
else:
raise TypeError, 'wrong number of arguments'
class VectorSlavePort(VectorPort):
# VectorSlavePort("description")
def __init__(self, *args):
if len(args) == 1:
self.desc = args[0]
self.role = 'SLAVE'
VectorPort.__init__(self, *args)
else:
raise TypeError, 'wrong number of arguments'
# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
# proxy objects (via set_param_desc()) so that proxy error messages
# make sense.
class PortParamDesc(object):
__metaclass__ = Singleton
ptype_str = 'Port'
ptype = Port
baseEnums = allEnums.copy()
baseParams = allParams.copy()
def clear():
global allEnums, allParams
allEnums = baseEnums.copy()
allParams = baseParams.copy()
__all__ = ['Param', 'VectorParam',
'Enum', 'Bool', 'String', 'Float',
'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',
'NetworkBandwidth', 'MemoryBandwidth',
'AddrRange',
'MaxAddr', 'MaxTick', 'AllMemory',
'Time',
'NextEthernetAddr', 'NULL',
'MasterPort', 'SlavePort',
'VectorMasterPort', 'VectorSlavePort']
import SimObject