params.py revision 3105
12SN/A# Copyright (c) 2004-2006 The Regents of The University of Michigan 21762SN/A# All rights reserved. 37534Ssteve.reinhardt@amd.com# 42SN/A# Redistribution and use in source and binary forms, with or without 52SN/A# modification, are permitted provided that the following conditions are 62SN/A# met: redistributions of source code must retain the above copyright 72SN/A# notice, this list of conditions and the following disclaimer; 82SN/A# redistributions in binary form must reproduce the above copyright 92SN/A# notice, this list of conditions and the following disclaimer in the 102SN/A# documentation and/or other materials provided with the distribution; 112SN/A# neither the name of the copyright holders nor the names of its 122SN/A# contributors may be used to endorse or promote products derived from 132SN/A# this software without specific prior written permission. 142SN/A# 152SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 162SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 172SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 182SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 192SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 202SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 212SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 222SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 232SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 242SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 252SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 262SN/A# 272SN/A# Authors: Steve Reinhardt 282665Ssaidi@eecs.umich.edu# Nathan Binkert 292665Ssaidi@eecs.umich.edu 302665Ssaidi@eecs.umich.edu##################################################################### 312SN/A# 322SN/A# Parameter description classes 332SN/A# 342SN/A# The _params dictionary in each class maps parameter names to either 352SN/A# a Param or a VectorParam object. These objects contain the 362SN/A# parameter description string, the parameter type, and the default 372SN/A# value (if any). The convert() method on these objects is used to 382SN/A# force whatever value is assigned to the parameter to the appropriate 392SN/A# type. 405491Sgblack@eecs.umich.edu# 415491Sgblack@eecs.umich.edu# Note that the default values are loaded into the class's attribute 422SN/A# space when the parameter dictionary is initialized (in 435491Sgblack@eecs.umich.edu# MetaSimObject._new_param()); after that point they aren't used. 442SN/A# 452SN/A##################################################################### 468737Skoansin.tan@gmail.com 474762Snate@binkert.orgimport sys, inspect, copy 489342SAndreas.Sandberg@arm.comimport convert 499356Snilay@cs.wisc.edufrom util import * 5056SN/A 512SN/A# Dummy base class to identify types that are legitimate for SimObject 522797Sktlim@umich.edu# parameters. 532797Sktlim@umich.educlass ParamValue(object): 5410023Smatt.horsnell@ARM.com 559196SAndreas.Sandberg@arm.com cxx_predecls = [] 562SN/A swig_predecls = [] 572SN/A 582SN/A # default for printing to .ini file is regular string conversion. 599196SAndreas.Sandberg@arm.com # will be overridden in some cases 609196SAndreas.Sandberg@arm.com def ini_str(self): 619196SAndreas.Sandberg@arm.com return str(self) 629196SAndreas.Sandberg@arm.com 639196SAndreas.Sandberg@arm.com # allows us to blithely call unproxy() on things without checking 649196SAndreas.Sandberg@arm.com # if they're really proxies or not 659196SAndreas.Sandberg@arm.com def unproxy(self, base): 669196SAndreas.Sandberg@arm.com return self 679196SAndreas.Sandberg@arm.com 689196SAndreas.Sandberg@arm.com# Regular parameter description. 699196SAndreas.Sandberg@arm.comclass ParamDesc(object): 709196SAndreas.Sandberg@arm.com def __init__(self, ptype_str, ptype, *args, **kwargs): 719196SAndreas.Sandberg@arm.com self.ptype_str = ptype_str 729196SAndreas.Sandberg@arm.com # remember ptype only if it is provided 739196SAndreas.Sandberg@arm.com if ptype != None: 749196SAndreas.Sandberg@arm.com self.ptype = ptype 759196SAndreas.Sandberg@arm.com 769342SAndreas.Sandberg@arm.com if args: 779196SAndreas.Sandberg@arm.com if len(args) == 1: 789196SAndreas.Sandberg@arm.com self.desc = args[0] 799196SAndreas.Sandberg@arm.com elif len(args) == 2: 809196SAndreas.Sandberg@arm.com self.default = args[0] 819196SAndreas.Sandberg@arm.com self.desc = args[1] 829196SAndreas.Sandberg@arm.com else: 839196SAndreas.Sandberg@arm.com raise TypeError, 'too many arguments' 842SN/A 859342SAndreas.Sandberg@arm.com if kwargs.has_key('desc'): 862SN/A assert(not hasattr(self, 'desc')) 872SN/A self.desc = kwargs['desc'] 882SN/A del kwargs['desc'] 892SN/A 909196SAndreas.Sandberg@arm.com if kwargs.has_key('default'): 912SN/A assert(not hasattr(self, 'default')) 922SN/A self.default = kwargs['default'] 9310023Smatt.horsnell@ARM.com del kwargs['default'] 9410023Smatt.horsnell@ARM.com 9510023Smatt.horsnell@ARM.com if kwargs: 964762Snate@binkert.org raise TypeError, 'extra unknown kwargs %s' % kwargs 979196SAndreas.Sandberg@arm.com 984762Snate@binkert.org if not hasattr(self, 'desc'): 994762Snate@binkert.org raise TypeError, 'desc attribute missing' 1002SN/A 1014762Snate@binkert.org def __getattr__(self, attr): 1024762Snate@binkert.org if attr == 'ptype': 1034762Snate@binkert.org try: 10410422Sandreas.hansson@arm.com ptype = eval(self.ptype_str, objects.__dict__) 1052SN/A if not isinstance(ptype, type): 1065034Smilesck@eecs.umich.edu raise NameError 1075034Smilesck@eecs.umich.edu self.ptype = ptype 1081553SN/A return ptype 109265SN/A except NameError: 1107532Ssteve.reinhardt@amd.com raise TypeError, \ 1117532Ssteve.reinhardt@amd.com "Param qualifier '%s' is not a type" % self.ptype_str 1127532Ssteve.reinhardt@amd.com raise AttributeError, "'%s' object has no attribute '%s'" % \ 1137532Ssteve.reinhardt@amd.com (type(self).__name__, attr) 1147532Ssteve.reinhardt@amd.com 1157532Ssteve.reinhardt@amd.com def convert(self, value): 116465SN/A if isinstance(value, proxy.BaseProxy): 117465SN/A value.set_param_desc(self) 1187532Ssteve.reinhardt@amd.com return value 1197532Ssteve.reinhardt@amd.com if not hasattr(self, 'ptype') and isNullPointer(value): 1207532Ssteve.reinhardt@amd.com # deferred evaluation of SimObject; continue to defer if 1217532Ssteve.reinhardt@amd.com # we're just assigning a null pointer 1227532Ssteve.reinhardt@amd.com return value 1237532Ssteve.reinhardt@amd.com if isinstance(value, self.ptype): 1247532Ssteve.reinhardt@amd.com return value 1257532Ssteve.reinhardt@amd.com if isNullPointer(value) and isSimObjectClass(self.ptype): 1269196SAndreas.Sandberg@arm.com return value 1279196SAndreas.Sandberg@arm.com return self.ptype(value) 1287532Ssteve.reinhardt@amd.com 12910905Sandreas.sandberg@arm.com def cxx_predecls(self): 1307532Ssteve.reinhardt@amd.com return self.ptype.cxx_predecls 1317532Ssteve.reinhardt@amd.com 1327532Ssteve.reinhardt@amd.com def swig_predecls(self): 1337532Ssteve.reinhardt@amd.com return self.ptype.swig_predecls 1347532Ssteve.reinhardt@amd.com 1357532Ssteve.reinhardt@amd.com def cxx_decl(self): 1367532Ssteve.reinhardt@amd.com return '%s %s;' % (self.ptype.cxx_type, self.name) 1377532Ssteve.reinhardt@amd.com 1389196SAndreas.Sandberg@arm.com# Vector-valued parameter description. Just like ParamDesc, except 1399196SAndreas.Sandberg@arm.com# that the value is a vector (list) of the specified type instead of a 1409196SAndreas.Sandberg@arm.com# single value. 1412SN/A 1429196SAndreas.Sandberg@arm.comclass VectorParamValue(list): 1439196SAndreas.Sandberg@arm.com def ini_str(self): 1449196SAndreas.Sandberg@arm.com return ' '.join([v.ini_str() for v in self]) 1459196SAndreas.Sandberg@arm.com 146330SN/A def unproxy(self, base): 1472SN/A return [v.unproxy(base) for v in self] 1487532Ssteve.reinhardt@amd.com 14910023Smatt.horsnell@ARM.comclass SimObjVector(VectorParamValue): 15010023Smatt.horsnell@ARM.com def print_ini(self): 15110023Smatt.horsnell@ARM.com for v in self: 15210023Smatt.horsnell@ARM.com v.print_ini() 15310023Smatt.horsnell@ARM.com 15410023Smatt.horsnell@ARM.comclass VectorParamDesc(ParamDesc): 15510023Smatt.horsnell@ARM.com # Convert assigned value to appropriate type. If the RHS is not a 15610023Smatt.horsnell@ARM.com # list or tuple, it generates a single-element list. 15710023Smatt.horsnell@ARM.com def convert(self, value): 15810023Smatt.horsnell@ARM.com if isinstance(value, (list, tuple)): 15910023Smatt.horsnell@ARM.com # list: coerce each element into new list 16010023Smatt.horsnell@ARM.com tmp_list = [ ParamDesc.convert(self, v) for v in value ] 16110023Smatt.horsnell@ARM.com if isSimObjectSequence(tmp_list): 16210023Smatt.horsnell@ARM.com return SimObjVector(tmp_list) 16310023Smatt.horsnell@ARM.com else: 1647532Ssteve.reinhardt@amd.com return VectorParamValue(tmp_list) 1657532Ssteve.reinhardt@amd.com else: 1667823Ssteve.reinhardt@amd.com # singleton: leave it be (could coerce to a single-element 1677532Ssteve.reinhardt@amd.com # list here, but for some historical reason we don't... 1687532Ssteve.reinhardt@amd.com return ParamDesc.convert(self, value) 1697492Ssteve.reinhardt@amd.com 170330SN/A def cxx_predecls(self): 1719196SAndreas.Sandberg@arm.com return ['#include <vector>'] + self.ptype.cxx_predecls 1729342SAndreas.Sandberg@arm.com 1739342SAndreas.Sandberg@arm.com def swig_predecls(self): 1749342SAndreas.Sandberg@arm.com return ['%include "std_vector.i"'] + self.ptype.swig_predecls 1759342SAndreas.Sandberg@arm.com 1769342SAndreas.Sandberg@arm.com def cxx_decl(self): 1779342SAndreas.Sandberg@arm.com return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) 17810905Sandreas.sandberg@arm.com 17910905Sandreas.sandberg@arm.comclass ParamFactory(object): 18010905Sandreas.sandberg@arm.com def __init__(self, param_desc_class, ptype_str = None): 18110905Sandreas.sandberg@arm.com self.param_desc_class = param_desc_class 1829342SAndreas.Sandberg@arm.com self.ptype_str = ptype_str 1839196SAndreas.Sandberg@arm.com 1849196SAndreas.Sandberg@arm.com def __getattr__(self, attr): 18510905Sandreas.sandberg@arm.com if self.ptype_str: 186938SN/A attr = self.ptype_str + '.' + attr 1871031SN/A return ParamFactory(self.param_desc_class, attr) 1881031SN/A 1891031SN/A # E.g., Param.Int(5, "number of widgets") 1901031SN/A def __call__(self, *args, **kwargs): 1911031SN/A caller_frame = inspect.currentframe().f_back 1921031SN/A ptype = None 1935314Sstever@gmail.com try: 1945314Sstever@gmail.com ptype = eval(self.ptype_str, 1955315Sstever@gmail.com caller_frame.f_globals, caller_frame.f_locals) 1965314Sstever@gmail.com if not isinstance(ptype, type): 1975314Sstever@gmail.com raise TypeError, \ 1985314Sstever@gmail.com "Param qualifier is not a type: %s" % ptype 1992SN/A except NameError: 2002SN/A # if name isn't defined yet, assume it's a SimObject, and 2019554Sandreas.hansson@arm.com # try to resolve it later 2029554Sandreas.hansson@arm.com pass 2039554Sandreas.hansson@arm.com return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 2049554Sandreas.hansson@arm.com 2052SN/AParam = ParamFactory(ParamDesc) 206VectorParam = ParamFactory(VectorParamDesc) 207 208##################################################################### 209# 210# Parameter Types 211# 212# Though native Python types could be used to specify parameter types 213# (the 'ptype' field of the Param and VectorParam classes), it's more 214# flexible to define our own set of types. This gives us more control 215# over how Python expressions are converted to values (via the 216# __init__() constructor) and how these values are printed out (via 217# the __str__() conversion method). 218# 219##################################################################### 220 221# String-valued parameter. Just mixin the ParamValue class with the 222# built-in str class. 223class String(ParamValue,str): 224 cxx_type = 'std::string' 225 cxx_predecls = ['#include <string>'] 226 swig_predecls = ['%include "std_string.i"\n' + 227 '%apply const std::string& {std::string *};'] 228 pass 229 230# superclass for "numeric" parameter values, to emulate math 231# operations in a type-safe way. e.g., a Latency times an int returns 232# a new Latency object. 233class NumericParamValue(ParamValue): 234 def __str__(self): 235 return str(self.value) 236 237 def __float__(self): 238 return float(self.value) 239 240 # hook for bounds checking 241 def _check(self): 242 return 243 244 def __mul__(self, other): 245 newobj = self.__class__(self) 246 newobj.value *= other 247 newobj._check() 248 return newobj 249 250 __rmul__ = __mul__ 251 252 def __div__(self, other): 253 newobj = self.__class__(self) 254 newobj.value /= other 255 newobj._check() 256 return newobj 257 258 def __sub__(self, other): 259 newobj = self.__class__(self) 260 newobj.value -= other 261 newobj._check() 262 return newobj 263 264# Metaclass for bounds-checked integer parameters. See CheckedInt. 265class CheckedIntType(type): 266 def __init__(cls, name, bases, dict): 267 super(CheckedIntType, cls).__init__(name, bases, dict) 268 269 # CheckedInt is an abstract base class, so we actually don't 270 # want to do any processing on it... the rest of this code is 271 # just for classes that derive from CheckedInt. 272 if name == 'CheckedInt': 273 return 274 275 if not cls.cxx_predecls: 276 # most derived types require this, so we just do it here once 277 cls.cxx_predecls = ['#include "sim/host.hh"'] 278 279 if not cls.swig_predecls: 280 # most derived types require this, so we just do it here once 281 cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 282 '%import "sim/host.hh"'] 283 284 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 285 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 286 panic("CheckedInt subclass %s must define either\n" \ 287 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 288 % name); 289 if cls.unsigned: 290 cls.min = 0 291 cls.max = 2 ** cls.size - 1 292 else: 293 cls.min = -(2 ** (cls.size - 1)) 294 cls.max = (2 ** (cls.size - 1)) - 1 295 296# Abstract superclass for bounds-checked integer parameters. This 297# class is subclassed to generate parameter classes with specific 298# bounds. Initialization of the min and max bounds is done in the 299# metaclass CheckedIntType.__init__. 300class CheckedInt(NumericParamValue): 301 __metaclass__ = CheckedIntType 302 303 def _check(self): 304 if not self.min <= self.value <= self.max: 305 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 306 (self.min, self.value, self.max) 307 308 def __init__(self, value): 309 if isinstance(value, str): 310 self.value = convert.toInteger(value) 311 elif isinstance(value, (int, long, float)): 312 self.value = long(value) 313 self._check() 314 315class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 316class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 317 318class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 319class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 320class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 321class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 322class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 323class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 324class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 325class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 326 327class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 328class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 329class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 330class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 331 332class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 333 334class Float(ParamValue, float): 335 pass 336 337class MemorySize(CheckedInt): 338 cxx_type = 'uint64_t' 339 size = 64 340 unsigned = True 341 def __init__(self, value): 342 if isinstance(value, MemorySize): 343 self.value = value.value 344 else: 345 self.value = convert.toMemorySize(value) 346 self._check() 347 348class MemorySize32(CheckedInt): 349 size = 32 350 unsigned = True 351 def __init__(self, value): 352 if isinstance(value, MemorySize): 353 self.value = value.value 354 else: 355 self.value = convert.toMemorySize(value) 356 self._check() 357 358class Addr(CheckedInt): 359 cxx_type = 'Addr' 360 cxx_predecls = ['#include "targetarch/isa_traits.hh"'] 361 size = 64 362 unsigned = True 363 def __init__(self, value): 364 if isinstance(value, Addr): 365 self.value = value.value 366 else: 367 try: 368 self.value = convert.toMemorySize(value) 369 except TypeError: 370 self.value = long(value) 371 self._check() 372 373 374class MetaRange(type): 375 def __init__(cls, name, bases, dict): 376 super(MetaRange, cls).__init__(name, bases, dict) 377 if name == 'Range': 378 return 379 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 380 cls.cxx_predecls = \ 381 ['#include "base/range.hh"'] + cls.type.cxx_predecls 382 383class Range(ParamValue): 384 __metaclass__ = MetaRange 385 type = Int # default; can be overridden in subclasses 386 def __init__(self, *args, **kwargs): 387 def handle_kwargs(self, kwargs): 388 if 'end' in kwargs: 389 self.second = self.type(kwargs.pop('end')) 390 elif 'size' in kwargs: 391 self.second = self.first + self.type(kwargs.pop('size')) - 1 392 else: 393 raise TypeError, "Either end or size must be specified" 394 395 if len(args) == 0: 396 self.first = self.type(kwargs.pop('start')) 397 handle_kwargs(self, kwargs) 398 399 elif len(args) == 1: 400 if kwargs: 401 self.first = self.type(args[0]) 402 handle_kwargs(self, kwargs) 403 elif isinstance(args[0], Range): 404 self.first = self.type(args[0].first) 405 self.second = self.type(args[0].second) 406 else: 407 self.first = self.type(0) 408 self.second = self.type(args[0]) - 1 409 410 elif len(args) == 2: 411 self.first = self.type(args[0]) 412 self.second = self.type(args[1]) 413 else: 414 raise TypeError, "Too many arguments specified" 415 416 if kwargs: 417 raise TypeError, "too many keywords: %s" % kwargs.keys() 418 419 def __str__(self): 420 return '%s:%s' % (self.first, self.second) 421 422class AddrRange(Range): 423 type = Addr 424 425class TickRange(Range): 426 type = Tick 427 428# Boolean parameter type. Python doesn't let you subclass bool, since 429# it doesn't want to let you create multiple instances of True and 430# False. Thus this is a little more complicated than String. 431class Bool(ParamValue): 432 cxx_type = 'bool' 433 def __init__(self, value): 434 try: 435 self.value = convert.toBool(value) 436 except TypeError: 437 self.value = bool(value) 438 439 def __str__(self): 440 return str(self.value) 441 442 def ini_str(self): 443 if self.value: 444 return 'true' 445 return 'false' 446 447def IncEthernetAddr(addr, val = 1): 448 bytes = map(lambda x: int(x, 16), addr.split(':')) 449 bytes[5] += val 450 for i in (5, 4, 3, 2, 1): 451 val,rem = divmod(bytes[i], 256) 452 bytes[i] = rem 453 if val == 0: 454 break 455 bytes[i - 1] += val 456 assert(bytes[0] <= 255) 457 return ':'.join(map(lambda x: '%02x' % x, bytes)) 458 459class NextEthernetAddr(object): 460 addr = "00:90:00:00:00:01" 461 462 def __init__(self, inc = 1): 463 self.value = NextEthernetAddr.addr 464 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) 465 466class EthernetAddr(ParamValue): 467 cxx_type = 'Net::EthAddr' 468 cxx_predecls = ['#include "base/inet.hh"'] 469 swig_predecls = ['class Net::EthAddr;'] 470 def __init__(self, value): 471 if value == NextEthernetAddr: 472 self.value = value 473 return 474 475 if not isinstance(value, str): 476 raise TypeError, "expected an ethernet address and didn't get one" 477 478 bytes = value.split(':') 479 if len(bytes) != 6: 480 raise TypeError, 'invalid ethernet address %s' % value 481 482 for byte in bytes: 483 if not 0 <= int(byte) <= 256: 484 raise TypeError, 'invalid ethernet address %s' % value 485 486 self.value = value 487 488 def unproxy(self, base): 489 if self.value == NextEthernetAddr: 490 self.addr = self.value().value 491 return self 492 493 def __str__(self): 494 if self.value == NextEthernetAddr: 495 if hasattr(self, 'addr'): 496 return self.addr 497 else: 498 return "NextEthernetAddr (unresolved)" 499 else: 500 return self.value 501 502# Enumerated types are a little more complex. The user specifies the 503# type as Enum(foo) where foo is either a list or dictionary of 504# alternatives (typically strings, but not necessarily so). (In the 505# long run, the integer value of the parameter will be the list index 506# or the corresponding dictionary value. For now, since we only check 507# that the alternative is valid and then spit it into a .ini file, 508# there's not much point in using the dictionary.) 509 510# What Enum() must do is generate a new type encapsulating the 511# provided list/dictionary so that specific values of the parameter 512# can be instances of that type. We define two hidden internal 513# classes (_ListEnum and _DictEnum) to serve as base classes, then 514# derive the new type from the appropriate base class on the fly. 515 516 517# Metaclass for Enum types 518class MetaEnum(type): 519 def __init__(cls, name, bases, init_dict): 520 if init_dict.has_key('map'): 521 if not isinstance(cls.map, dict): 522 raise TypeError, "Enum-derived class attribute 'map' " \ 523 "must be of type dict" 524 # build list of value strings from map 525 cls.vals = cls.map.keys() 526 cls.vals.sort() 527 elif init_dict.has_key('vals'): 528 if not isinstance(cls.vals, list): 529 raise TypeError, "Enum-derived class attribute 'vals' " \ 530 "must be of type list" 531 # build string->value map from vals sequence 532 cls.map = {} 533 for idx,val in enumerate(cls.vals): 534 cls.map[val] = idx 535 else: 536 raise TypeError, "Enum-derived class must define "\ 537 "attribute 'map' or 'vals'" 538 539 cls.cxx_type = name + '::Enum' 540 541 super(MetaEnum, cls).__init__(name, bases, init_dict) 542 543 # Generate C++ class declaration for this enum type. 544 # Note that we wrap the enum in a class/struct to act as a namespace, 545 # so that the enum strings can be brief w/o worrying about collisions. 546 def cxx_decl(cls): 547 s = 'struct %s {\n enum Enum {\n ' % cls.__name__ 548 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 549 s += '\n };\n};\n' 550 return s 551 552# Base class for enum types. 553class Enum(ParamValue): 554 __metaclass__ = MetaEnum 555 vals = [] 556 557 def __init__(self, value): 558 if value not in self.map: 559 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 560 % (value, self.vals) 561 self.value = value 562 563 def __str__(self): 564 return self.value 565 566ticks_per_sec = None 567 568# how big does a rounding error need to be before we warn about it? 569frequency_tolerance = 0.001 # 0.1% 570 571# convert a floting-point # of ticks to integer, and warn if rounding 572# discards too much precision 573def tick_check(float_ticks): 574 if float_ticks == 0: 575 return 0 576 int_ticks = int(round(float_ticks)) 577 err = (float_ticks - int_ticks) / float_ticks 578 if err > frequency_tolerance: 579 print >> sys.stderr, "Warning: rounding error > tolerance" 580 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) 581 #raise ValueError 582 return int_ticks 583 584def getLatency(value): 585 if isinstance(value, Latency) or isinstance(value, Clock): 586 return value.value 587 elif isinstance(value, Frequency) or isinstance(value, RootClock): 588 return 1 / value.value 589 elif isinstance(value, str): 590 try: 591 return convert.toLatency(value) 592 except ValueError: 593 try: 594 return 1 / convert.toFrequency(value) 595 except ValueError: 596 pass # fall through 597 raise ValueError, "Invalid Frequency/Latency value '%s'" % value 598 599 600class Latency(NumericParamValue): 601 cxx_type = 'Tick' 602 cxx_predecls = ['#include "sim/host.hh"'] 603 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 604 '%import "sim/host.hh"'] 605 def __init__(self, value): 606 self.value = getLatency(value) 607 608 def __getattr__(self, attr): 609 if attr in ('latency', 'period'): 610 return self 611 if attr == 'frequency': 612 return Frequency(self) 613 raise AttributeError, "Latency object has no attribute '%s'" % attr 614 615 # convert latency to ticks 616 def ini_str(self): 617 return str(tick_check(self.value * ticks_per_sec)) 618 619class Frequency(NumericParamValue): 620 cxx_type = 'Tick' 621 cxx_predecls = ['#include "sim/host.hh"'] 622 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 623 '%import "sim/host.hh"'] 624 def __init__(self, value): 625 self.value = 1 / getLatency(value) 626 627 def __getattr__(self, attr): 628 if attr == 'frequency': 629 return self 630 if attr in ('latency', 'period'): 631 return Latency(self) 632 raise AttributeError, "Frequency object has no attribute '%s'" % attr 633 634 # convert frequency to ticks per period 635 def ini_str(self): 636 return self.period.ini_str() 637 638# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). 639# We can't inherit from Frequency because we don't want it to be directly 640# assignable to a regular Frequency parameter. 641class RootClock(ParamValue): 642 cxx_type = 'Tick' 643 cxx_predecls = ['#include "sim/host.hh"'] 644 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 645 '%import "sim/host.hh"'] 646 def __init__(self, value): 647 self.value = 1 / getLatency(value) 648 649 def __getattr__(self, attr): 650 if attr == 'frequency': 651 return Frequency(self) 652 if attr in ('latency', 'period'): 653 return Latency(self) 654 raise AttributeError, "Frequency object has no attribute '%s'" % attr 655 656 def ini_str(self): 657 return str(tick_check(self.value)) 658 659# A generic frequency and/or Latency value. Value is stored as a latency, 660# but to avoid ambiguity this object does not support numeric ops (* or /). 661# An explicit conversion to a Latency or Frequency must be made first. 662class Clock(ParamValue): 663 cxx_type = 'Tick' 664 cxx_predecls = ['#include "sim/host.hh"'] 665 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 666 '%import "sim/host.hh"'] 667 def __init__(self, value): 668 self.value = getLatency(value) 669 670 def __getattr__(self, attr): 671 if attr == 'frequency': 672 return Frequency(self) 673 if attr in ('latency', 'period'): 674 return Latency(self) 675 raise AttributeError, "Frequency object has no attribute '%s'" % attr 676 677 def ini_str(self): 678 return self.period.ini_str() 679 680class NetworkBandwidth(float,ParamValue): 681 cxx_type = 'float' 682 def __new__(cls, value): 683 val = convert.toNetworkBandwidth(value) / 8.0 684 return super(cls, NetworkBandwidth).__new__(cls, val) 685 686 def __str__(self): 687 return str(self.val) 688 689 def ini_str(self): 690 return '%f' % (ticks_per_sec / float(self)) 691 692class MemoryBandwidth(float,ParamValue): 693 cxx_type = 'float' 694 def __new__(self, value): 695 val = convert.toMemoryBandwidth(value) 696 return super(cls, MemoryBandwidth).__new__(cls, val) 697 698 def __str__(self): 699 return str(self.val) 700 701 def ini_str(self): 702 return '%f' % (ticks_per_sec / float(self)) 703 704# 705# "Constants"... handy aliases for various values. 706# 707 708# Special class for NULL pointers. Note the special check in 709# make_param_value() above that lets these be assigned where a 710# SimObject is required. 711# only one copy of a particular node 712class NullSimObject(object): 713 __metaclass__ = Singleton 714 715 def __call__(cls): 716 return cls 717 718 def _instantiate(self, parent = None, path = ''): 719 pass 720 721 def ini_str(self): 722 return 'Null' 723 724 def unproxy(self, base): 725 return self 726 727 def set_path(self, parent, name): 728 pass 729 def __str__(self): 730 return 'Null' 731 732# The only instance you'll ever need... 733NULL = NullSimObject() 734 735def isNullPointer(value): 736 return isinstance(value, NullSimObject) 737 738# Some memory range specifications use this as a default upper bound. 739MaxAddr = Addr.max 740MaxTick = Tick.max 741AllMemory = AddrRange(0, MaxAddr) 742 743 744##################################################################### 745# 746# Port objects 747# 748# Ports are used to interconnect objects in the memory system. 749# 750##################################################################### 751 752# Port reference: encapsulates a reference to a particular port on a 753# particular SimObject. 754class PortRef(object): 755 def __init__(self, simobj, name): 756 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 757 self.simobj = simobj 758 self.name = name 759 self.peer = None # not associated with another port yet 760 self.ccConnected = False # C++ port connection done? 761 self.index = -1 # always -1 for non-vector ports 762 763 def __str__(self): 764 return '%s.%s' % (self.simobj, self.name) 765 766 # for config.ini, print peer's name (not ours) 767 def ini_str(self): 768 return str(self.peer) 769 770 def __getattr__(self, attr): 771 if attr == 'peerObj': 772 # shorthand for proxies 773 return self.peer.simobj 774 raise AttributeError, "'%s' object has no attribute '%s'" % \ 775 (self.__class__.__name__, attr) 776 777 # Full connection is symmetric (both ways). Called via 778 # SimObject.__setattr__ as a result of a port assignment, e.g., 779 # "obj1.portA = obj2.portB", or via VectorPortRef.__setitem__, 780 # e.g., "obj1.portA[3] = obj2.portB". 781 def connect(self, other): 782 if isinstance(other, VectorPortRef): 783 # reference to plain VectorPort is implicit append 784 other = other._get_next() 785 if not (isinstance(other, PortRef) or proxy.isproxy(other)): 786 raise TypeError, \ 787 "assigning non-port reference '%s' to port '%s'" \ 788 % (other, self) 789 if self.peer and not proxy.isproxy(self.peer): 790 print "warning: overwriting port", self, \ 791 "value", self.peer, "with", other 792 self.peer = other 793 assert(not isinstance(self.peer, VectorPortRef)) 794 if isinstance(other, PortRef) and other.peer is not self: 795 other.connect(self) 796 797 def clone(self, simobj, memo): 798 if memo.has_key(self): 799 return memo[self] 800 newRef = copy.copy(self) 801 memo[self] = newRef 802 newRef.simobj = simobj 803 assert(isSimObject(newRef.simobj)) 804 if self.peer and not proxy.isproxy(self.peer): 805 peerObj = memo[self.peer.simobj] 806 newRef.peer = self.peer.clone(peerObj, memo) 807 assert(not isinstance(newRef.peer, VectorPortRef)) 808 return newRef 809 810 def unproxy(self, simobj): 811 assert(simobj is self.simobj) 812 if proxy.isproxy(self.peer): 813 try: 814 realPeer = self.peer.unproxy(self.simobj) 815 except: 816 print "Error in unproxying port '%s' of %s" % \ 817 (self.name, self.simobj.path()) 818 raise 819 self.connect(realPeer) 820 821 # Call C++ to create corresponding port connection between C++ objects 822 def ccConnect(self): 823 if self.ccConnected: # already done this 824 return 825 peer = self.peer 826 cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, 827 peer.simobj.getCCObject(), peer.name, peer.index) 828 self.ccConnected = True 829 peer.ccConnected = True 830 831# A reference to an individual element of a VectorPort... much like a 832# PortRef, but has an index. 833class VectorPortElementRef(PortRef): 834 def __init__(self, simobj, name, index): 835 PortRef.__init__(self, simobj, name) 836 self.index = index 837 838 def __str__(self): 839 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 840 841# A reference to a complete vector-valued port (not just a single element). 842# Can be indexed to retrieve individual VectorPortElementRef instances. 843class VectorPortRef(object): 844 def __init__(self, simobj, name): 845 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 846 self.simobj = simobj 847 self.name = name 848 self.elements = [] 849 850 # for config.ini, print peer's name (not ours) 851 def ini_str(self): 852 return ' '.join([el.ini_str() for el in self.elements]) 853 854 def __getitem__(self, key): 855 if not isinstance(key, int): 856 raise TypeError, "VectorPort index must be integer" 857 if key >= len(self.elements): 858 # need to extend list 859 ext = [VectorPortElementRef(self.simobj, self.name, i) 860 for i in range(len(self.elements), key+1)] 861 self.elements.extend(ext) 862 return self.elements[key] 863 864 def _get_next(self): 865 return self[len(self.elements)] 866 867 def __setitem__(self, key, value): 868 if not isinstance(key, int): 869 raise TypeError, "VectorPort index must be integer" 870 self[key].connect(value) 871 872 def connect(self, other): 873 # reference to plain VectorPort is implicit append 874 self._get_next().connect(other) 875 876 def unproxy(self, simobj): 877 [el.unproxy(simobj) for el in self.elements] 878 879 def ccConnect(self): 880 [el.ccConnect() for el in self.elements] 881 882# Port description object. Like a ParamDesc object, this represents a 883# logical port in the SimObject class, not a particular port on a 884# SimObject instance. The latter are represented by PortRef objects. 885class Port(object): 886 # Port("description") or Port(default, "description") 887 def __init__(self, *args): 888 if len(args) == 1: 889 self.desc = args[0] 890 elif len(args) == 2: 891 self.default = args[0] 892 self.desc = args[1] 893 else: 894 raise TypeError, 'wrong number of arguments' 895 # self.name is set by SimObject class on assignment 896 # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 897 898 # Generate a PortRef for this port on the given SimObject with the 899 # given name 900 def makeRef(self, simobj): 901 return PortRef(simobj, self.name) 902 903 # Connect an instance of this port (on the given SimObject with 904 # the given name) with the port described by the supplied PortRef 905 def connect(self, simobj, ref): 906 self.makeRef(simobj).connect(ref) 907 908# VectorPort description object. Like Port, but represents a vector 909# of connections (e.g., as on a Bus). 910class VectorPort(Port): 911 def __init__(self, *args): 912 Port.__init__(self, *args) 913 self.isVec = True 914 915 def makeRef(self, simobj): 916 return VectorPortRef(simobj, self.name) 917 918 919 920__all__ = ['Param', 'VectorParam', 921 'Enum', 'Bool', 'String', 'Float', 922 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 923 'Int32', 'UInt32', 'Int64', 'UInt64', 924 'Counter', 'Addr', 'Tick', 'Percent', 925 'TcpPort', 'UdpPort', 'EthernetAddr', 926 'MemorySize', 'MemorySize32', 927 'Latency', 'Frequency', 'RootClock', 'Clock', 928 'NetworkBandwidth', 'MemoryBandwidth', 929 'Range', 'AddrRange', 'TickRange', 930 'MaxAddr', 'MaxTick', 'AllMemory', 931 'NextEthernetAddr', 'NULL', 932 'Port', 'VectorPort'] 933 934# see comment on imports at end of __init__.py. 935from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass 936import proxy 937import objects 938import cc_main 939