params.py revision 4167
1848SN/A# Copyright (c) 2004-2006 The Regents of The University of Michigan 29956SN/A# All rights reserved. 39956SN/A# 49956SN/A# Redistribution and use in source and binary forms, with or without 59956SN/A# modification, are permitted provided that the following conditions are 69956SN/A# met: redistributions of source code must retain the above copyright 79956SN/A# notice, this list of conditions and the following disclaimer; 89956SN/A# redistributions in binary form must reproduce the above copyright 99956SN/A# notice, this list of conditions and the following disclaimer in the 109956SN/A# documentation and/or other materials provided with the distribution; 119956SN/A# neither the name of the copyright holders nor the names of its 129956SN/A# contributors may be used to endorse or promote products derived from 139956SN/A# this software without specific prior written permission. 141762SN/A# 15848SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16848SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17848SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18848SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19848SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20848SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21848SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22848SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23848SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24848SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25848SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26848SN/A# 27848SN/A# Authors: Steve Reinhardt 28848SN/A# Nathan Binkert 29848SN/A 30848SN/A##################################################################### 31848SN/A# 32848SN/A# Parameter description classes 33848SN/A# 34848SN/A# The _params dictionary in each class maps parameter names to either 35848SN/A# a Param or a VectorParam object. These objects contain the 36848SN/A# parameter description string, the parameter type, and the default 37848SN/A# value (if any). The convert() method on these objects is used to 38848SN/A# force whatever value is assigned to the parameter to the appropriate 392665SN/A# type. 402665SN/A# 412665SN/A# Note that the default values are loaded into the class's attribute 42848SN/A# space when the parameter dictionary is initialized (in 43848SN/A# MetaSimObject._new_param()); after that point they aren't used. 44848SN/A# 45848SN/A##################################################################### 46848SN/A 47848SN/Aimport copy 4811264Sandreas.sandberg@arm.comimport datetime 4911264Sandreas.sandberg@arm.comimport inspect 50848SN/Aimport sys 51848SN/Aimport time 52848SN/A 53848SN/Aimport convert 54848SN/Aimport ticks 554762SN/Afrom util import * 562565SN/A 57848SN/A# Dummy base class to identify types that are legitimate for SimObject 58848SN/A# parameters. 598229SN/Aclass ParamValue(object): 608232SN/A 6111264Sandreas.sandberg@arm.com cxx_predecls = [] 6211264Sandreas.sandberg@arm.com swig_predecls = [] 634762SN/A 64848SN/A # default for printing to .ini file is regular string conversion. 65848SN/A # will be overridden in some cases 66848SN/A def ini_str(self): 672107SN/A return str(self) 68848SN/A 695034SN/A # allows us to blithely call unproxy() on things without checking 705034SN/A # if they're really proxies or not 7112087Sspwilson2@wisc.edu def unproxy(self, base): 7212087Sspwilson2@wisc.edu return self 7312087Sspwilson2@wisc.edu 7412087Sspwilson2@wisc.edu# Regular parameter description. 7512087Sspwilson2@wisc.educlass ParamDesc(object): 7612087Sspwilson2@wisc.edu def __init__(self, ptype_str, ptype, *args, **kwargs): 7712087Sspwilson2@wisc.edu self.ptype_str = ptype_str 7812087Sspwilson2@wisc.edu # remember ptype only if it is provided 79848SN/A if ptype != None: 80893SN/A self.ptype = ptype 815034SN/A 82893SN/A if args: 83849SN/A if len(args) == 1: 841722SN/A self.desc = args[0] 85849SN/A elif len(args) == 2: 86849SN/A self.default = args[0] 87849SN/A self.desc = args[1] 88849SN/A else: 89849SN/A raise TypeError, 'too many arguments' 90849SN/A 91849SN/A if kwargs.has_key('desc'): 92849SN/A assert(not hasattr(self, 'desc')) 93849SN/A self.desc = kwargs['desc'] 94849SN/A del kwargs['desc'] 95849SN/A 96849SN/A if kwargs.has_key('default'): 97849SN/A assert(not hasattr(self, 'default')) 98849SN/A self.default = kwargs['default'] 9911101SN/A del kwargs['default'] 10011101SN/A 101849SN/A if kwargs: 102849SN/A raise TypeError, 'extra unknown kwargs %s' % kwargs 103849SN/A 104849SN/A if not hasattr(self, 'desc'): 105849SN/A raise TypeError, 'desc attribute missing' 106849SN/A 107849SN/A def __getattr__(self, attr): 108849SN/A if attr == 'ptype': 109849SN/A try: 110849SN/A ptype = eval(self.ptype_str, objects.__dict__) 111849SN/A if not isinstance(ptype, type): 112849SN/A raise NameError 1131853SN/A self.ptype = ptype 1141853SN/A return ptype 115849SN/A except NameError: 1161722SN/A raise TypeError, \ 117849SN/A "Param qualifier '%s' is not a type" % self.ptype_str 1181722SN/A raise AttributeError, "'%s' object has no attribute '%s'" % \ 119849SN/A (type(self).__name__, attr) 1201722SN/A 121849SN/A def convert(self, value): 1221722SN/A if isinstance(value, proxy.BaseProxy): 1231722SN/A value.set_param_desc(self) 1241722SN/A return value 125849SN/A if not hasattr(self, 'ptype') and isNullPointer(value): 1261722SN/A # deferred evaluation of SimObject; continue to defer if 1271722SN/A # we're just assigning a null pointer 128849SN/A return value 1291722SN/A if isinstance(value, self.ptype): 130849SN/A return value 1312989SN/A if isNullPointer(value) and isSimObjectClass(self.ptype): 132849SN/A return value 1331722SN/A return self.ptype(value) 134849SN/A 1351886SN/A def cxx_predecls(self): 136849SN/A return self.ptype.cxx_predecls 1371722SN/A 1381817SN/A def swig_predecls(self): 1391817SN/A return self.ptype.swig_predecls 1401817SN/A 141893SN/A def cxx_decl(self): 142893SN/A return '%s %s;' % (self.ptype.cxx_type, self.name) 143893SN/A 144893SN/A# Vector-valued parameter description. Just like ParamDesc, except 145893SN/A# that the value is a vector (list) of the specified type instead of a 146893SN/A# single value. 147893SN/A 148893SN/Aclass VectorParamValue(list): 149893SN/A def ini_str(self): 150893SN/A return ' '.join([v.ini_str() for v in self]) 151893SN/A 152893SN/A def unproxy(self, base): 153893SN/A return [v.unproxy(base) for v in self] 154893SN/A 155893SN/Aclass SimObjVector(VectorParamValue): 156893SN/A def print_ini(self): 157893SN/A for v in self: 158893SN/A v.print_ini() 159893SN/A 160893SN/Aclass VectorParamDesc(ParamDesc): 161893SN/A # Convert assigned value to appropriate type. If the RHS is not a 162893SN/A # list or tuple, it generates a single-element list. 163893SN/A def convert(self, value): 164893SN/A if isinstance(value, (list, tuple)): 165893SN/A # list: coerce each element into new list 1669956SN/A tmp_list = [ ParamDesc.convert(self, v) for v in value ] 167849SN/A if isSimObjectSequence(tmp_list): 168849SN/A return SimObjVector(tmp_list) 169849SN/A else: 170849SN/A return VectorParamValue(tmp_list) 171849SN/A else: 172849SN/A # singleton: leave it be (could coerce to a single-element 173849SN/A # list here, but for some historical reason we don't... 174849SN/A return ParamDesc.convert(self, value) 175849SN/A 176849SN/A def cxx_predecls(self): 177849SN/A return ['#include <vector>'] + self.ptype.cxx_predecls 178849SN/A 179849SN/A def swig_predecls(self): 180849SN/A return ['%include "std_vector.i"'] + self.ptype.swig_predecls 181849SN/A 182893SN/A def cxx_decl(self): 1831817SN/A return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) 1841817SN/A 1851817SN/Aclass ParamFactory(object): 1861817SN/A def __init__(self, param_desc_class, ptype_str = None): 187849SN/A self.param_desc_class = param_desc_class 188849SN/A self.ptype_str = ptype_str 189864SN/A 190864SN/A def __getattr__(self, attr): 191864SN/A if self.ptype_str: 192864SN/A attr = self.ptype_str + '.' + attr 193929SN/A return ParamFactory(self.param_desc_class, attr) 194929SN/A 195929SN/A # E.g., Param.Int(5, "number of widgets") 196929SN/A def __call__(self, *args, **kwargs): 197929SN/A caller_frame = inspect.currentframe().f_back 198929SN/A ptype = None 199861SN/A try: 200864SN/A ptype = eval(self.ptype_str, 201861SN/A caller_frame.f_globals, caller_frame.f_locals) 202861SN/A if not isinstance(ptype, type): 2035772SN/A raise TypeError, \ 204861SN/A "Param qualifier is not a type: %s" % ptype 205861SN/A except NameError: 206861SN/A # if name isn't defined yet, assume it's a SimObject, and 207861SN/A # try to resolve it later 208849SN/A pass 209849SN/A return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 210849SN/A 211849SN/AParam = ParamFactory(ParamDesc) 212849SN/AVectorParam = ParamFactory(VectorParamDesc) 2135772SN/A 214849SN/A##################################################################### 2155772SN/A# 2165772SN/A# Parameter Types 2175772SN/A# 2185772SN/A# Though native Python types could be used to specify parameter types 2195772SN/A# (the 'ptype' field of the Param and VectorParam classes), it's more 2205772SN/A# flexible to define our own set of types. This gives us more control 2215772SN/A# over how Python expressions are converted to values (via the 2225772SN/A# __init__() constructor) and how these values are printed out (via 2235772SN/A# the __str__() conversion method). 2241817SN/A# 2255772SN/A##################################################################### 2265772SN/A 2275772SN/A# String-valued parameter. Just mixin the ParamValue class with the 2285772SN/A# built-in str class. 2295772SN/Aclass String(ParamValue,str): 2305772SN/A cxx_type = 'std::string' 2315772SN/A cxx_predecls = ['#include <string>'] 2321817SN/A swig_predecls = ['%include "std_string.i"\n' + 2335772SN/A '%apply const std::string& {std::string *};'] 2345772SN/A pass 2355772SN/A 2365772SN/A# superclass for "numeric" parameter values, to emulate math 2375772SN/A# operations in a type-safe way. e.g., a Latency times an int returns 2385772SN/A# a new Latency object. 2395772SN/Aclass NumericParamValue(ParamValue): 2405772SN/A def __str__(self): 2415772SN/A return str(self.value) 2425772SN/A 2435772SN/A def __float__(self): 2445772SN/A return float(self.value) 2455772SN/A 2465772SN/A def __long__(self): 2475772SN/A return long(self.value) 2485772SN/A 2495772SN/A def __int__(self): 2505772SN/A return int(self.value) 2511817SN/A 2521817SN/A # hook for bounds checking 2535772SN/A def _check(self): 254849SN/A return 2555772SN/A 256849SN/A def __mul__(self, other): 257849SN/A newobj = self.__class__(self) 258849SN/A newobj.value *= other 2595772SN/A newobj._check() 260849SN/A return newobj 2615772SN/A 2625772SN/A __rmul__ = __mul__ 2635772SN/A 2645772SN/A def __div__(self, other): 2655772SN/A newobj = self.__class__(self) 2665772SN/A newobj.value /= other 267849SN/A newobj._check() 2685772SN/A return newobj 2695772SN/A 2705772SN/A def __sub__(self, other): 2715772SN/A newobj = self.__class__(self) 2725772SN/A newobj.value -= other 2735772SN/A newobj._check() 2745772SN/A return newobj 2755772SN/A 2765772SN/A# Metaclass for bounds-checked integer parameters. See CheckedInt. 2775772SN/Aclass CheckedIntType(type): 2785772SN/A def __init__(cls, name, bases, dict): 2795772SN/A super(CheckedIntType, cls).__init__(name, bases, dict) 2801817SN/A 2815772SN/A # CheckedInt is an abstract base class, so we actually don't 2825772SN/A # want to do any processing on it... the rest of this code is 2835772SN/A # just for classes that derive from CheckedInt. 2845772SN/A if name == 'CheckedInt': 2855772SN/A return 2865772SN/A 2875772SN/A if not cls.cxx_predecls: 2881817SN/A # most derived types require this, so we just do it here once 2895772SN/A cls.cxx_predecls = ['#include "sim/host.hh"'] 2905772SN/A 2915772SN/A if not cls.swig_predecls: 2925772SN/A # most derived types require this, so we just do it here once 2935772SN/A cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 2945772SN/A '%import "sim/host.hh"'] 2955772SN/A 2965772SN/A if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 2975772SN/A if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 2985772SN/A panic("CheckedInt subclass %s must define either\n" \ 2995772SN/A " 'min' and 'max' or 'size' and 'unsigned'\n" \ 3005772SN/A % name); 3015772SN/A if cls.unsigned: 3025772SN/A cls.min = 0 3035772SN/A cls.max = 2 ** cls.size - 1 3045772SN/A else: 3055772SN/A cls.min = -(2 ** (cls.size - 1)) 3065772SN/A cls.max = (2 ** (cls.size - 1)) - 1 3075772SN/A 3081817SN/A# Abstract superclass for bounds-checked integer parameters. This 3091817SN/A# class is subclassed to generate parameter classes with specific 3105772SN/A# bounds. Initialization of the min and max bounds is done in the 311849SN/A# metaclass CheckedIntType.__init__. 3125772SN/Aclass CheckedInt(NumericParamValue): 3135772SN/A __metaclass__ = CheckedIntType 3145772SN/A 3155772SN/A def _check(self): 3165772SN/A if not self.min <= self.value <= self.max: 3175772SN/A raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 3185772SN/A (self.min, self.value, self.max) 3195772SN/A 3205772SN/A def __init__(self, value): 3215772SN/A if isinstance(value, str): 3225772SN/A self.value = convert.toInteger(value) 3235772SN/A elif isinstance(value, (int, long, float, NumericParamValue)): 3245772SN/A self.value = long(value) 3255772SN/A else: 3265772SN/A raise TypeError, "Can't convert object of type %s to CheckedInt" \ 3275772SN/A % type(value).__name__ 3285772SN/A self._check() 3295772SN/A 3305772SN/Aclass Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 331849SN/Aclass Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 3322566SN/A 3332566SN/Aclass Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 334849SN/Aclass UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 335849SN/Aclass Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 336849SN/Aclass UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 337849SN/Aclass Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 338849SN/Aclass UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 339849SN/Aclass Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 340849SN/Aclass UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 341849SN/A 342849SN/Aclass Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 3439956SN/Aclass Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 3449956SN/Aclass TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 3459956SN/Aclass UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 3469956SN/A 3479956SN/Aclass Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 3489956SN/A 349849SN/Aclass Float(ParamValue, float): 350849SN/A pass 351849SN/A 352849SN/Aclass MemorySize(CheckedInt): 35310913SN/A cxx_type = 'uint64_t' 3547823SN/A size = 64 3552565SN/A unsigned = True 3562565SN/A def __init__(self, value): 3572565SN/A if isinstance(value, MemorySize): 3582565SN/A self.value = value.value 359849SN/A else: 360849SN/A self.value = convert.toMemorySize(value) 361849SN/A self._check() 362849SN/A 363849SN/Aclass MemorySize32(CheckedInt): 3649956SN/A size = 32 3659956SN/A unsigned = True 3669956SN/A def __init__(self, value): 3679956SN/A if isinstance(value, MemorySize): 3689956SN/A self.value = value.value 3698522SN/A else: 3701625SN/A self.value = convert.toMemorySize(value) 3711625SN/A self._check() 372896SN/A 373896SN/Aclass Addr(CheckedInt): 374896SN/A cxx_type = 'Addr' 375896SN/A cxx_predecls = ['#include "targetarch/isa_traits.hh"'] 376989SN/A size = 64 377989SN/A unsigned = True 378849SN/A def __init__(self, value): 379849SN/A if isinstance(value, Addr): 3802565SN/A self.value = value.value 381849SN/A else: 3822565SN/A try: 383849SN/A self.value = convert.toMemorySize(value) 384849SN/A except TypeError: 385849SN/A self.value = long(value) 3862565SN/A self._check() 387849SN/A def __add__(self, other): 3881763SN/A if isinstance(other, Addr): 389864SN/A return self.value + other.value 390849SN/A else: 3911634SN/A return self.value + other 3921634SN/A 393849SN/A 3947823SN/Aclass MetaRange(type): 3952565SN/A def __init__(cls, name, bases, dict): 396864SN/A super(MetaRange, cls).__init__(name, bases, dict) 3972627SN/A if name == 'Range': 3982627SN/A return 3992627SN/A cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 40011522Sstephan.diestelhorst@arm.com cls.cxx_predecls = \ 40111522Sstephan.diestelhorst@arm.com ['#include "base/range.hh"'] + cls.type.cxx_predecls 4022627SN/A 4032627SN/Aclass Range(ParamValue): 4042627SN/A __metaclass__ = MetaRange 4052627SN/A type = Int # default; can be overridden in subclasses 4062627SN/A def __init__(self, *args, **kwargs): 4072627SN/A def handle_kwargs(self, kwargs): 4082627SN/A if 'end' in kwargs: 4092627SN/A self.second = self.type(kwargs.pop('end')) 4102627SN/A elif 'size' in kwargs: 4112627SN/A self.second = self.first + self.type(kwargs.pop('size')) - 1 4122627SN/A else: 4132627SN/A raise TypeError, "Either end or size must be specified" 4142627SN/A 4152627SN/A if len(args) == 0: 4162627SN/A self.first = self.type(kwargs.pop('start')) 4172627SN/A handle_kwargs(self, kwargs) 4182627SN/A 4192627SN/A elif len(args) == 1: 4202627SN/A if kwargs: 4212627SN/A self.first = self.type(args[0]) 4222627SN/A handle_kwargs(self, kwargs) 4232627SN/A elif isinstance(args[0], Range): 4242627SN/A self.first = self.type(args[0].first) 4252627SN/A self.second = self.type(args[0].second) 4262627SN/A else: 4272627SN/A self.first = self.type(0) 4282627SN/A self.second = self.type(args[0]) - 1 429864SN/A 4302565SN/A elif len(args) == 2: 4312565SN/A self.first = self.type(args[0]) 4322565SN/A self.second = self.type(args[1]) 4339956SN/A else: 4349956SN/A raise TypeError, "Too many arguments specified" 4359956SN/A 4369956SN/A if kwargs: 4379956SN/A raise TypeError, "too many keywords: %s" % kwargs.keys() 4389956SN/A 4399956SN/A def __str__(self): 4409956SN/A return '%s:%s' % (self.first, self.second) 441864SN/A 4422565SN/Aclass AddrRange(Range): 4432565SN/A type = Addr 4442565SN/A 4452565SN/Aclass TickRange(Range): 4462565SN/A type = Tick 4472565SN/A 4482565SN/A# Boolean parameter type. Python doesn't let you subclass bool, since 44910913SN/A# it doesn't want to let you create multiple instances of True and 4507823SN/A# False. Thus this is a little more complicated than String. 4512565SN/Aclass Bool(ParamValue): 4522565SN/A cxx_type = 'bool' 4532565SN/A def __init__(self, value): 4542565SN/A try: 4552565SN/A self.value = convert.toBool(value) 4562627SN/A except TypeError: 4572627SN/A self.value = bool(value) 4582627SN/A 4592627SN/A def __str__(self): 4602565SN/A return str(self.value) 461849SN/A 4622565SN/A def ini_str(self): 4632565SN/A if self.value: 4642565SN/A return 'true' 4652565SN/A return 'false' 466849SN/A 467849SN/Adef IncEthernetAddr(addr, val = 1): 468849SN/A bytes = map(lambda x: int(x, 16), addr.split(':')) 469849SN/A bytes[5] += val 470849SN/A for i in (5, 4, 3, 2, 1): 471849SN/A val,rem = divmod(bytes[i], 256) 4722565SN/A bytes[i] = rem 473861SN/A if val == 0: 474861SN/A break 4752565SN/A bytes[i - 1] += val 476864SN/A assert(bytes[0] <= 255) 477861SN/A return ':'.join(map(lambda x: '%02x' % x, bytes)) 4782565SN/A 479861SN/Aclass NextEthernetAddr(object): 480864SN/A addr = "00:90:00:00:00:01" 481861SN/A 482849SN/A def __init__(self, inc = 1): 483920SN/A self.value = NextEthernetAddr.addr 484849SN/A NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) 485849SN/A 486849SN/Aclass EthernetAddr(ParamValue): 487849SN/A cxx_type = 'Net::EthAddr' 488849SN/A cxx_predecls = ['#include "base/inet.hh"'] 489849SN/A swig_predecls = ['class Net::EthAddr;'] 490849SN/A def __init__(self, value): 491849SN/A if value == NextEthernetAddr: 492849SN/A self.value = value 4932565SN/A return 494849SN/A 4951763SN/A if not isinstance(value, str): 496864SN/A raise TypeError, "expected an ethernet address and didn't get one" 4972565SN/A 498849SN/A bytes = value.split(':') 4991634SN/A if len(bytes) != 6: 5001634SN/A raise TypeError, 'invalid ethernet address %s' % value 5011634SN/A 5022565SN/A for byte in bytes: 5032565SN/A if not 0 <= int(byte) <= 256: 5042565SN/A raise TypeError, 'invalid ethernet address %s' % value 5052565SN/A 5062565SN/A self.value = value 5072565SN/A 5082565SN/A def unproxy(self, base): 5098522SN/A if self.value == NextEthernetAddr: 5108522SN/A self.addr = self.value().value 511849SN/A return self 5127823SN/A 5132565SN/A def __str__(self): 514864SN/A if self.value == NextEthernetAddr: 5152565SN/A if hasattr(self, 'addr'): 5162565SN/A return self.addr 5172565SN/A else: 5189956SN/A return "NextEthernetAddr (unresolved)" 5199956SN/A else: 5209956SN/A return self.value 5219956SN/A 5229956SN/Atime_formats = [ "%a %b %d %H:%M:%S %Z %Y", 5239956SN/A "%a %b %d %H:%M:%S %Z %Y", 5249956SN/A "%Y/%m/%d %H:%M:%S", 5259956SN/A "%Y/%m/%d %H:%M", 5262565SN/A "%Y/%m/%d", 5272565SN/A "%m/%d/%Y %H:%M:%S", 5282565SN/A "%m/%d/%Y %H:%M", 5292565SN/A "%m/%d/%Y", 5302565SN/A "%m/%d/%y %H:%M:%S", 53110913SN/A "%m/%d/%y %H:%M", 5327823SN/A "%m/%d/%y"] 5338522SN/A 5342565SN/A 5352565SN/Adef parse_time(value): 5362565SN/A from time import gmtime, strptime, struct_time, time 5372565SN/A from datetime import datetime, date 5382565SN/A 5398522SN/A if isinstance(value, struct_time): 5408522SN/A return value 5412627SN/A 5422627SN/A if isinstance(value, (int, long)): 5432627SN/A return gmtime(value) 5442627SN/A 5452565SN/A if isinstance(value, (datetime, date)): 546849SN/A return value.timetuple() 5478522SN/A 5488522SN/A if isinstance(value, str): 5492565SN/A if value in ('Now', 'Today'): 5502565SN/A return time.gmtime(time.time()) 5512565SN/A 5522565SN/A for format in time_formats: 553849SN/A try: 554849SN/A return strptime(value, format) 555849SN/A except ValueError: 556849SN/A pass 557849SN/A 558849SN/A raise ValueError, "Could not parse '%s' as a time" % value 5598522SN/A 5608522SN/Aclass Time(ParamValue): 561849SN/A cxx_type = 'time_t' 562849SN/A def __init__(self, value): 563849SN/A self.value = parse_time(value) 564849SN/A 565849SN/A def __str__(self): 566849SN/A tm = self.value 567849SN/A return ' '.join([ str(tm[i]) for i in xrange(8)]) 568849SN/A 569849SN/A def ini_str(self): 570849SN/A return str(self) 571849SN/A 572849SN/A# Enumerated types are a little more complex. The user specifies the 573849SN/A# type as Enum(foo) where foo is either a list or dictionary of 574849SN/A# alternatives (typically strings, but not necessarily so). (In the 575849SN/A# long run, the integer value of the parameter will be the list index 576849SN/A# or the corresponding dictionary value. For now, since we only check 577849SN/A# that the alternative is valid and then spit it into a .ini file, 578849SN/A# there's not much point in using the dictionary.) 579849SN/A 580849SN/A# What Enum() must do is generate a new type encapsulating the 581849SN/A# provided list/dictionary so that specific values of the parameter 582849SN/A# can be instances of that type. We define two hidden internal 583849SN/A# classes (_ListEnum and _DictEnum) to serve as base classes, then 584849SN/A# derive the new type from the appropriate base class on the fly. 585849SN/A 586849SN/A 587849SN/A# Metaclass for Enum types 588849SN/Aclass MetaEnum(type): 589849SN/A def __init__(cls, name, bases, init_dict): 590849SN/A if init_dict.has_key('map'): 591849SN/A if not isinstance(cls.map, dict): 592849SN/A raise TypeError, "Enum-derived class attribute 'map' " \ 593849SN/A "must be of type dict" 594849SN/A # build list of value strings from map 595849SN/A cls.vals = cls.map.keys() 596849SN/A cls.vals.sort() 597849SN/A elif init_dict.has_key('vals'): 598849SN/A if not isinstance(cls.vals, list): 599849SN/A raise TypeError, "Enum-derived class attribute 'vals' " \ 600849SN/A "must be of type list" 601849SN/A # build string->value map from vals sequence 602849SN/A cls.map = {} 603849SN/A for idx,val in enumerate(cls.vals): 604849SN/A cls.map[val] = idx 605849SN/A else: 606849SN/A raise TypeError, "Enum-derived class must define "\ 607849SN/A "attribute 'map' or 'vals'" 608896SN/A 609896SN/A cls.cxx_type = name + '::Enum' 610849SN/A 611849SN/A super(MetaEnum, cls).__init__(name, bases, init_dict) 612849SN/A 613849SN/A # Generate C++ class declaration for this enum type. 6147823SN/A # Note that we wrap the enum in a class/struct to act as a namespace, 615849SN/A # so that the enum strings can be brief w/o worrying about collisions. 616849SN/A def cxx_decl(cls): 617849SN/A s = 'struct %s {\n enum Enum {\n ' % cls.__name__ 618849SN/A s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 619849SN/A s += '\n };\n};\n' 620849SN/A return s 6211625SN/A 622849SN/A# Base class for enum types. 623849SN/Aclass Enum(ParamValue): 6241625SN/A __metaclass__ = MetaEnum 625849SN/A vals = [] 626849SN/A 627849SN/A def __init__(self, value): 628849SN/A if value not in self.map: 629849SN/A raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 630849SN/A % (value, self.vals) 631849SN/A self.value = value 632849SN/A 633849SN/A def __str__(self): 634849SN/A return self.value 635849SN/A 636849SN/A# how big does a rounding error need to be before we warn about it? 637849SN/Afrequency_tolerance = 0.001 # 0.1% 638849SN/A 6391722SN/Aclass TickParamValue(NumericParamValue): 6409533SN/A cxx_type = 'Tick' 641849SN/A cxx_predecls = ['#include "sim/host.hh"'] 642849SN/A swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 643849SN/A '%import "sim/host.hh"'] 644849SN/A 645849SN/Aclass Latency(TickParamValue): 646849SN/A def __init__(self, value): 647849SN/A if isinstance(value, (Latency, Clock)): 648849SN/A self.ticks = value.ticks 649849SN/A self.value = value.value 6501722SN/A elif isinstance(value, Frequency): 6511722SN/A self.ticks = value.ticks 6521722SN/A self.value = 1.0 / value.value 6531722SN/A elif value.endswith('t'): 6541722SN/A self.ticks = True 6551722SN/A self.value = int(value[:-1]) 6561722SN/A else: 6571722SN/A self.ticks = False 65810587SN/A self.value = convert.toLatency(value) 659849SN/A 660849SN/A def __getattr__(self, attr): 661849SN/A if attr in ('latency', 'period'): 662849SN/A return self 663849SN/A if attr == 'frequency': 6641722SN/A return Frequency(self) 66511980Sjason@lowepower.com raise AttributeError, "Latency object has no attribute '%s'" % attr 6661722SN/A 667849SN/A # convert latency to ticks 668849SN/A def ini_str(self): 669849SN/A if self.ticks or self.value == 0: 670849SN/A return '%d' % self.value 6711722SN/A else: 6721722SN/A return '%d' % (ticks.fromSeconds(self.value)) 673849SN/A 674849SN/Aclass Frequency(TickParamValue): 675849SN/A def __init__(self, value): 676849SN/A if isinstance(value, (Latency, Clock)): 677893SN/A if value.value == 0: 678849SN/A self.value = 0 679893SN/A else: 680849SN/A self.value = 1.0 / value.value 681849SN/A self.ticks = value.ticks 682849SN/A elif isinstance(value, Frequency): 683877SN/A self.value = value.value 684849SN/A self.ticks = value.ticks 685849SN/A else: 686849SN/A self.ticks = False 687849SN/A self.value = convert.toFrequency(value) 688849SN/A 6891722SN/A def __getattr__(self, attr): 6901722SN/A if attr == 'frequency': 691849SN/A return self 692849SN/A if attr in ('latency', 'period'): 693849SN/A return Latency(self) 694849SN/A raise AttributeError, "Frequency object has no attribute '%s'" % attr 695893SN/A 696849SN/A # convert latency to ticks 697893SN/A def ini_str(self): 6988522SN/A if self.ticks or self.value == 0: 699849SN/A return '%d' % self.value 700849SN/A else: 701849SN/A return '%d' % (ticks.fromSeconds(1.0 / self.value)) 702849SN/A 703849SN/A# A generic frequency and/or Latency value. Value is stored as a latency, 704849SN/A# but to avoid ambiguity this object does not support numeric ops (* or /). 705849SN/A# An explicit conversion to a Latency or Frequency must be made first. 7061722SN/Aclass Clock(ParamValue): 707849SN/A cxx_type = 'Tick' 70812392Sjason@lowepower.com cxx_predecls = ['#include "sim/host.hh"'] 7091722SN/A swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 710849SN/A '%import "sim/host.hh"'] 711849SN/A def __init__(self, value): 712849SN/A if isinstance(value, (Latency, Clock)): 713849SN/A self.ticks = value.ticks 714893SN/A self.value = value.value 715849SN/A elif isinstance(value, Frequency): 716893SN/A self.ticks = value.ticks 7178522SN/A self.value = 1.0 / value.value 718849SN/A elif value.endswith('t'): 719849SN/A self.ticks = True 720849SN/A self.value = int(value[:-1]) 721849SN/A else: 722849SN/A self.ticks = False 723849SN/A self.value = convert.anyToLatency(value) 724849SN/A 725849SN/A def __getattr__(self, attr): 726849SN/A if attr == 'frequency': 727849SN/A return Frequency(self) 728849SN/A if attr in ('latency', 'period'): 729849SN/A return Latency(self) 730849SN/A raise AttributeError, "Frequency object has no attribute '%s'" % attr 731893SN/A 732849SN/A def ini_str(self): 733893SN/A return self.period.ini_str() 734893SN/A 735893SN/Aclass NetworkBandwidth(float,ParamValue): 736849SN/A cxx_type = 'float' 737849SN/A def __new__(cls, value): 738849SN/A # convert to bits per second 739849SN/A val = convert.toNetworkBandwidth(value) 740849SN/A return super(cls, NetworkBandwidth).__new__(cls, val) 741849SN/A 742849SN/A def __str__(self): 743849SN/A return str(self.val) 744849SN/A 745849SN/A def ini_str(self): 746849SN/A # convert to seconds per byte 747849SN/A value = 8.0 / float(self) 7481625SN/A # convert to ticks per byte 749849SN/A return '%f' % (ticks.fromSeconds(value)) 750849SN/A 751849SN/Aclass MemoryBandwidth(float,ParamValue): 752849SN/A cxx_type = 'float' 753849SN/A def __new__(self, value): 754849SN/A # we want the number of ticks per byte of data 7551817SN/A val = convert.toMemoryBandwidth(value) 756849SN/A return super(cls, MemoryBandwidth).__new__(cls, val) 7571817SN/A 758849SN/A def __str__(self): 759849SN/A return str(self.val) 760849SN/A 761849SN/A def ini_str(self): 762849SN/A # convert to seconds per byte 7631625SN/A value = 1.0 / float(self) 764849SN/A # convert to ticks per byte 765849SN/A return '%f' % (ticks.fromSeconds(value)) 766849SN/A 767849SN/A# 768849SN/A# "Constants"... handy aliases for various values. 769849SN/A# 770849SN/A 771849SN/A# Special class for NULL pointers. Note the special check in 772849SN/A# make_param_value() above that lets these be assigned where a 773849SN/A# SimObject is required. 774849SN/A# only one copy of a particular node 775849SN/Aclass NullSimObject(object): 776849SN/A __metaclass__ = Singleton 777849SN/A 778849SN/A def __call__(cls): 779849SN/A return cls 780849SN/A 781849SN/A def _instantiate(self, parent = None, path = ''): 782893SN/A pass 783893SN/A 784893SN/A def ini_str(self): 785893SN/A return 'Null' 786893SN/A 787893SN/A def unproxy(self, base): 788893SN/A return self 789893SN/A 790893SN/A def set_path(self, parent, name): 791893SN/A pass 792893SN/A def __str__(self): 793893SN/A return 'Null' 794893SN/A 795849SN/A# The only instance you'll ever need... 796893SN/ANULL = NullSimObject() 797849SN/A 798893SN/Adef isNullPointer(value): 799849SN/A return isinstance(value, NullSimObject) 800893SN/A 801849SN/A# Some memory range specifications use this as a default upper bound. 802849SN/AMaxAddr = Addr.max 803849SN/AMaxTick = Tick.max 804849SN/AAllMemory = AddrRange(0, MaxAddr) 805893SN/A 806849SN/A 807849SN/A##################################################################### 808849SN/A# 809849SN/A# Port objects 810849SN/A# 811849SN/A# Ports are used to interconnect objects in the memory system. 812849SN/A# 813849SN/A##################################################################### 814849SN/A 815849SN/A# Port reference: encapsulates a reference to a particular port on a 816849SN/A# particular SimObject. 817849SN/Aclass PortRef(object): 818849SN/A def __init__(self, simobj, name): 819893SN/A assert(isSimObject(simobj) or isSimObjectClass(simobj)) 820849SN/A self.simobj = simobj 821849SN/A self.name = name 822849SN/A self.peer = None # not associated with another port yet 823849SN/A self.ccConnected = False # C++ port connection done? 824849SN/A self.index = -1 # always -1 for non-vector ports 825849SN/A 826849SN/A def __str__(self): 827849SN/A return '%s.%s' % (self.simobj, self.name) 828849SN/A 829849SN/A # for config.ini, print peer's name (not ours) 830849SN/A def ini_str(self): 831849SN/A return str(self.peer) 832849SN/A 833849SN/A def __getattr__(self, attr): 834849SN/A if attr == 'peerObj': 835849SN/A # shorthand for proxies 836849SN/A return self.peer.simobj 837849SN/A raise AttributeError, "'%s' object has no attribute '%s'" % \ 838849SN/A (self.__class__.__name__, attr) 839849SN/A 840849SN/A # Full connection is symmetric (both ways). Called via 841849SN/A # SimObject.__setattr__ as a result of a port assignment, e.g., 842849SN/A # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 843849SN/A # e.g., "obj1.portA[3] = obj2.portB". 844849SN/A def connect(self, other): 845849SN/A if isinstance(other, VectorPortRef): 846849SN/A # reference to plain VectorPort is implicit append 847849SN/A other = other._get_next() 848849SN/A if self.peer and not proxy.isproxy(self.peer): 849849SN/A print "warning: overwriting port", self, \ 850849SN/A "value", self.peer, "with", other 851849SN/A self.peer = other 852849SN/A if proxy.isproxy(other): 853849SN/A other.set_param_desc(PortParamDesc()) 854849SN/A elif isinstance(other, PortRef): 855849SN/A if other.peer is not self: 856849SN/A other.connect(self) 857893SN/A else: 858849SN/A raise TypeError, \ 859893SN/A "assigning non-port reference '%s' to port '%s'" \ 860849SN/A % (other, self) 861877SN/A 86211980Sjason@lowepower.com def clone(self, simobj, memo): 86311980Sjason@lowepower.com if memo.has_key(self): 864877SN/A return memo[self] 8651722SN/A newRef = copy.copy(self) 866877SN/A memo[self] = newRef 867877SN/A newRef.simobj = simobj 8681722SN/A assert(isSimObject(newRef.simobj)) 869877SN/A if self.peer and not proxy.isproxy(self.peer): 870877SN/A peerObj = self.peer.simobj(_memo=memo) 871877SN/A newRef.peer = self.peer.clone(peerObj, memo) 872877SN/A assert(not isinstance(newRef.peer, VectorPortRef)) 873877SN/A return newRef 874877SN/A 875877SN/A def unproxy(self, simobj): 876849SN/A assert(simobj is self.simobj) 8771817SN/A if proxy.isproxy(self.peer): 878857SN/A try: 879857SN/A realPeer = self.peer.unproxy(self.simobj) 880849SN/A except: 881849SN/A print "Error in unproxying port '%s' of %s" % \ 882849SN/A (self.name, self.simobj.path()) 883849SN/A raise 884849SN/A self.connect(realPeer) 885849SN/A 886849SN/A # Call C++ to create corresponding port connection between C++ objects 887849SN/A def ccConnect(self): 888849SN/A if self.ccConnected: # already done this 889849SN/A return 890849SN/A peer = self.peer 891849SN/A internal.sim_object.connectPorts(self.simobj.getCCObject(), self.name, 892849SN/A self.index, peer.simobj.getCCObject(), peer.name, peer.index) 893849SN/A self.ccConnected = True 894849SN/A peer.ccConnected = True 895849SN/A 896849SN/A# A reference to an individual element of a VectorPort... much like a 897849SN/A# PortRef, but has an index. 898849SN/Aclass VectorPortElementRef(PortRef): 899849SN/A def __init__(self, simobj, name, index): 900849SN/A PortRef.__init__(self, simobj, name) 901849SN/A self.index = index 902849SN/A 903849SN/A def __str__(self): 904849SN/A return '%s.%s[%d]' % (self.simobj, self.name, self.index) 905877SN/A 9061817SN/A# A reference to a complete vector-valued port (not just a single element). 907877SN/A# Can be indexed to retrieve individual VectorPortElementRef instances. 908877SN/Aclass VectorPortRef(object): 909849SN/A def __init__(self, simobj, name): 910849SN/A assert(isSimObject(simobj) or isSimObjectClass(simobj)) 911849SN/A self.simobj = simobj 912849SN/A self.name = name 913849SN/A self.elements = [] 914849SN/A 915849SN/A def __str__(self): 916849SN/A return '%s.%s[:]' % (self.simobj, self.name) 917849SN/A 918877SN/A # for config.ini, print peer's name (not ours) 919893SN/A def ini_str(self): 920877SN/A return ' '.join([el.ini_str() for el in self.elements]) 921893SN/A 922877SN/A def __getitem__(self, key): 923877SN/A if not isinstance(key, int): 924877SN/A raise TypeError, "VectorPort index must be integer" 925877SN/A if key >= len(self.elements): 926849SN/A # need to extend list 927849SN/A ext = [VectorPortElementRef(self.simobj, self.name, i) 928849SN/A for i in range(len(self.elements), key+1)] 929849SN/A self.elements.extend(ext) 930849SN/A return self.elements[key] 931849SN/A 932849SN/A def _get_next(self): 933849SN/A return self[len(self.elements)] 934849SN/A 935849SN/A def __setitem__(self, key, value): 936849SN/A if not isinstance(key, int): 937849SN/A raise TypeError, "VectorPort index must be integer" 938849SN/A self[key].connect(value) 939849SN/A 940849SN/A def connect(self, other): 941849SN/A if isinstance(other, (list, tuple)): 942893SN/A # Assign list of port refs to vector port. 943849SN/A # For now, append them... not sure if that's the right semantics 944893SN/A # or if it should replace the current vector. 945849SN/A for ref in other: 946893SN/A self._get_next().connect(ref) 947849SN/A else: 948849SN/A # scalar assignment to plain VectorPort is implicit append 949849SN/A self._get_next().connect(other) 950849SN/A 951893SN/A def clone(self, simobj, memo): 952893SN/A if memo.has_key(self): 953893SN/A return memo[self] 954893SN/A newRef = copy.copy(self) 955893SN/A memo[self] = newRef 956893SN/A newRef.simobj = simobj 957849SN/A assert(isSimObject(newRef.simobj)) 958849SN/A newRef.elements = [el.clone(simobj, memo) for el in self.elements] 959849SN/A return newRef 960849SN/A 961849SN/A def unproxy(self, simobj): 962849SN/A [el.unproxy(simobj) for el in self.elements] 963849SN/A 964849SN/A def ccConnect(self): 965849SN/A [el.ccConnect() for el in self.elements] 966849SN/A 967849SN/A# Port description object. Like a ParamDesc object, this represents a 968849SN/A# logical port in the SimObject class, not a particular port on a 969849SN/A# SimObject instance. The latter are represented by PortRef objects. 970849SN/Aclass Port(object): 971857SN/A # Port("description") or Port(default, "description") 972857SN/A def __init__(self, *args): 973857SN/A if len(args) == 1: 974849SN/A self.desc = args[0] 975849SN/A elif len(args) == 2: 976849SN/A self.default = args[0] 977849SN/A self.desc = args[1] 978849SN/A else: 9791817SN/A raise TypeError, 'wrong number of arguments' 980849SN/A # self.name is set by SimObject class on assignment 981849SN/A # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 982849SN/A 983849SN/A # Generate a PortRef for this port on the given SimObject with the 984849SN/A # given name 985849SN/A def makeRef(self, simobj): 986849SN/A return PortRef(simobj, self.name) 987849SN/A 988849SN/A # Connect an instance of this port (on the given SimObject with 989849SN/A # the given name) with the port described by the supplied PortRef 990849SN/A def connect(self, simobj, ref): 991893SN/A self.makeRef(simobj).connect(ref) 992893SN/A 993893SN/A# VectorPort description object. Like Port, but represents a vector 994849SN/A# of connections (e.g., as on a Bus). 995893SN/Aclass VectorPort(Port): 996849SN/A def __init__(self, *args): 997849SN/A Port.__init__(self, *args) 998878SN/A self.isVec = True 999878SN/A 1000878SN/A def makeRef(self, simobj): 1001878SN/A return VectorPortRef(simobj, self.name) 1002849SN/A 1003849SN/A# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 1004849SN/A# proxy objects (via set_param_desc()) so that proxy error messages 1005849SN/A# make sense. 1006849SN/Aclass PortParamDesc(object): 1007849SN/A __metaclass__ = Singleton 1008849SN/A 1009849SN/A ptype_str = 'Port' 1010849SN/A ptype = Port 1011849SN/A 1012849SN/A 1013849SN/A__all__ = ['Param', 'VectorParam', 1014849SN/A 'Enum', 'Bool', 'String', 'Float', 1015849SN/A 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1016849SN/A 'Int32', 'UInt32', 'Int64', 'UInt64', 1017849SN/A 'Counter', 'Addr', 'Tick', 'Percent', 1018849SN/A 'TcpPort', 'UdpPort', 'EthernetAddr', 1019893SN/A 'MemorySize', 'MemorySize32', 1020849SN/A 'Latency', 'Frequency', 'Clock', 1021893SN/A 'NetworkBandwidth', 'MemoryBandwidth', 1022849SN/A 'Range', 'AddrRange', 'TickRange', 1023849SN/A 'MaxAddr', 'MaxTick', 'AllMemory', 1024849SN/A 'Time', 1025849SN/A 'NextEthernetAddr', 'NULL', 1026849SN/A 'Port', 'VectorPort'] 1027849SN/A 1028849SN/A# see comment on imports at end of __init__.py. 1029849SN/Afrom SimObject import isSimObject, isSimObjectSequence, isSimObjectClass 1030849SN/Aimport proxy 1031849SN/Aimport objects 1032849SN/Aimport internal 1033849SN/A