params.py revision 4446
14680Sgblack@eecs.umich.edu# Copyright (c) 2004-2006 The Regents of The University of Michigan 25442Sgblack@eecs.umich.edu# All rights reserved. 34680Sgblack@eecs.umich.edu# 44680Sgblack@eecs.umich.edu# Redistribution and use in source and binary forms, with or without 54680Sgblack@eecs.umich.edu# modification, are permitted provided that the following conditions are 64680Sgblack@eecs.umich.edu# met: redistributions of source code must retain the above copyright 74680Sgblack@eecs.umich.edu# notice, this list of conditions and the following disclaimer; 84680Sgblack@eecs.umich.edu# redistributions in binary form must reproduce the above copyright 94680Sgblack@eecs.umich.edu# notice, this list of conditions and the following disclaimer in the 104680Sgblack@eecs.umich.edu# documentation and/or other materials provided with the distribution; 114680Sgblack@eecs.umich.edu# neither the name of the copyright holders nor the names of its 124680Sgblack@eecs.umich.edu# contributors may be used to endorse or promote products derived from 134680Sgblack@eecs.umich.edu# this software without specific prior written permission. 144680Sgblack@eecs.umich.edu# 154680Sgblack@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 164680Sgblack@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 174680Sgblack@eecs.umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 184680Sgblack@eecs.umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 194680Sgblack@eecs.umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 204680Sgblack@eecs.umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 214680Sgblack@eecs.umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 224680Sgblack@eecs.umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 234680Sgblack@eecs.umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 244680Sgblack@eecs.umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 254680Sgblack@eecs.umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 264680Sgblack@eecs.umich.edu# 274680Sgblack@eecs.umich.edu# Authors: Steve Reinhardt 284680Sgblack@eecs.umich.edu# Nathan Binkert 294680Sgblack@eecs.umich.edu 304680Sgblack@eecs.umich.edu##################################################################### 314680Sgblack@eecs.umich.edu# 324680Sgblack@eecs.umich.edu# Parameter description classes 334680Sgblack@eecs.umich.edu# 344680Sgblack@eecs.umich.edu# The _params dictionary in each class maps parameter names to either 356215Snate@binkert.org# a Param or a VectorParam object. These objects contain the 364680Sgblack@eecs.umich.edu# parameter description string, the parameter type, and the default 375543Ssaidi@eecs.umich.edu# value (if any). The convert() method on these objects is used to 384680Sgblack@eecs.umich.edu# force whatever value is assigned to the parameter to the appropriate 394680Sgblack@eecs.umich.edu# type. 404680Sgblack@eecs.umich.edu# 414680Sgblack@eecs.umich.edu# Note that the default values are loaded into the class's attribute 424680Sgblack@eecs.umich.edu# space when the parameter dictionary is initialized (in 434680Sgblack@eecs.umich.edu# MetaSimObject._new_param()); after that point they aren't used. 444680Sgblack@eecs.umich.edu# 454680Sgblack@eecs.umich.edu##################################################################### 464680Sgblack@eecs.umich.edu 474680Sgblack@eecs.umich.eduimport copy 484680Sgblack@eecs.umich.eduimport datetime 494680Sgblack@eecs.umich.eduimport inspect 504680Sgblack@eecs.umich.eduimport sys 514680Sgblack@eecs.umich.eduimport time 524680Sgblack@eecs.umich.edu 534680Sgblack@eecs.umich.eduimport convert 544680Sgblack@eecs.umich.eduimport proxy 554680Sgblack@eecs.umich.eduimport ticks 564680Sgblack@eecs.umich.edufrom util import * 574680Sgblack@eecs.umich.edu 584680Sgblack@eecs.umich.edu# Dummy base class to identify types that are legitimate for SimObject 594680Sgblack@eecs.umich.edu# parameters. 604680Sgblack@eecs.umich.educlass ParamValue(object): 614680Sgblack@eecs.umich.edu 624680Sgblack@eecs.umich.edu cxx_predecls = [] 634680Sgblack@eecs.umich.edu swig_predecls = [] 644680Sgblack@eecs.umich.edu 654680Sgblack@eecs.umich.edu # default for printing to .ini file is regular string conversion. 664680Sgblack@eecs.umich.edu # will be overridden in some cases 674680Sgblack@eecs.umich.edu def ini_str(self): 684680Sgblack@eecs.umich.edu return str(self) 694680Sgblack@eecs.umich.edu 704680Sgblack@eecs.umich.edu # allows us to blithely call unproxy() on things without checking 714680Sgblack@eecs.umich.edu # if they're really proxies or not 724680Sgblack@eecs.umich.edu def unproxy(self, base): 734680Sgblack@eecs.umich.edu return self 744680Sgblack@eecs.umich.edu 754680Sgblack@eecs.umich.edu# Regular parameter description. 764680Sgblack@eecs.umich.educlass ParamDesc(object): 774680Sgblack@eecs.umich.edu def __init__(self, ptype_str, ptype, *args, **kwargs): 784680Sgblack@eecs.umich.edu self.ptype_str = ptype_str 794680Sgblack@eecs.umich.edu # remember ptype only if it is provided 804680Sgblack@eecs.umich.edu if ptype != None: 814680Sgblack@eecs.umich.edu self.ptype = ptype 824680Sgblack@eecs.umich.edu 834680Sgblack@eecs.umich.edu if args: 844680Sgblack@eecs.umich.edu if len(args) == 1: 854680Sgblack@eecs.umich.edu self.desc = args[0] 864680Sgblack@eecs.umich.edu elif len(args) == 2: 874680Sgblack@eecs.umich.edu self.default = args[0] 8810289SAndreas.Sandberg@ARM.com self.desc = args[1] 8910289SAndreas.Sandberg@ARM.com else: 9010289SAndreas.Sandberg@ARM.com raise TypeError, 'too many arguments' 914680Sgblack@eecs.umich.edu 926249Sgblack@eecs.umich.edu if kwargs.has_key('desc'): 934680Sgblack@eecs.umich.edu assert(not hasattr(self, 'desc')) 944680Sgblack@eecs.umich.edu self.desc = kwargs['desc'] 954680Sgblack@eecs.umich.edu del kwargs['desc'] 964680Sgblack@eecs.umich.edu 974680Sgblack@eecs.umich.edu if kwargs.has_key('default'): 984680Sgblack@eecs.umich.edu assert(not hasattr(self, 'default')) 994680Sgblack@eecs.umich.edu self.default = kwargs['default'] 1004680Sgblack@eecs.umich.edu del kwargs['default'] 1014680Sgblack@eecs.umich.edu 1024680Sgblack@eecs.umich.edu if kwargs: 1034680Sgblack@eecs.umich.edu raise TypeError, 'extra unknown kwargs %s' % kwargs 1044680Sgblack@eecs.umich.edu 1054680Sgblack@eecs.umich.edu if not hasattr(self, 'desc'): 1064680Sgblack@eecs.umich.edu raise TypeError, 'desc attribute missing' 1074680Sgblack@eecs.umich.edu 1084680Sgblack@eecs.umich.edu def __getattr__(self, attr): 1094680Sgblack@eecs.umich.edu if attr == 'ptype': 1104680Sgblack@eecs.umich.edu try: 1114680Sgblack@eecs.umich.edu ptype = eval(self.ptype_str, objects.__dict__) 1124680Sgblack@eecs.umich.edu if not isinstance(ptype, type): 1134680Sgblack@eecs.umich.edu raise NameError 1144680Sgblack@eecs.umich.edu self.ptype = ptype 1154680Sgblack@eecs.umich.edu return ptype 1164680Sgblack@eecs.umich.edu except NameError: 1174680Sgblack@eecs.umich.edu raise TypeError, \ 1184680Sgblack@eecs.umich.edu "Param qualifier '%s' is not a type" % self.ptype_str 1194680Sgblack@eecs.umich.edu raise AttributeError, "'%s' object has no attribute '%s'" % \ 1204680Sgblack@eecs.umich.edu (type(self).__name__, attr) 1214680Sgblack@eecs.umich.edu 1224680Sgblack@eecs.umich.edu def convert(self, value): 1236249Sgblack@eecs.umich.edu if isinstance(value, proxy.BaseProxy): 1244680Sgblack@eecs.umich.edu value.set_param_desc(self) 1254680Sgblack@eecs.umich.edu return value 1264680Sgblack@eecs.umich.edu if not hasattr(self, 'ptype') and isNullPointer(value): 1274680Sgblack@eecs.umich.edu # deferred evaluation of SimObject; continue to defer if 1284680Sgblack@eecs.umich.edu # we're just assigning a null pointer 1294680Sgblack@eecs.umich.edu return value 1304680Sgblack@eecs.umich.edu if isinstance(value, self.ptype): 1314680Sgblack@eecs.umich.edu return value 1324680Sgblack@eecs.umich.edu if isNullPointer(value) and isSimObjectClass(self.ptype): 1334680Sgblack@eecs.umich.edu return value 1344680Sgblack@eecs.umich.edu return self.ptype(value) 1354680Sgblack@eecs.umich.edu 1364680Sgblack@eecs.umich.edu def cxx_predecls(self): 1374680Sgblack@eecs.umich.edu return self.ptype.cxx_predecls 1384680Sgblack@eecs.umich.edu 1394680Sgblack@eecs.umich.edu def swig_predecls(self): 1404680Sgblack@eecs.umich.edu return self.ptype.swig_predecls 1414680Sgblack@eecs.umich.edu 1426249Sgblack@eecs.umich.edu def cxx_decl(self): 1434680Sgblack@eecs.umich.edu return '%s %s;' % (self.ptype.cxx_type, self.name) 1444680Sgblack@eecs.umich.edu 1454680Sgblack@eecs.umich.edu# Vector-valued parameter description. Just like ParamDesc, except 1464680Sgblack@eecs.umich.edu# that the value is a vector (list) of the specified type instead of a 1474680Sgblack@eecs.umich.edu# single value. 1484680Sgblack@eecs.umich.edu 1494680Sgblack@eecs.umich.educlass VectorParamValue(list): 1504680Sgblack@eecs.umich.edu def ini_str(self): 1514680Sgblack@eecs.umich.edu return ' '.join([v.ini_str() for v in self]) 1524680Sgblack@eecs.umich.edu 1534680Sgblack@eecs.umich.edu def unproxy(self, base): 1544680Sgblack@eecs.umich.edu return [v.unproxy(base) for v in self] 1554680Sgblack@eecs.umich.edu 1564680Sgblack@eecs.umich.educlass SimObjVector(VectorParamValue): 1574680Sgblack@eecs.umich.edu def print_ini(self): 1584680Sgblack@eecs.umich.edu for v in self: 1594680Sgblack@eecs.umich.edu v.print_ini() 1604680Sgblack@eecs.umich.edu 1614680Sgblack@eecs.umich.educlass VectorParamDesc(ParamDesc): 1624680Sgblack@eecs.umich.edu # Convert assigned value to appropriate type. If the RHS is not a 1634680Sgblack@eecs.umich.edu # list or tuple, it generates a single-element list. 1644680Sgblack@eecs.umich.edu def convert(self, value): 1654680Sgblack@eecs.umich.edu if isinstance(value, (list, tuple)): 1664680Sgblack@eecs.umich.edu # list: coerce each element into new list 1674680Sgblack@eecs.umich.edu tmp_list = [ ParamDesc.convert(self, v) for v in value ] 1684680Sgblack@eecs.umich.edu if isSimObjectSequence(tmp_list): 1694680Sgblack@eecs.umich.edu return SimObjVector(tmp_list) 1704680Sgblack@eecs.umich.edu else: 1714680Sgblack@eecs.umich.edu return VectorParamValue(tmp_list) 1724680Sgblack@eecs.umich.edu else: 1736249Sgblack@eecs.umich.edu # singleton: leave it be (could coerce to a single-element 1744680Sgblack@eecs.umich.edu # list here, but for some historical reason we don't... 1754680Sgblack@eecs.umich.edu return ParamDesc.convert(self, value) 1764680Sgblack@eecs.umich.edu 1774680Sgblack@eecs.umich.edu def cxx_predecls(self): 1784680Sgblack@eecs.umich.edu return ['#include <vector>'] + self.ptype.cxx_predecls 1794680Sgblack@eecs.umich.edu 1804680Sgblack@eecs.umich.edu def swig_predecls(self): 1814680Sgblack@eecs.umich.edu return ['%include "std_vector.i"'] + self.ptype.swig_predecls 1824680Sgblack@eecs.umich.edu 1834680Sgblack@eecs.umich.edu def cxx_decl(self): 1844680Sgblack@eecs.umich.edu return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) 1854680Sgblack@eecs.umich.edu 1864680Sgblack@eecs.umich.educlass ParamFactory(object): 1874680Sgblack@eecs.umich.edu def __init__(self, param_desc_class, ptype_str = None): 1884680Sgblack@eecs.umich.edu self.param_desc_class = param_desc_class 1894680Sgblack@eecs.umich.edu self.ptype_str = ptype_str 1904680Sgblack@eecs.umich.edu 1914680Sgblack@eecs.umich.edu def __getattr__(self, attr): 1924680Sgblack@eecs.umich.edu if self.ptype_str: 1934680Sgblack@eecs.umich.edu attr = self.ptype_str + '.' + attr 1944680Sgblack@eecs.umich.edu return ParamFactory(self.param_desc_class, attr) 1954680Sgblack@eecs.umich.edu 1964680Sgblack@eecs.umich.edu # E.g., Param.Int(5, "number of widgets") 1974680Sgblack@eecs.umich.edu def __call__(self, *args, **kwargs): 1984680Sgblack@eecs.umich.edu caller_frame = inspect.currentframe().f_back 1995136Sgblack@eecs.umich.edu ptype = None 2004681Sgblack@eecs.umich.edu try: 2014681Sgblack@eecs.umich.edu ptype = eval(self.ptype_str, 2024681Sgblack@eecs.umich.edu caller_frame.f_globals, caller_frame.f_locals) 2034681Sgblack@eecs.umich.edu if not isinstance(ptype, type): 2044681Sgblack@eecs.umich.edu raise TypeError, \ 2054681Sgblack@eecs.umich.edu "Param qualifier is not a type: %s" % ptype 2066249Sgblack@eecs.umich.edu except NameError: 2074680Sgblack@eecs.umich.edu # if name isn't defined yet, assume it's a SimObject, and 2084680Sgblack@eecs.umich.edu # try to resolve it later 2094680Sgblack@eecs.umich.edu pass 2104680Sgblack@eecs.umich.edu return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 2114680Sgblack@eecs.umich.edu 2125136Sgblack@eecs.umich.eduParam = ParamFactory(ParamDesc) 2134680Sgblack@eecs.umich.eduVectorParam = ParamFactory(VectorParamDesc) 2144680Sgblack@eecs.umich.edu 2154680Sgblack@eecs.umich.edu##################################################################### 2164680Sgblack@eecs.umich.edu# 2174680Sgblack@eecs.umich.edu# Parameter Types 2184680Sgblack@eecs.umich.edu# 2195136Sgblack@eecs.umich.edu# Though native Python types could be used to specify parameter types 2204680Sgblack@eecs.umich.edu# (the 'ptype' field of the Param and VectorParam classes), it's more 2214680Sgblack@eecs.umich.edu# flexible to define our own set of types. This gives us more control 2224680Sgblack@eecs.umich.edu# over how Python expressions are converted to values (via the 2234680Sgblack@eecs.umich.edu# __init__() constructor) and how these values are printed out (via 2244680Sgblack@eecs.umich.edu# the __str__() conversion method). 2255136Sgblack@eecs.umich.edu# 2264680Sgblack@eecs.umich.edu##################################################################### 2274680Sgblack@eecs.umich.edu 2284680Sgblack@eecs.umich.edu# String-valued parameter. Just mixin the ParamValue class with the 2294680Sgblack@eecs.umich.edu# built-in str class. 2304680Sgblack@eecs.umich.educlass String(ParamValue,str): 2314680Sgblack@eecs.umich.edu cxx_type = 'std::string' 2324680Sgblack@eecs.umich.edu cxx_predecls = ['#include <string>'] 2334680Sgblack@eecs.umich.edu swig_predecls = ['%include "std_string.i"\n' + 2344680Sgblack@eecs.umich.edu '%apply const std::string& {std::string *};'] 2354680Sgblack@eecs.umich.edu pass 2364680Sgblack@eecs.umich.edu 2374680Sgblack@eecs.umich.edu# superclass for "numeric" parameter values, to emulate math 2384680Sgblack@eecs.umich.edu# operations in a type-safe way. e.g., a Latency times an int returns 2394680Sgblack@eecs.umich.edu# a new Latency object. 2404680Sgblack@eecs.umich.educlass NumericParamValue(ParamValue): 2414680Sgblack@eecs.umich.edu def __str__(self): 2424680Sgblack@eecs.umich.edu return str(self.value) 2434680Sgblack@eecs.umich.edu 2444680Sgblack@eecs.umich.edu def __float__(self): 2454680Sgblack@eecs.umich.edu return float(self.value) 2464680Sgblack@eecs.umich.edu 2474680Sgblack@eecs.umich.edu def __long__(self): 2485442Sgblack@eecs.umich.edu return long(self.value) 2494680Sgblack@eecs.umich.edu 2504680Sgblack@eecs.umich.edu def __int__(self): 2514680Sgblack@eecs.umich.edu return int(self.value) 2524680Sgblack@eecs.umich.edu 2534680Sgblack@eecs.umich.edu # hook for bounds checking 2544680Sgblack@eecs.umich.edu def _check(self): 2554680Sgblack@eecs.umich.edu return 2564680Sgblack@eecs.umich.edu 2574680Sgblack@eecs.umich.edu def __mul__(self, other): 2584680Sgblack@eecs.umich.edu newobj = self.__class__(self) 2594680Sgblack@eecs.umich.edu newobj.value *= other 2604680Sgblack@eecs.umich.edu newobj._check() 2614680Sgblack@eecs.umich.edu return newobj 2624680Sgblack@eecs.umich.edu 2634680Sgblack@eecs.umich.edu __rmul__ = __mul__ 2645442Sgblack@eecs.umich.edu 2655442Sgblack@eecs.umich.edu def __div__(self, other): 2664680Sgblack@eecs.umich.edu newobj = self.__class__(self) 2674680Sgblack@eecs.umich.edu newobj.value /= other 2684680Sgblack@eecs.umich.edu newobj._check() 2694680Sgblack@eecs.umich.edu return newobj 2704680Sgblack@eecs.umich.edu 2714680Sgblack@eecs.umich.edu def __sub__(self, other): 2724680Sgblack@eecs.umich.edu newobj = self.__class__(self) 2734680Sgblack@eecs.umich.edu newobj.value -= other 2744680Sgblack@eecs.umich.edu newobj._check() 2754680Sgblack@eecs.umich.edu return newobj 2764680Sgblack@eecs.umich.edu 2774680Sgblack@eecs.umich.edu# Metaclass for bounds-checked integer parameters. See CheckedInt. 2784680Sgblack@eecs.umich.educlass CheckedIntType(type): 2794680Sgblack@eecs.umich.edu def __init__(cls, name, bases, dict): 2804680Sgblack@eecs.umich.edu super(CheckedIntType, cls).__init__(name, bases, dict) 2814680Sgblack@eecs.umich.edu 2824680Sgblack@eecs.umich.edu # CheckedInt is an abstract base class, so we actually don't 2834680Sgblack@eecs.umich.edu # want to do any processing on it... the rest of this code is 2844680Sgblack@eecs.umich.edu # just for classes that derive from CheckedInt. 2854680Sgblack@eecs.umich.edu if name == 'CheckedInt': 2866249Sgblack@eecs.umich.edu return 2874680Sgblack@eecs.umich.edu 2884680Sgblack@eecs.umich.edu if not cls.cxx_predecls: 2894680Sgblack@eecs.umich.edu # most derived types require this, so we just do it here once 2904698Sgblack@eecs.umich.edu cls.cxx_predecls = ['#include "sim/host.hh"'] 2914680Sgblack@eecs.umich.edu 2924680Sgblack@eecs.umich.edu if not cls.swig_predecls: 2934680Sgblack@eecs.umich.edu # most derived types require this, so we just do it here once 2944680Sgblack@eecs.umich.edu cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 2954680Sgblack@eecs.umich.edu '%import "sim/host.hh"'] 2964680Sgblack@eecs.umich.edu 2974680Sgblack@eecs.umich.edu if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 2984680Sgblack@eecs.umich.edu if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 2994680Sgblack@eecs.umich.edu panic("CheckedInt subclass %s must define either\n" \ 3004680Sgblack@eecs.umich.edu " 'min' and 'max' or 'size' and 'unsigned'\n" \ 3014680Sgblack@eecs.umich.edu % name); 3024680Sgblack@eecs.umich.edu if cls.unsigned: 3034680Sgblack@eecs.umich.edu cls.min = 0 3044680Sgblack@eecs.umich.edu cls.max = 2 ** cls.size - 1 3054680Sgblack@eecs.umich.edu else: 3064680Sgblack@eecs.umich.edu cls.min = -(2 ** (cls.size - 1)) 3074680Sgblack@eecs.umich.edu cls.max = (2 ** (cls.size - 1)) - 1 3084680Sgblack@eecs.umich.edu 3094680Sgblack@eecs.umich.edu# Abstract superclass for bounds-checked integer parameters. This 3104680Sgblack@eecs.umich.edu# class is subclassed to generate parameter classes with specific 3114680Sgblack@eecs.umich.edu# bounds. Initialization of the min and max bounds is done in the 3124680Sgblack@eecs.umich.edu# metaclass CheckedIntType.__init__. 313class CheckedInt(NumericParamValue): 314 __metaclass__ = CheckedIntType 315 316 def _check(self): 317 if not self.min <= self.value <= self.max: 318 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 319 (self.min, self.value, self.max) 320 321 def __init__(self, value): 322 if isinstance(value, str): 323 self.value = convert.toInteger(value) 324 elif isinstance(value, (int, long, float, NumericParamValue)): 325 self.value = long(value) 326 else: 327 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 328 % type(value).__name__ 329 self._check() 330 331class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 332class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 333 334class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 335class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 336class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 337class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 338class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 339class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 340class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 341class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 342 343class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 344class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 345class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 346class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 347 348class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 349 350class Float(ParamValue, float): 351 cxx_type = 'double' 352 353class MemorySize(CheckedInt): 354 cxx_type = 'uint64_t' 355 size = 64 356 unsigned = True 357 def __init__(self, value): 358 if isinstance(value, MemorySize): 359 self.value = value.value 360 else: 361 self.value = convert.toMemorySize(value) 362 self._check() 363 364class MemorySize32(CheckedInt): 365 cxx_type = 'uint32_t' 366 size = 32 367 unsigned = True 368 def __init__(self, value): 369 if isinstance(value, MemorySize): 370 self.value = value.value 371 else: 372 self.value = convert.toMemorySize(value) 373 self._check() 374 375class Addr(CheckedInt): 376 cxx_type = 'Addr' 377 cxx_predecls = ['#include "targetarch/isa_traits.hh"'] 378 size = 64 379 unsigned = True 380 def __init__(self, value): 381 if isinstance(value, Addr): 382 self.value = value.value 383 else: 384 try: 385 self.value = convert.toMemorySize(value) 386 except TypeError: 387 self.value = long(value) 388 self._check() 389 def __add__(self, other): 390 if isinstance(other, Addr): 391 return self.value + other.value 392 else: 393 return self.value + other 394 395 396class MetaRange(type): 397 def __init__(cls, name, bases, dict): 398 super(MetaRange, cls).__init__(name, bases, dict) 399 if name == 'Range': 400 return 401 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 402 cls.cxx_predecls = \ 403 ['#include "base/range.hh"'] + cls.type.cxx_predecls 404 405class Range(ParamValue): 406 __metaclass__ = MetaRange 407 type = Int # default; can be overridden in subclasses 408 def __init__(self, *args, **kwargs): 409 def handle_kwargs(self, kwargs): 410 if 'end' in kwargs: 411 self.second = self.type(kwargs.pop('end')) 412 elif 'size' in kwargs: 413 self.second = self.first + self.type(kwargs.pop('size')) - 1 414 else: 415 raise TypeError, "Either end or size must be specified" 416 417 if len(args) == 0: 418 self.first = self.type(kwargs.pop('start')) 419 handle_kwargs(self, kwargs) 420 421 elif len(args) == 1: 422 if kwargs: 423 self.first = self.type(args[0]) 424 handle_kwargs(self, kwargs) 425 elif isinstance(args[0], Range): 426 self.first = self.type(args[0].first) 427 self.second = self.type(args[0].second) 428 else: 429 self.first = self.type(0) 430 self.second = self.type(args[0]) - 1 431 432 elif len(args) == 2: 433 self.first = self.type(args[0]) 434 self.second = self.type(args[1]) 435 else: 436 raise TypeError, "Too many arguments specified" 437 438 if kwargs: 439 raise TypeError, "too many keywords: %s" % kwargs.keys() 440 441 def __str__(self): 442 return '%s:%s' % (self.first, self.second) 443 444class AddrRange(Range): 445 type = Addr 446 447class TickRange(Range): 448 type = Tick 449 450# Boolean parameter type. Python doesn't let you subclass bool, since 451# it doesn't want to let you create multiple instances of True and 452# False. Thus this is a little more complicated than String. 453class Bool(ParamValue): 454 cxx_type = 'bool' 455 def __init__(self, value): 456 try: 457 self.value = convert.toBool(value) 458 except TypeError: 459 self.value = bool(value) 460 461 def __str__(self): 462 return str(self.value) 463 464 def ini_str(self): 465 if self.value: 466 return 'true' 467 return 'false' 468 469def IncEthernetAddr(addr, val = 1): 470 bytes = map(lambda x: int(x, 16), addr.split(':')) 471 bytes[5] += val 472 for i in (5, 4, 3, 2, 1): 473 val,rem = divmod(bytes[i], 256) 474 bytes[i] = rem 475 if val == 0: 476 break 477 bytes[i - 1] += val 478 assert(bytes[0] <= 255) 479 return ':'.join(map(lambda x: '%02x' % x, bytes)) 480 481_NextEthernetAddr = "00:90:00:00:00:01" 482def NextEthernetAddr(): 483 global _NextEthernetAddr 484 485 value = _NextEthernetAddr 486 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 487 return value 488 489class EthernetAddr(ParamValue): 490 cxx_type = 'Net::EthAddr' 491 cxx_predecls = ['#include "base/inet.hh"'] 492 swig_predecls = ['class Net::EthAddr;'] 493 def __init__(self, value): 494 if value == NextEthernetAddr: 495 self.value = value 496 return 497 498 if not isinstance(value, str): 499 raise TypeError, "expected an ethernet address and didn't get one" 500 501 bytes = value.split(':') 502 if len(bytes) != 6: 503 raise TypeError, 'invalid ethernet address %s' % value 504 505 for byte in bytes: 506 if not 0 <= int(byte) <= 256: 507 raise TypeError, 'invalid ethernet address %s' % value 508 509 self.value = value 510 511 def unproxy(self, base): 512 if self.value == NextEthernetAddr: 513 return EthernetAddr(self.value()) 514 return self 515 516 def ini_str(self): 517 return self.value 518 519time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 520 "%a %b %d %H:%M:%S %Z %Y", 521 "%Y/%m/%d %H:%M:%S", 522 "%Y/%m/%d %H:%M", 523 "%Y/%m/%d", 524 "%m/%d/%Y %H:%M:%S", 525 "%m/%d/%Y %H:%M", 526 "%m/%d/%Y", 527 "%m/%d/%y %H:%M:%S", 528 "%m/%d/%y %H:%M", 529 "%m/%d/%y"] 530 531 532def parse_time(value): 533 from time import gmtime, strptime, struct_time, time 534 from datetime import datetime, date 535 536 if isinstance(value, struct_time): 537 return value 538 539 if isinstance(value, (int, long)): 540 return gmtime(value) 541 542 if isinstance(value, (datetime, date)): 543 return value.timetuple() 544 545 if isinstance(value, str): 546 if value in ('Now', 'Today'): 547 return time.gmtime(time.time()) 548 549 for format in time_formats: 550 try: 551 return strptime(value, format) 552 except ValueError: 553 pass 554 555 raise ValueError, "Could not parse '%s' as a time" % value 556 557class Time(ParamValue): 558 cxx_type = 'time_t' 559 def __init__(self, value): 560 self.value = parse_time(value) 561 562 def __str__(self): 563 tm = self.value 564 return ' '.join([ str(tm[i]) for i in xrange(8)]) 565 566 def ini_str(self): 567 return str(self) 568 569# Enumerated types are a little more complex. The user specifies the 570# type as Enum(foo) where foo is either a list or dictionary of 571# alternatives (typically strings, but not necessarily so). (In the 572# long run, the integer value of the parameter will be the list index 573# or the corresponding dictionary value. For now, since we only check 574# that the alternative is valid and then spit it into a .ini file, 575# there's not much point in using the dictionary.) 576 577# What Enum() must do is generate a new type encapsulating the 578# provided list/dictionary so that specific values of the parameter 579# can be instances of that type. We define two hidden internal 580# classes (_ListEnum and _DictEnum) to serve as base classes, then 581# derive the new type from the appropriate base class on the fly. 582 583 584# Metaclass for Enum types 585class MetaEnum(type): 586 def __init__(cls, name, bases, init_dict): 587 if init_dict.has_key('map'): 588 if not isinstance(cls.map, dict): 589 raise TypeError, "Enum-derived class attribute 'map' " \ 590 "must be of type dict" 591 # build list of value strings from map 592 cls.vals = cls.map.keys() 593 cls.vals.sort() 594 elif init_dict.has_key('vals'): 595 if not isinstance(cls.vals, list): 596 raise TypeError, "Enum-derived class attribute 'vals' " \ 597 "must be of type list" 598 # build string->value map from vals sequence 599 cls.map = {} 600 for idx,val in enumerate(cls.vals): 601 cls.map[val] = idx 602 else: 603 raise TypeError, "Enum-derived class must define "\ 604 "attribute 'map' or 'vals'" 605 606 cls.cxx_type = name + '::Enum' 607 608 super(MetaEnum, cls).__init__(name, bases, init_dict) 609 610 # Generate C++ class declaration for this enum type. 611 # Note that we wrap the enum in a class/struct to act as a namespace, 612 # so that the enum strings can be brief w/o worrying about collisions. 613 def cxx_decl(cls): 614 s = 'struct %s {\n enum Enum {\n ' % cls.__name__ 615 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 616 s += '\n };\n};\n' 617 return s 618 619# Base class for enum types. 620class Enum(ParamValue): 621 __metaclass__ = MetaEnum 622 vals = [] 623 624 def __init__(self, value): 625 if value not in self.map: 626 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 627 % (value, self.vals) 628 self.value = value 629 630 def __str__(self): 631 return self.value 632 633# how big does a rounding error need to be before we warn about it? 634frequency_tolerance = 0.001 # 0.1% 635 636class TickParamValue(NumericParamValue): 637 cxx_type = 'Tick' 638 cxx_predecls = ['#include "sim/host.hh"'] 639 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 640 '%import "sim/host.hh"'] 641 642class Latency(TickParamValue): 643 def __init__(self, value): 644 if isinstance(value, (Latency, Clock)): 645 self.ticks = value.ticks 646 self.value = value.value 647 elif isinstance(value, Frequency): 648 self.ticks = value.ticks 649 self.value = 1.0 / value.value 650 elif value.endswith('t'): 651 self.ticks = True 652 self.value = int(value[:-1]) 653 else: 654 self.ticks = False 655 self.value = convert.toLatency(value) 656 657 def __getattr__(self, attr): 658 if attr in ('latency', 'period'): 659 return self 660 if attr == 'frequency': 661 return Frequency(self) 662 raise AttributeError, "Latency object has no attribute '%s'" % attr 663 664 # convert latency to ticks 665 def ini_str(self): 666 if self.ticks or self.value == 0: 667 return '%d' % self.value 668 else: 669 return '%d' % (ticks.fromSeconds(self.value)) 670 671class Frequency(TickParamValue): 672 def __init__(self, value): 673 if isinstance(value, (Latency, Clock)): 674 if value.value == 0: 675 self.value = 0 676 else: 677 self.value = 1.0 / value.value 678 self.ticks = value.ticks 679 elif isinstance(value, Frequency): 680 self.value = value.value 681 self.ticks = value.ticks 682 else: 683 self.ticks = False 684 self.value = convert.toFrequency(value) 685 686 def __getattr__(self, attr): 687 if attr == 'frequency': 688 return self 689 if attr in ('latency', 'period'): 690 return Latency(self) 691 raise AttributeError, "Frequency object has no attribute '%s'" % attr 692 693 # convert latency to ticks 694 def ini_str(self): 695 if self.ticks or self.value == 0: 696 return '%d' % self.value 697 else: 698 return '%d' % (ticks.fromSeconds(1.0 / self.value)) 699 700# A generic frequency and/or Latency value. Value is stored as a latency, 701# but to avoid ambiguity this object does not support numeric ops (* or /). 702# An explicit conversion to a Latency or Frequency must be made first. 703class Clock(ParamValue): 704 cxx_type = 'Tick' 705 cxx_predecls = ['#include "sim/host.hh"'] 706 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 707 '%import "sim/host.hh"'] 708 def __init__(self, value): 709 if isinstance(value, (Latency, Clock)): 710 self.ticks = value.ticks 711 self.value = value.value 712 elif isinstance(value, Frequency): 713 self.ticks = value.ticks 714 self.value = 1.0 / value.value 715 elif value.endswith('t'): 716 self.ticks = True 717 self.value = int(value[:-1]) 718 else: 719 self.ticks = False 720 self.value = convert.anyToLatency(value) 721 722 def __getattr__(self, attr): 723 if attr == 'frequency': 724 return Frequency(self) 725 if attr in ('latency', 'period'): 726 return Latency(self) 727 raise AttributeError, "Frequency object has no attribute '%s'" % attr 728 729 def ini_str(self): 730 return self.period.ini_str() 731 732class NetworkBandwidth(float,ParamValue): 733 cxx_type = 'float' 734 def __new__(cls, value): 735 # convert to bits per second 736 val = convert.toNetworkBandwidth(value) 737 return super(cls, NetworkBandwidth).__new__(cls, val) 738 739 def __str__(self): 740 return str(self.val) 741 742 def ini_str(self): 743 # convert to seconds per byte 744 value = 8.0 / float(self) 745 # convert to ticks per byte 746 return '%f' % (ticks.fromSeconds(value)) 747 748class MemoryBandwidth(float,ParamValue): 749 cxx_type = 'float' 750 def __new__(self, value): 751 # we want the number of ticks per byte of data 752 val = convert.toMemoryBandwidth(value) 753 return super(cls, MemoryBandwidth).__new__(cls, val) 754 755 def __str__(self): 756 return str(self.val) 757 758 def ini_str(self): 759 # convert to seconds per byte 760 value = 1.0 / float(self) 761 # convert to ticks per byte 762 return '%f' % (ticks.fromSeconds(value)) 763 764# 765# "Constants"... handy aliases for various values. 766# 767 768# Special class for NULL pointers. Note the special check in 769# make_param_value() above that lets these be assigned where a 770# SimObject is required. 771# only one copy of a particular node 772class NullSimObject(object): 773 __metaclass__ = Singleton 774 775 def __call__(cls): 776 return cls 777 778 def _instantiate(self, parent = None, path = ''): 779 pass 780 781 def ini_str(self): 782 return 'Null' 783 784 def unproxy(self, base): 785 return self 786 787 def set_path(self, parent, name): 788 pass 789 def __str__(self): 790 return 'Null' 791 792# The only instance you'll ever need... 793NULL = NullSimObject() 794 795def isNullPointer(value): 796 return isinstance(value, NullSimObject) 797 798# Some memory range specifications use this as a default upper bound. 799MaxAddr = Addr.max 800MaxTick = Tick.max 801AllMemory = AddrRange(0, MaxAddr) 802 803 804##################################################################### 805# 806# Port objects 807# 808# Ports are used to interconnect objects in the memory system. 809# 810##################################################################### 811 812# Port reference: encapsulates a reference to a particular port on a 813# particular SimObject. 814class PortRef(object): 815 def __init__(self, simobj, name): 816 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 817 self.simobj = simobj 818 self.name = name 819 self.peer = None # not associated with another port yet 820 self.ccConnected = False # C++ port connection done? 821 self.index = -1 # always -1 for non-vector ports 822 823 def __str__(self): 824 return '%s.%s' % (self.simobj, self.name) 825 826 # for config.ini, print peer's name (not ours) 827 def ini_str(self): 828 return str(self.peer) 829 830 def __getattr__(self, attr): 831 if attr == 'peerObj': 832 # shorthand for proxies 833 return self.peer.simobj 834 raise AttributeError, "'%s' object has no attribute '%s'" % \ 835 (self.__class__.__name__, attr) 836 837 # Full connection is symmetric (both ways). Called via 838 # SimObject.__setattr__ as a result of a port assignment, e.g., 839 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 840 # e.g., "obj1.portA[3] = obj2.portB". 841 def connect(self, other): 842 if isinstance(other, VectorPortRef): 843 # reference to plain VectorPort is implicit append 844 other = other._get_next() 845 if self.peer and not proxy.isproxy(self.peer): 846 print "warning: overwriting port", self, \ 847 "value", self.peer, "with", other 848 self.peer = other 849 if proxy.isproxy(other): 850 other.set_param_desc(PortParamDesc()) 851 elif isinstance(other, PortRef): 852 if other.peer is not self: 853 other.connect(self) 854 else: 855 raise TypeError, \ 856 "assigning non-port reference '%s' to port '%s'" \ 857 % (other, self) 858 859 def clone(self, simobj, memo): 860 if memo.has_key(self): 861 return memo[self] 862 newRef = copy.copy(self) 863 memo[self] = newRef 864 newRef.simobj = simobj 865 assert(isSimObject(newRef.simobj)) 866 if self.peer and not proxy.isproxy(self.peer): 867 peerObj = self.peer.simobj(_memo=memo) 868 newRef.peer = self.peer.clone(peerObj, memo) 869 assert(not isinstance(newRef.peer, VectorPortRef)) 870 return newRef 871 872 def unproxy(self, simobj): 873 assert(simobj is self.simobj) 874 if proxy.isproxy(self.peer): 875 try: 876 realPeer = self.peer.unproxy(self.simobj) 877 except: 878 print "Error in unproxying port '%s' of %s" % \ 879 (self.name, self.simobj.path()) 880 raise 881 self.connect(realPeer) 882 883 # Call C++ to create corresponding port connection between C++ objects 884 def ccConnect(self): 885 if self.ccConnected: # already done this 886 return 887 peer = self.peer 888 internal.sim_object.connectPorts(self.simobj.getCCObject(), self.name, 889 self.index, peer.simobj.getCCObject(), peer.name, peer.index) 890 self.ccConnected = True 891 peer.ccConnected = True 892 893# A reference to an individual element of a VectorPort... much like a 894# PortRef, but has an index. 895class VectorPortElementRef(PortRef): 896 def __init__(self, simobj, name, index): 897 PortRef.__init__(self, simobj, name) 898 self.index = index 899 900 def __str__(self): 901 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 902 903# A reference to a complete vector-valued port (not just a single element). 904# Can be indexed to retrieve individual VectorPortElementRef instances. 905class VectorPortRef(object): 906 def __init__(self, simobj, name): 907 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 908 self.simobj = simobj 909 self.name = name 910 self.elements = [] 911 912 def __str__(self): 913 return '%s.%s[:]' % (self.simobj, self.name) 914 915 # for config.ini, print peer's name (not ours) 916 def ini_str(self): 917 return ' '.join([el.ini_str() for el in self.elements]) 918 919 def __getitem__(self, key): 920 if not isinstance(key, int): 921 raise TypeError, "VectorPort index must be integer" 922 if key >= len(self.elements): 923 # need to extend list 924 ext = [VectorPortElementRef(self.simobj, self.name, i) 925 for i in range(len(self.elements), key+1)] 926 self.elements.extend(ext) 927 return self.elements[key] 928 929 def _get_next(self): 930 return self[len(self.elements)] 931 932 def __setitem__(self, key, value): 933 if not isinstance(key, int): 934 raise TypeError, "VectorPort index must be integer" 935 self[key].connect(value) 936 937 def connect(self, other): 938 if isinstance(other, (list, tuple)): 939 # Assign list of port refs to vector port. 940 # For now, append them... not sure if that's the right semantics 941 # or if it should replace the current vector. 942 for ref in other: 943 self._get_next().connect(ref) 944 else: 945 # scalar assignment to plain VectorPort is implicit append 946 self._get_next().connect(other) 947 948 def clone(self, simobj, memo): 949 if memo.has_key(self): 950 return memo[self] 951 newRef = copy.copy(self) 952 memo[self] = newRef 953 newRef.simobj = simobj 954 assert(isSimObject(newRef.simobj)) 955 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 956 return newRef 957 958 def unproxy(self, simobj): 959 [el.unproxy(simobj) for el in self.elements] 960 961 def ccConnect(self): 962 [el.ccConnect() for el in self.elements] 963 964# Port description object. Like a ParamDesc object, this represents a 965# logical port in the SimObject class, not a particular port on a 966# SimObject instance. The latter are represented by PortRef objects. 967class Port(object): 968 # Port("description") or Port(default, "description") 969 def __init__(self, *args): 970 if len(args) == 1: 971 self.desc = args[0] 972 elif len(args) == 2: 973 self.default = args[0] 974 self.desc = args[1] 975 else: 976 raise TypeError, 'wrong number of arguments' 977 # self.name is set by SimObject class on assignment 978 # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 979 980 # Generate a PortRef for this port on the given SimObject with the 981 # given name 982 def makeRef(self, simobj): 983 return PortRef(simobj, self.name) 984 985 # Connect an instance of this port (on the given SimObject with 986 # the given name) with the port described by the supplied PortRef 987 def connect(self, simobj, ref): 988 self.makeRef(simobj).connect(ref) 989 990# VectorPort description object. Like Port, but represents a vector 991# of connections (e.g., as on a Bus). 992class VectorPort(Port): 993 def __init__(self, *args): 994 Port.__init__(self, *args) 995 self.isVec = True 996 997 def makeRef(self, simobj): 998 return VectorPortRef(simobj, self.name) 999 1000# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 1001# proxy objects (via set_param_desc()) so that proxy error messages 1002# make sense. 1003class PortParamDesc(object): 1004 __metaclass__ = Singleton 1005 1006 ptype_str = 'Port' 1007 ptype = Port 1008 1009 1010__all__ = ['Param', 'VectorParam', 1011 'Enum', 'Bool', 'String', 'Float', 1012 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1013 'Int32', 'UInt32', 'Int64', 'UInt64', 1014 'Counter', 'Addr', 'Tick', 'Percent', 1015 'TcpPort', 'UdpPort', 'EthernetAddr', 1016 'MemorySize', 'MemorySize32', 1017 'Latency', 'Frequency', 'Clock', 1018 'NetworkBandwidth', 'MemoryBandwidth', 1019 'Range', 'AddrRange', 'TickRange', 1020 'MaxAddr', 'MaxTick', 'AllMemory', 1021 'Time', 1022 'NextEthernetAddr', 'NULL', 1023 'Port', 'VectorPort'] 1024 1025# see comment on imports at end of __init__.py. 1026from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass 1027import objects 1028import internal 1029