params.py revision 10676
16657Snate@binkert.org# Copyright (c) 2012-2014 ARM Limited 26657Snate@binkert.org# All rights reserved. 36657Snate@binkert.org# 46657Snate@binkert.org# The license below extends only to copyright in the software and shall 56657Snate@binkert.org# not be construed as granting a license to any other intellectual 66657Snate@binkert.org# property including but not limited to intellectual property relating 76657Snate@binkert.org# to a hardware implementation of the functionality of the software 86657Snate@binkert.org# licensed hereunder. You may use the software subject to the license 96657Snate@binkert.org# terms below provided that you ensure that this notice is replicated 106657Snate@binkert.org# unmodified and in its entirety in all distributions of the software, 116657Snate@binkert.org# modified or unmodified, in source code or in binary form. 126657Snate@binkert.org# 136657Snate@binkert.org# Copyright (c) 2004-2006 The Regents of The University of Michigan 146657Snate@binkert.org# Copyright (c) 2010-2011 Advanced Micro Devices, Inc. 156657Snate@binkert.org# All rights reserved. 166657Snate@binkert.org# 176657Snate@binkert.org# Redistribution and use in source and binary forms, with or without 186657Snate@binkert.org# modification, are permitted provided that the following conditions are 196657Snate@binkert.org# met: redistributions of source code must retain the above copyright 206657Snate@binkert.org# notice, this list of conditions and the following disclaimer; 216657Snate@binkert.org# redistributions in binary form must reproduce the above copyright 226657Snate@binkert.org# notice, this list of conditions and the following disclaimer in the 236657Snate@binkert.org# documentation and/or other materials provided with the distribution; 246657Snate@binkert.org# neither the name of the copyright holders nor the names of its 256657Snate@binkert.org# contributors may be used to endorse or promote products derived from 266657Snate@binkert.org# this software without specific prior written permission. 276657Snate@binkert.org# 286657Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 296657Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 306657Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 316657Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 326657Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 339298Snilay@cs.wisc.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 346657Snate@binkert.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 356657Snate@binkert.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 366657Snate@binkert.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 376657Snate@binkert.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 386657Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 396657Snate@binkert.org# 408337Snilay@cs.wisc.edu# Authors: Steve Reinhardt 419298Snilay@cs.wisc.edu# Nathan Binkert 426657Snate@binkert.org# Gabe Black 436657Snate@binkert.org# Andreas Hansson 446657Snate@binkert.org 456657Snate@binkert.org##################################################################### 466657Snate@binkert.org# 476657Snate@binkert.org# Parameter description classes 486657Snate@binkert.org# 496657Snate@binkert.org# The _params dictionary in each class maps parameter names to either 506657Snate@binkert.org# a Param or a VectorParam object. These objects contain the 516657Snate@binkert.org# parameter description string, the parameter type, and the default 526657Snate@binkert.org# value (if any). The convert() method on these objects is used to 536657Snate@binkert.org# force whatever value is assigned to the parameter to the appropriate 546657Snate@binkert.org# type. 557839Snilay@cs.wisc.edu# 567839Snilay@cs.wisc.edu# Note that the default values are loaded into the class's attribute 576657Snate@binkert.org# space when the parameter dictionary is initialized (in 586657Snate@binkert.org# MetaSimObject._new_param()); after that point they aren't used. 596657Snate@binkert.org# 606657Snate@binkert.org##################################################################### 619219Spower.jg@gmail.com 628478Snilay@cs.wisc.eduimport copy 638478Snilay@cs.wisc.eduimport datetime 648478Snilay@cs.wisc.eduimport re 656657Snate@binkert.orgimport sys 666657Snate@binkert.orgimport time 678478Snilay@cs.wisc.eduimport math 686657Snate@binkert.org 696999Snate@binkert.orgimport proxy 706657Snate@binkert.orgimport ticks 716657Snate@binkert.orgfrom util import * 726657Snate@binkert.org 736657Snate@binkert.orgdef isSimObject(*args, **kwargs): 746657Snate@binkert.org return SimObject.isSimObject(*args, **kwargs) 756657Snate@binkert.org 767839Snilay@cs.wisc.edudef isSimObjectSequence(*args, **kwargs): 777839Snilay@cs.wisc.edu return SimObject.isSimObjectSequence(*args, **kwargs) 786657Snate@binkert.org 796657Snate@binkert.orgdef isSimObjectClass(*args, **kwargs): 806657Snate@binkert.org return SimObject.isSimObjectClass(*args, **kwargs) 816657Snate@binkert.org 827007Snate@binkert.orgallParams = {} 839298Snilay@cs.wisc.edu 846657Snate@binkert.orgclass MetaParamValue(type): 856657Snate@binkert.org def __new__(mcls, name, bases, dct): 866657Snate@binkert.org cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct) 876657Snate@binkert.org assert name not in allParams 888478Snilay@cs.wisc.edu allParams[name] = cls 896657Snate@binkert.org return cls 906657Snate@binkert.org 91 92# Dummy base class to identify types that are legitimate for SimObject 93# parameters. 94class ParamValue(object): 95 __metaclass__ = MetaParamValue 96 cmd_line_settable = False 97 98 # Generate the code needed as a prerequisite for declaring a C++ 99 # object of this type. Typically generates one or more #include 100 # statements. Used when declaring parameters of this type. 101 @classmethod 102 def cxx_predecls(cls, code): 103 pass 104 105 # Generate the code needed as a prerequisite for including a 106 # reference to a C++ object of this type in a SWIG .i file. 107 # Typically generates one or more %import or %include statements. 108 @classmethod 109 def swig_predecls(cls, code): 110 pass 111 112 # default for printing to .ini file is regular string conversion. 113 # will be overridden in some cases 114 def ini_str(self): 115 return str(self) 116 117 # default for printing to .json file is regular string conversion. 118 # will be overridden in some cases, mostly to use native Python 119 # types where there are similar JSON types 120 def config_value(self): 121 return str(self) 122 123 # Prerequisites for .ini parsing with cxx_ini_parse 124 @classmethod 125 def cxx_ini_predecls(cls, code): 126 pass 127 128 # parse a .ini file entry for this param from string expression 129 # src into lvalue dest (of the param's C++ type) 130 @classmethod 131 def cxx_ini_parse(cls, code, src, dest, ret): 132 code('// Unhandled param type: %s' % cls.__name__) 133 code('%s false;' % ret) 134 135 # allows us to blithely call unproxy() on things without checking 136 # if they're really proxies or not 137 def unproxy(self, base): 138 return self 139 140 # Produce a human readable version of the stored value 141 def pretty_print(self, value): 142 return str(value) 143 144# Regular parameter description. 145class ParamDesc(object): 146 def __init__(self, ptype_str, ptype, *args, **kwargs): 147 self.ptype_str = ptype_str 148 # remember ptype only if it is provided 149 if ptype != None: 150 self.ptype = ptype 151 152 if args: 153 if len(args) == 1: 154 self.desc = args[0] 155 elif len(args) == 2: 156 self.default = args[0] 157 self.desc = args[1] 158 else: 159 raise TypeError, 'too many arguments' 160 161 if kwargs.has_key('desc'): 162 assert(not hasattr(self, 'desc')) 163 self.desc = kwargs['desc'] 164 del kwargs['desc'] 165 166 if kwargs.has_key('default'): 167 assert(not hasattr(self, 'default')) 168 self.default = kwargs['default'] 169 del kwargs['default'] 170 171 if kwargs: 172 raise TypeError, 'extra unknown kwargs %s' % kwargs 173 174 if not hasattr(self, 'desc'): 175 raise TypeError, 'desc attribute missing' 176 177 def __getattr__(self, attr): 178 if attr == 'ptype': 179 ptype = SimObject.allClasses[self.ptype_str] 180 assert isSimObjectClass(ptype) 181 self.ptype = ptype 182 return ptype 183 184 raise AttributeError, "'%s' object has no attribute '%s'" % \ 185 (type(self).__name__, attr) 186 187 def example_str(self): 188 if hasattr(self.ptype, "ex_str"): 189 return self.ptype.ex_str 190 else: 191 return self.ptype_str 192 193 # Is the param available to be exposed on the command line 194 def isCmdLineSettable(self): 195 if hasattr(self.ptype, "cmd_line_settable"): 196 return self.ptype.cmd_line_settable 197 else: 198 return False 199 200 def convert(self, value): 201 if isinstance(value, proxy.BaseProxy): 202 value.set_param_desc(self) 203 return value 204 if not hasattr(self, 'ptype') and isNullPointer(value): 205 # deferred evaluation of SimObject; continue to defer if 206 # we're just assigning a null pointer 207 return value 208 if isinstance(value, self.ptype): 209 return value 210 if isNullPointer(value) and isSimObjectClass(self.ptype): 211 return value 212 return self.ptype(value) 213 214 def pretty_print(self, value): 215 if isinstance(value, proxy.BaseProxy): 216 return str(value) 217 if isNullPointer(value): 218 return NULL 219 return self.ptype(value).pretty_print(value) 220 221 def cxx_predecls(self, code): 222 code('#include <cstddef>') 223 self.ptype.cxx_predecls(code) 224 225 def swig_predecls(self, code): 226 self.ptype.swig_predecls(code) 227 228 def cxx_decl(self, code): 229 code('${{self.ptype.cxx_type}} ${{self.name}};') 230 231# Vector-valued parameter description. Just like ParamDesc, except 232# that the value is a vector (list) of the specified type instead of a 233# single value. 234 235class VectorParamValue(list): 236 __metaclass__ = MetaParamValue 237 def __setattr__(self, attr, value): 238 raise AttributeError, \ 239 "Not allowed to set %s on '%s'" % (attr, type(self).__name__) 240 241 def config_value(self): 242 return [v.config_value() for v in self] 243 244 def ini_str(self): 245 return ' '.join([v.ini_str() for v in self]) 246 247 def getValue(self): 248 return [ v.getValue() for v in self ] 249 250 def unproxy(self, base): 251 if len(self) == 1 and isinstance(self[0], proxy.AllProxy): 252 return self[0].unproxy(base) 253 else: 254 return [v.unproxy(base) for v in self] 255 256class SimObjectVector(VectorParamValue): 257 # support clone operation 258 def __call__(self, **kwargs): 259 return SimObjectVector([v(**kwargs) for v in self]) 260 261 def clear_parent(self, old_parent): 262 for v in self: 263 v.clear_parent(old_parent) 264 265 def set_parent(self, parent, name): 266 if len(self) == 1: 267 self[0].set_parent(parent, name) 268 else: 269 width = int(math.ceil(math.log(len(self))/math.log(10))) 270 for i,v in enumerate(self): 271 v.set_parent(parent, "%s%0*d" % (name, width, i)) 272 273 def has_parent(self): 274 return reduce(lambda x,y: x and y, [v.has_parent() for v in self]) 275 276 # return 'cpu0 cpu1' etc. for print_ini() 277 def get_name(self): 278 return ' '.join([v._name for v in self]) 279 280 # By iterating through the constituent members of the vector here 281 # we can nicely handle iterating over all a SimObject's children 282 # without having to provide lots of special functions on 283 # SimObjectVector directly. 284 def descendants(self): 285 for v in self: 286 for obj in v.descendants(): 287 yield obj 288 289 def get_config_as_dict(self): 290 a = [] 291 for v in self: 292 a.append(v.get_config_as_dict()) 293 return a 294 295 # If we are replacing an item in the vector, make sure to set the 296 # parent reference of the new SimObject to be the same as the parent 297 # of the SimObject being replaced. Useful to have if we created 298 # a SimObjectVector of temporary objects that will be modified later in 299 # configuration scripts. 300 def __setitem__(self, key, value): 301 val = self[key] 302 if value.has_parent(): 303 warn("SimObject %s already has a parent" % value.get_name() +\ 304 " that is being overwritten by a SimObjectVector") 305 value.set_parent(val.get_parent(), val._name) 306 super(SimObjectVector, self).__setitem__(key, value) 307 308 # Enumerate the params of each member of the SimObject vector. Creates 309 # strings that will allow indexing into the vector by the python code and 310 # allow it to be specified on the command line. 311 def enumerateParams(self, flags_dict = {}, 312 cmd_line_str = "", 313 access_str = ""): 314 if hasattr(self, "_paramEnumed"): 315 print "Cycle detected enumerating params at %s?!" % (cmd_line_str) 316 else: 317 x = 0 318 for vals in self: 319 # Each entry in the SimObjectVector should be an 320 # instance of a SimObject 321 flags_dict = vals.enumerateParams(flags_dict, 322 cmd_line_str + "%d." % x, 323 access_str + "[%d]." % x) 324 x = x + 1 325 326 return flags_dict 327 328class VectorParamDesc(ParamDesc): 329 # Convert assigned value to appropriate type. If the RHS is not a 330 # list or tuple, it generates a single-element list. 331 def convert(self, value): 332 if isinstance(value, (list, tuple)): 333 # list: coerce each element into new list 334 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 335 elif isinstance(value, str): 336 # If input is a csv string 337 tmp_list = [ ParamDesc.convert(self, v) \ 338 for v in value.strip('[').strip(']').split(',') ] 339 else: 340 # singleton: coerce to a single-element list 341 tmp_list = [ ParamDesc.convert(self, value) ] 342 343 if isSimObjectSequence(tmp_list): 344 return SimObjectVector(tmp_list) 345 else: 346 return VectorParamValue(tmp_list) 347 348 # Produce a human readable example string that describes 349 # how to set this vector parameter in the absence of a default 350 # value. 351 def example_str(self): 352 s = super(VectorParamDesc, self).example_str() 353 help_str = "[" + s + "," + s + ", ...]" 354 return help_str 355 356 # Produce a human readable representation of the value of this vector param. 357 def pretty_print(self, value): 358 if isinstance(value, (list, tuple)): 359 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ] 360 elif isinstance(value, str): 361 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ] 362 else: 363 tmp_list = [ ParamDesc.pretty_print(self, value) ] 364 365 return tmp_list 366 367 # This is a helper function for the new config system 368 def __call__(self, value): 369 if isinstance(value, (list, tuple)): 370 # list: coerce each element into new list 371 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 372 elif isinstance(value, str): 373 # If input is a csv string 374 tmp_list = [ ParamDesc.convert(self, v) \ 375 for v in value.strip('[').strip(']').split(',') ] 376 else: 377 # singleton: coerce to a single-element list 378 tmp_list = [ ParamDesc.convert(self, value) ] 379 380 return VectorParamValue(tmp_list) 381 382 def swig_module_name(self): 383 return "%s_vector" % self.ptype_str 384 385 def swig_predecls(self, code): 386 code('%import "${{self.swig_module_name()}}.i"') 387 388 def swig_decl(self, code): 389 code('%module(package="m5.internal") ${{self.swig_module_name()}}') 390 code('%{') 391 self.ptype.cxx_predecls(code) 392 code('%}') 393 code() 394 # Make sure the SWIGPY_SLICE_ARG is defined through this inclusion 395 code('%include "std_container.i"') 396 code() 397 self.ptype.swig_predecls(code) 398 code() 399 code('%include "std_vector.i"') 400 code() 401 402 ptype = self.ptype_str 403 cxx_type = self.ptype.cxx_type 404 405 code('%template(vector_$ptype) std::vector< $cxx_type >;') 406 407 def cxx_predecls(self, code): 408 code('#include <vector>') 409 self.ptype.cxx_predecls(code) 410 411 def cxx_decl(self, code): 412 code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};') 413 414class ParamFactory(object): 415 def __init__(self, param_desc_class, ptype_str = None): 416 self.param_desc_class = param_desc_class 417 self.ptype_str = ptype_str 418 419 def __getattr__(self, attr): 420 if self.ptype_str: 421 attr = self.ptype_str + '.' + attr 422 return ParamFactory(self.param_desc_class, attr) 423 424 # E.g., Param.Int(5, "number of widgets") 425 def __call__(self, *args, **kwargs): 426 ptype = None 427 try: 428 ptype = allParams[self.ptype_str] 429 except KeyError: 430 # if name isn't defined yet, assume it's a SimObject, and 431 # try to resolve it later 432 pass 433 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 434 435Param = ParamFactory(ParamDesc) 436VectorParam = ParamFactory(VectorParamDesc) 437 438##################################################################### 439# 440# Parameter Types 441# 442# Though native Python types could be used to specify parameter types 443# (the 'ptype' field of the Param and VectorParam classes), it's more 444# flexible to define our own set of types. This gives us more control 445# over how Python expressions are converted to values (via the 446# __init__() constructor) and how these values are printed out (via 447# the __str__() conversion method). 448# 449##################################################################### 450 451# String-valued parameter. Just mixin the ParamValue class with the 452# built-in str class. 453class String(ParamValue,str): 454 cxx_type = 'std::string' 455 cmd_line_settable = True 456 457 @classmethod 458 def cxx_predecls(self, code): 459 code('#include <string>') 460 461 @classmethod 462 def swig_predecls(cls, code): 463 code('%include "std_string.i"') 464 465 def __call__(self, value): 466 self = value 467 return value 468 469 @classmethod 470 def cxx_ini_parse(self, code, src, dest, ret): 471 code('%s = %s;' % (dest, src)) 472 code('%s true;' % ret) 473 474 def getValue(self): 475 return self 476 477# superclass for "numeric" parameter values, to emulate math 478# operations in a type-safe way. e.g., a Latency times an int returns 479# a new Latency object. 480class NumericParamValue(ParamValue): 481 def __str__(self): 482 return str(self.value) 483 484 def __float__(self): 485 return float(self.value) 486 487 def __long__(self): 488 return long(self.value) 489 490 def __int__(self): 491 return int(self.value) 492 493 # hook for bounds checking 494 def _check(self): 495 return 496 497 def __mul__(self, other): 498 newobj = self.__class__(self) 499 newobj.value *= other 500 newobj._check() 501 return newobj 502 503 __rmul__ = __mul__ 504 505 def __div__(self, other): 506 newobj = self.__class__(self) 507 newobj.value /= other 508 newobj._check() 509 return newobj 510 511 def __sub__(self, other): 512 newobj = self.__class__(self) 513 newobj.value -= other 514 newobj._check() 515 return newobj 516 517 def config_value(self): 518 return self.value 519 520 @classmethod 521 def cxx_ini_predecls(cls, code): 522 # Assume that base/str.hh will be included anyway 523 # code('#include "base/str.hh"') 524 pass 525 526 # The default for parsing PODs from an .ini entry is to extract from an 527 # istringstream and let overloading choose the right type according to 528 # the dest type. 529 @classmethod 530 def cxx_ini_parse(self, code, src, dest, ret): 531 code('%s to_number(%s, %s);' % (ret, src, dest)) 532 533# Metaclass for bounds-checked integer parameters. See CheckedInt. 534class CheckedIntType(MetaParamValue): 535 def __init__(cls, name, bases, dict): 536 super(CheckedIntType, cls).__init__(name, bases, dict) 537 538 # CheckedInt is an abstract base class, so we actually don't 539 # want to do any processing on it... the rest of this code is 540 # just for classes that derive from CheckedInt. 541 if name == 'CheckedInt': 542 return 543 544 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 545 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 546 panic("CheckedInt subclass %s must define either\n" \ 547 " 'min' and 'max' or 'size' and 'unsigned'\n", 548 name); 549 if cls.unsigned: 550 cls.min = 0 551 cls.max = 2 ** cls.size - 1 552 else: 553 cls.min = -(2 ** (cls.size - 1)) 554 cls.max = (2 ** (cls.size - 1)) - 1 555 556# Abstract superclass for bounds-checked integer parameters. This 557# class is subclassed to generate parameter classes with specific 558# bounds. Initialization of the min and max bounds is done in the 559# metaclass CheckedIntType.__init__. 560class CheckedInt(NumericParamValue): 561 __metaclass__ = CheckedIntType 562 cmd_line_settable = True 563 564 def _check(self): 565 if not self.min <= self.value <= self.max: 566 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 567 (self.min, self.value, self.max) 568 569 def __init__(self, value): 570 if isinstance(value, str): 571 self.value = convert.toInteger(value) 572 elif isinstance(value, (int, long, float, NumericParamValue)): 573 self.value = long(value) 574 else: 575 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 576 % type(value).__name__ 577 self._check() 578 579 def __call__(self, value): 580 self.__init__(value) 581 return value 582 583 @classmethod 584 def cxx_predecls(cls, code): 585 # most derived types require this, so we just do it here once 586 code('#include "base/types.hh"') 587 588 @classmethod 589 def swig_predecls(cls, code): 590 # most derived types require this, so we just do it here once 591 code('%import "stdint.i"') 592 code('%import "base/types.hh"') 593 594 def getValue(self): 595 return long(self.value) 596 597class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 598class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 599 600class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 601class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 602class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 603class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 604class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 605class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 606class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 607class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 608 609class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 610class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 611class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 612class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 613 614class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 615 616class Cycles(CheckedInt): 617 cxx_type = 'Cycles' 618 size = 64 619 unsigned = True 620 621 def getValue(self): 622 from m5.internal.core import Cycles 623 return Cycles(self.value) 624 625 @classmethod 626 def cxx_ini_predecls(cls, code): 627 # Assume that base/str.hh will be included anyway 628 # code('#include "base/str.hh"') 629 pass 630 631 @classmethod 632 def cxx_ini_parse(cls, code, src, dest, ret): 633 code('uint64_t _temp;') 634 code('bool _ret = to_number(%s, _temp);' % src) 635 code('if (_ret)') 636 code(' %s = Cycles(_temp);' % dest) 637 code('%s _ret;' % ret) 638 639class Float(ParamValue, float): 640 cxx_type = 'double' 641 cmd_line_settable = True 642 643 def __init__(self, value): 644 if isinstance(value, (int, long, float, NumericParamValue, Float, str)): 645 self.value = float(value) 646 else: 647 raise TypeError, "Can't convert object of type %s to Float" \ 648 % type(value).__name__ 649 650 def __call__(self, value): 651 self.__init__(value) 652 return value 653 654 def getValue(self): 655 return float(self.value) 656 657 def config_value(self): 658 return self 659 660 @classmethod 661 def cxx_ini_predecls(cls, code): 662 code('#include <sstream>') 663 664 @classmethod 665 def cxx_ini_parse(self, code, src, dest, ret): 666 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 667 668class MemorySize(CheckedInt): 669 cxx_type = 'uint64_t' 670 ex_str = '512MB' 671 size = 64 672 unsigned = True 673 def __init__(self, value): 674 if isinstance(value, MemorySize): 675 self.value = value.value 676 else: 677 self.value = convert.toMemorySize(value) 678 self._check() 679 680class MemorySize32(CheckedInt): 681 cxx_type = 'uint32_t' 682 ex_str = '512MB' 683 size = 32 684 unsigned = True 685 def __init__(self, value): 686 if isinstance(value, MemorySize): 687 self.value = value.value 688 else: 689 self.value = convert.toMemorySize(value) 690 self._check() 691 692class Addr(CheckedInt): 693 cxx_type = 'Addr' 694 size = 64 695 unsigned = True 696 def __init__(self, value): 697 if isinstance(value, Addr): 698 self.value = value.value 699 else: 700 try: 701 # Often addresses are referred to with sizes. Ex: A device 702 # base address is at "512MB". Use toMemorySize() to convert 703 # these into addresses. If the address is not specified with a 704 # "size", an exception will occur and numeric translation will 705 # proceed below. 706 self.value = convert.toMemorySize(value) 707 except (TypeError, ValueError): 708 # Convert number to string and use long() to do automatic 709 # base conversion (requires base=0 for auto-conversion) 710 self.value = long(str(value), base=0) 711 712 self._check() 713 def __add__(self, other): 714 if isinstance(other, Addr): 715 return self.value + other.value 716 else: 717 return self.value + other 718 def pretty_print(self, value): 719 try: 720 val = convert.toMemorySize(value) 721 except TypeError: 722 val = long(value) 723 return "0x%x" % long(val) 724 725class AddrRange(ParamValue): 726 cxx_type = 'AddrRange' 727 728 def __init__(self, *args, **kwargs): 729 # Disable interleaving and hashing by default 730 self.intlvHighBit = 0 731 self.xorHighBit = 0 732 self.intlvBits = 0 733 self.intlvMatch = 0 734 735 def handle_kwargs(self, kwargs): 736 # An address range needs to have an upper limit, specified 737 # either explicitly with an end, or as an offset using the 738 # size keyword. 739 if 'end' in kwargs: 740 self.end = Addr(kwargs.pop('end')) 741 elif 'size' in kwargs: 742 self.end = self.start + Addr(kwargs.pop('size')) - 1 743 else: 744 raise TypeError, "Either end or size must be specified" 745 746 # Now on to the optional bit 747 if 'intlvHighBit' in kwargs: 748 self.intlvHighBit = int(kwargs.pop('intlvHighBit')) 749 if 'xorHighBit' in kwargs: 750 self.xorHighBit = int(kwargs.pop('xorHighBit')) 751 if 'intlvBits' in kwargs: 752 self.intlvBits = int(kwargs.pop('intlvBits')) 753 if 'intlvMatch' in kwargs: 754 self.intlvMatch = int(kwargs.pop('intlvMatch')) 755 756 if len(args) == 0: 757 self.start = Addr(kwargs.pop('start')) 758 handle_kwargs(self, kwargs) 759 760 elif len(args) == 1: 761 if kwargs: 762 self.start = Addr(args[0]) 763 handle_kwargs(self, kwargs) 764 elif isinstance(args[0], (list, tuple)): 765 self.start = Addr(args[0][0]) 766 self.end = Addr(args[0][1]) 767 else: 768 self.start = Addr(0) 769 self.end = Addr(args[0]) - 1 770 771 elif len(args) == 2: 772 self.start = Addr(args[0]) 773 self.end = Addr(args[1]) 774 else: 775 raise TypeError, "Too many arguments specified" 776 777 if kwargs: 778 raise TypeError, "Too many keywords: %s" % kwargs.keys() 779 780 def __str__(self): 781 return '%s:%s' % (self.start, self.end) 782 783 def size(self): 784 # Divide the size by the size of the interleaving slice 785 return (long(self.end) - long(self.start) + 1) >> self.intlvBits 786 787 @classmethod 788 def cxx_predecls(cls, code): 789 Addr.cxx_predecls(code) 790 code('#include "base/addr_range.hh"') 791 792 @classmethod 793 def swig_predecls(cls, code): 794 Addr.swig_predecls(code) 795 796 @classmethod 797 def cxx_ini_predecls(cls, code): 798 code('#include <sstream>') 799 800 @classmethod 801 def cxx_ini_parse(cls, code, src, dest, ret): 802 code('uint64_t _start, _end;') 803 code('char _sep;') 804 code('std::istringstream _stream(${src});') 805 code('_stream >> _start;') 806 code('_stream.get(_sep);') 807 code('_stream >> _end;') 808 code('bool _ret = !_stream.fail() &&' 809 '_stream.eof() && _sep == \':\';') 810 code('if (_ret)') 811 code(' ${dest} = AddrRange(_start, _end);') 812 code('${ret} _ret;') 813 814 def getValue(self): 815 # Go from the Python class to the wrapped C++ class generated 816 # by swig 817 from m5.internal.range import AddrRange 818 819 return AddrRange(long(self.start), long(self.end), 820 int(self.intlvHighBit), int(self.xorHighBit), 821 int(self.intlvBits), int(self.intlvMatch)) 822 823# Boolean parameter type. Python doesn't let you subclass bool, since 824# it doesn't want to let you create multiple instances of True and 825# False. Thus this is a little more complicated than String. 826class Bool(ParamValue): 827 cxx_type = 'bool' 828 cmd_line_settable = True 829 830 def __init__(self, value): 831 try: 832 self.value = convert.toBool(value) 833 except TypeError: 834 self.value = bool(value) 835 836 def __call__(self, value): 837 self.__init__(value) 838 return value 839 840 def getValue(self): 841 return bool(self.value) 842 843 def __str__(self): 844 return str(self.value) 845 846 # implement truth value testing for Bool parameters so that these params 847 # evaluate correctly during the python configuration phase 848 def __nonzero__(self): 849 return bool(self.value) 850 851 def ini_str(self): 852 if self.value: 853 return 'true' 854 return 'false' 855 856 def config_value(self): 857 return self.value 858 859 @classmethod 860 def cxx_ini_predecls(cls, code): 861 # Assume that base/str.hh will be included anyway 862 # code('#include "base/str.hh"') 863 pass 864 865 @classmethod 866 def cxx_ini_parse(cls, code, src, dest, ret): 867 code('%s to_bool(%s, %s);' % (ret, src, dest)) 868 869def IncEthernetAddr(addr, val = 1): 870 bytes = map(lambda x: int(x, 16), addr.split(':')) 871 bytes[5] += val 872 for i in (5, 4, 3, 2, 1): 873 val,rem = divmod(bytes[i], 256) 874 bytes[i] = rem 875 if val == 0: 876 break 877 bytes[i - 1] += val 878 assert(bytes[0] <= 255) 879 return ':'.join(map(lambda x: '%02x' % x, bytes)) 880 881_NextEthernetAddr = "00:90:00:00:00:01" 882def NextEthernetAddr(): 883 global _NextEthernetAddr 884 885 value = _NextEthernetAddr 886 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 887 return value 888 889class EthernetAddr(ParamValue): 890 cxx_type = 'Net::EthAddr' 891 ex_str = "00:90:00:00:00:01" 892 cmd_line_settable = True 893 894 @classmethod 895 def cxx_predecls(cls, code): 896 code('#include "base/inet.hh"') 897 898 @classmethod 899 def swig_predecls(cls, code): 900 code('%include "python/swig/inet.i"') 901 902 def __init__(self, value): 903 if value == NextEthernetAddr: 904 self.value = value 905 return 906 907 if not isinstance(value, str): 908 raise TypeError, "expected an ethernet address and didn't get one" 909 910 bytes = value.split(':') 911 if len(bytes) != 6: 912 raise TypeError, 'invalid ethernet address %s' % value 913 914 for byte in bytes: 915 if not 0 <= int(byte, base=16) <= 0xff: 916 raise TypeError, 'invalid ethernet address %s' % value 917 918 self.value = value 919 920 def __call__(self, value): 921 self.__init__(value) 922 return value 923 924 def unproxy(self, base): 925 if self.value == NextEthernetAddr: 926 return EthernetAddr(self.value()) 927 return self 928 929 def getValue(self): 930 from m5.internal.params import EthAddr 931 return EthAddr(self.value) 932 933 def ini_str(self): 934 return self.value 935 936 @classmethod 937 def cxx_ini_parse(self, code, src, dest, ret): 938 code('%s = Net::EthAddr(%s);' % (dest, src)) 939 code('%s true;' % ret) 940 941# When initializing an IpAddress, pass in an existing IpAddress, a string of 942# the form "a.b.c.d", or an integer representing an IP. 943class IpAddress(ParamValue): 944 cxx_type = 'Net::IpAddress' 945 ex_str = "127.0.0.1" 946 cmd_line_settable = True 947 948 @classmethod 949 def cxx_predecls(cls, code): 950 code('#include "base/inet.hh"') 951 952 @classmethod 953 def swig_predecls(cls, code): 954 code('%include "python/swig/inet.i"') 955 956 def __init__(self, value): 957 if isinstance(value, IpAddress): 958 self.ip = value.ip 959 else: 960 try: 961 self.ip = convert.toIpAddress(value) 962 except TypeError: 963 self.ip = long(value) 964 self.verifyIp() 965 966 def __call__(self, value): 967 self.__init__(value) 968 return value 969 970 def __str__(self): 971 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)] 972 return '%d.%d.%d.%d' % tuple(tup) 973 974 def __eq__(self, other): 975 if isinstance(other, IpAddress): 976 return self.ip == other.ip 977 elif isinstance(other, str): 978 try: 979 return self.ip == convert.toIpAddress(other) 980 except: 981 return False 982 else: 983 return self.ip == other 984 985 def __ne__(self, other): 986 return not (self == other) 987 988 def verifyIp(self): 989 if self.ip < 0 or self.ip >= (1 << 32): 990 raise TypeError, "invalid ip address %#08x" % self.ip 991 992 def getValue(self): 993 from m5.internal.params import IpAddress 994 return IpAddress(self.ip) 995 996# When initializing an IpNetmask, pass in an existing IpNetmask, a string of 997# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as 998# positional or keyword arguments. 999class IpNetmask(IpAddress): 1000 cxx_type = 'Net::IpNetmask' 1001 ex_str = "127.0.0.0/24" 1002 cmd_line_settable = True 1003 1004 @classmethod 1005 def cxx_predecls(cls, code): 1006 code('#include "base/inet.hh"') 1007 1008 @classmethod 1009 def swig_predecls(cls, code): 1010 code('%include "python/swig/inet.i"') 1011 1012 def __init__(self, *args, **kwargs): 1013 def handle_kwarg(self, kwargs, key, elseVal = None): 1014 if key in kwargs: 1015 setattr(self, key, kwargs.pop(key)) 1016 elif elseVal: 1017 setattr(self, key, elseVal) 1018 else: 1019 raise TypeError, "No value set for %s" % key 1020 1021 if len(args) == 0: 1022 handle_kwarg(self, kwargs, 'ip') 1023 handle_kwarg(self, kwargs, 'netmask') 1024 1025 elif len(args) == 1: 1026 if kwargs: 1027 if not 'ip' in kwargs and not 'netmask' in kwargs: 1028 raise TypeError, "Invalid arguments" 1029 handle_kwarg(self, kwargs, 'ip', args[0]) 1030 handle_kwarg(self, kwargs, 'netmask', args[0]) 1031 elif isinstance(args[0], IpNetmask): 1032 self.ip = args[0].ip 1033 self.netmask = args[0].netmask 1034 else: 1035 (self.ip, self.netmask) = convert.toIpNetmask(args[0]) 1036 1037 elif len(args) == 2: 1038 self.ip = args[0] 1039 self.netmask = args[1] 1040 else: 1041 raise TypeError, "Too many arguments specified" 1042 1043 if kwargs: 1044 raise TypeError, "Too many keywords: %s" % kwargs.keys() 1045 1046 self.verify() 1047 1048 def __call__(self, value): 1049 self.__init__(value) 1050 return value 1051 1052 def __str__(self): 1053 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask) 1054 1055 def __eq__(self, other): 1056 if isinstance(other, IpNetmask): 1057 return self.ip == other.ip and self.netmask == other.netmask 1058 elif isinstance(other, str): 1059 try: 1060 return (self.ip, self.netmask) == convert.toIpNetmask(other) 1061 except: 1062 return False 1063 else: 1064 return False 1065 1066 def verify(self): 1067 self.verifyIp() 1068 if self.netmask < 0 or self.netmask > 32: 1069 raise TypeError, "invalid netmask %d" % netmask 1070 1071 def getValue(self): 1072 from m5.internal.params import IpNetmask 1073 return IpNetmask(self.ip, self.netmask) 1074 1075# When initializing an IpWithPort, pass in an existing IpWithPort, a string of 1076# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments. 1077class IpWithPort(IpAddress): 1078 cxx_type = 'Net::IpWithPort' 1079 ex_str = "127.0.0.1:80" 1080 cmd_line_settable = True 1081 1082 @classmethod 1083 def cxx_predecls(cls, code): 1084 code('#include "base/inet.hh"') 1085 1086 @classmethod 1087 def swig_predecls(cls, code): 1088 code('%include "python/swig/inet.i"') 1089 1090 def __init__(self, *args, **kwargs): 1091 def handle_kwarg(self, kwargs, key, elseVal = None): 1092 if key in kwargs: 1093 setattr(self, key, kwargs.pop(key)) 1094 elif elseVal: 1095 setattr(self, key, elseVal) 1096 else: 1097 raise TypeError, "No value set for %s" % key 1098 1099 if len(args) == 0: 1100 handle_kwarg(self, kwargs, 'ip') 1101 handle_kwarg(self, kwargs, 'port') 1102 1103 elif len(args) == 1: 1104 if kwargs: 1105 if not 'ip' in kwargs and not 'port' in kwargs: 1106 raise TypeError, "Invalid arguments" 1107 handle_kwarg(self, kwargs, 'ip', args[0]) 1108 handle_kwarg(self, kwargs, 'port', args[0]) 1109 elif isinstance(args[0], IpWithPort): 1110 self.ip = args[0].ip 1111 self.port = args[0].port 1112 else: 1113 (self.ip, self.port) = convert.toIpWithPort(args[0]) 1114 1115 elif len(args) == 2: 1116 self.ip = args[0] 1117 self.port = args[1] 1118 else: 1119 raise TypeError, "Too many arguments specified" 1120 1121 if kwargs: 1122 raise TypeError, "Too many keywords: %s" % kwargs.keys() 1123 1124 self.verify() 1125 1126 def __call__(self, value): 1127 self.__init__(value) 1128 return value 1129 1130 def __str__(self): 1131 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port) 1132 1133 def __eq__(self, other): 1134 if isinstance(other, IpWithPort): 1135 return self.ip == other.ip and self.port == other.port 1136 elif isinstance(other, str): 1137 try: 1138 return (self.ip, self.port) == convert.toIpWithPort(other) 1139 except: 1140 return False 1141 else: 1142 return False 1143 1144 def verify(self): 1145 self.verifyIp() 1146 if self.port < 0 or self.port > 0xffff: 1147 raise TypeError, "invalid port %d" % self.port 1148 1149 def getValue(self): 1150 from m5.internal.params import IpWithPort 1151 return IpWithPort(self.ip, self.port) 1152 1153time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 1154 "%a %b %d %H:%M:%S %Y", 1155 "%Y/%m/%d %H:%M:%S", 1156 "%Y/%m/%d %H:%M", 1157 "%Y/%m/%d", 1158 "%m/%d/%Y %H:%M:%S", 1159 "%m/%d/%Y %H:%M", 1160 "%m/%d/%Y", 1161 "%m/%d/%y %H:%M:%S", 1162 "%m/%d/%y %H:%M", 1163 "%m/%d/%y"] 1164 1165 1166def parse_time(value): 1167 from time import gmtime, strptime, struct_time, time 1168 from datetime import datetime, date 1169 1170 if isinstance(value, struct_time): 1171 return value 1172 1173 if isinstance(value, (int, long)): 1174 return gmtime(value) 1175 1176 if isinstance(value, (datetime, date)): 1177 return value.timetuple() 1178 1179 if isinstance(value, str): 1180 if value in ('Now', 'Today'): 1181 return time.gmtime(time.time()) 1182 1183 for format in time_formats: 1184 try: 1185 return strptime(value, format) 1186 except ValueError: 1187 pass 1188 1189 raise ValueError, "Could not parse '%s' as a time" % value 1190 1191class Time(ParamValue): 1192 cxx_type = 'tm' 1193 1194 @classmethod 1195 def cxx_predecls(cls, code): 1196 code('#include <time.h>') 1197 1198 @classmethod 1199 def swig_predecls(cls, code): 1200 code('%include "python/swig/time.i"') 1201 1202 def __init__(self, value): 1203 self.value = parse_time(value) 1204 1205 def __call__(self, value): 1206 self.__init__(value) 1207 return value 1208 1209 def getValue(self): 1210 from m5.internal.params import tm 1211 1212 c_time = tm() 1213 py_time = self.value 1214 1215 # UNIX is years since 1900 1216 c_time.tm_year = py_time.tm_year - 1900; 1217 1218 # Python starts at 1, UNIX starts at 0 1219 c_time.tm_mon = py_time.tm_mon - 1; 1220 c_time.tm_mday = py_time.tm_mday; 1221 c_time.tm_hour = py_time.tm_hour; 1222 c_time.tm_min = py_time.tm_min; 1223 c_time.tm_sec = py_time.tm_sec; 1224 1225 # Python has 0 as Monday, UNIX is 0 as sunday 1226 c_time.tm_wday = py_time.tm_wday + 1 1227 if c_time.tm_wday > 6: 1228 c_time.tm_wday -= 7; 1229 1230 # Python starts at 1, Unix starts at 0 1231 c_time.tm_yday = py_time.tm_yday - 1; 1232 1233 return c_time 1234 1235 def __str__(self): 1236 return time.asctime(self.value) 1237 1238 def ini_str(self): 1239 return str(self) 1240 1241 def get_config_as_dict(self): 1242 assert false 1243 return str(self) 1244 1245 @classmethod 1246 def cxx_ini_predecls(cls, code): 1247 code('#include <time.h>') 1248 1249 @classmethod 1250 def cxx_ini_parse(cls, code, src, dest, ret): 1251 code('char *_parse_ret = strptime((${src}).c_str(),') 1252 code(' "%a %b %d %H:%M:%S %Y", &(${dest}));') 1253 code('${ret} _parse_ret && *_parse_ret == \'\\0\';'); 1254 1255# Enumerated types are a little more complex. The user specifies the 1256# type as Enum(foo) where foo is either a list or dictionary of 1257# alternatives (typically strings, but not necessarily so). (In the 1258# long run, the integer value of the parameter will be the list index 1259# or the corresponding dictionary value. For now, since we only check 1260# that the alternative is valid and then spit it into a .ini file, 1261# there's not much point in using the dictionary.) 1262 1263# What Enum() must do is generate a new type encapsulating the 1264# provided list/dictionary so that specific values of the parameter 1265# can be instances of that type. We define two hidden internal 1266# classes (_ListEnum and _DictEnum) to serve as base classes, then 1267# derive the new type from the appropriate base class on the fly. 1268 1269allEnums = {} 1270# Metaclass for Enum types 1271class MetaEnum(MetaParamValue): 1272 def __new__(mcls, name, bases, dict): 1273 assert name not in allEnums 1274 1275 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 1276 allEnums[name] = cls 1277 return cls 1278 1279 def __init__(cls, name, bases, init_dict): 1280 if init_dict.has_key('map'): 1281 if not isinstance(cls.map, dict): 1282 raise TypeError, "Enum-derived class attribute 'map' " \ 1283 "must be of type dict" 1284 # build list of value strings from map 1285 cls.vals = cls.map.keys() 1286 cls.vals.sort() 1287 elif init_dict.has_key('vals'): 1288 if not isinstance(cls.vals, list): 1289 raise TypeError, "Enum-derived class attribute 'vals' " \ 1290 "must be of type list" 1291 # build string->value map from vals sequence 1292 cls.map = {} 1293 for idx,val in enumerate(cls.vals): 1294 cls.map[val] = idx 1295 else: 1296 raise TypeError, "Enum-derived class must define "\ 1297 "attribute 'map' or 'vals'" 1298 1299 cls.cxx_type = 'Enums::%s' % name 1300 1301 super(MetaEnum, cls).__init__(name, bases, init_dict) 1302 1303 # Generate C++ class declaration for this enum type. 1304 # Note that we wrap the enum in a class/struct to act as a namespace, 1305 # so that the enum strings can be brief w/o worrying about collisions. 1306 def cxx_decl(cls, code): 1307 wrapper_name = cls.wrapper_name 1308 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace' 1309 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1310 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name) 1311 1312 code('''\ 1313#ifndef $idem_macro 1314#define $idem_macro 1315 1316$wrapper $wrapper_name { 1317 enum $name { 1318''') 1319 code.indent(2) 1320 for val in cls.vals: 1321 code('$val = ${{cls.map[val]}},') 1322 code('Num_$name = ${{len(cls.vals)}}') 1323 code.dedent(2) 1324 code(' };') 1325 1326 if cls.wrapper_is_struct: 1327 code(' static const char *${name}Strings[Num_${name}];') 1328 code('};') 1329 else: 1330 code('extern const char *${name}Strings[Num_${name}];') 1331 code('}') 1332 1333 code() 1334 code('#endif // $idem_macro') 1335 1336 def cxx_def(cls, code): 1337 wrapper_name = cls.wrapper_name 1338 file_name = cls.__name__ 1339 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1340 1341 code('#include "enums/$file_name.hh"') 1342 if cls.wrapper_is_struct: 1343 code('const char *${wrapper_name}::${name}Strings' 1344 '[Num_${name}] =') 1345 else: 1346 code('namespace Enums {') 1347 code.indent(1) 1348 code(' const char *${name}Strings[Num_${name}] =') 1349 1350 code('{') 1351 code.indent(1) 1352 for val in cls.vals: 1353 code('"$val",') 1354 code.dedent(1) 1355 code('};') 1356 1357 if not cls.wrapper_is_struct: 1358 code('} // namespace $wrapper_name') 1359 code.dedent(1) 1360 1361 def swig_decl(cls, code): 1362 name = cls.__name__ 1363 code('''\ 1364%module(package="m5.internal") enum_$name 1365 1366%{ 1367#include "enums/$name.hh" 1368%} 1369 1370%include "enums/$name.hh" 1371''') 1372 1373 1374# Base class for enum types. 1375class Enum(ParamValue): 1376 __metaclass__ = MetaEnum 1377 vals = [] 1378 cmd_line_settable = True 1379 1380 # The name of the wrapping namespace or struct 1381 wrapper_name = 'Enums' 1382 1383 # If true, the enum is wrapped in a struct rather than a namespace 1384 wrapper_is_struct = False 1385 1386 # If not None, use this as the enum name rather than this class name 1387 enum_name = None 1388 1389 def __init__(self, value): 1390 if value not in self.map: 1391 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 1392 % (value, self.vals) 1393 self.value = value 1394 1395 def __call__(self, value): 1396 self.__init__(value) 1397 return value 1398 1399 @classmethod 1400 def cxx_predecls(cls, code): 1401 code('#include "enums/$0.hh"', cls.__name__) 1402 1403 @classmethod 1404 def swig_predecls(cls, code): 1405 code('%import "python/m5/internal/enum_$0.i"', cls.__name__) 1406 1407 @classmethod 1408 def cxx_ini_parse(cls, code, src, dest, ret): 1409 code('if (false) {') 1410 for elem_name in cls.map.iterkeys(): 1411 code('} else if (%s == "%s") {' % (src, elem_name)) 1412 code.indent() 1413 code('%s = Enums::%s;' % (dest, elem_name)) 1414 code('%s true;' % ret) 1415 code.dedent() 1416 code('} else {') 1417 code(' %s false;' % ret) 1418 code('}') 1419 1420 def getValue(self): 1421 return int(self.map[self.value]) 1422 1423 def __str__(self): 1424 return self.value 1425 1426# how big does a rounding error need to be before we warn about it? 1427frequency_tolerance = 0.001 # 0.1% 1428 1429class TickParamValue(NumericParamValue): 1430 cxx_type = 'Tick' 1431 ex_str = "1MHz" 1432 cmd_line_settable = True 1433 1434 @classmethod 1435 def cxx_predecls(cls, code): 1436 code('#include "base/types.hh"') 1437 1438 @classmethod 1439 def swig_predecls(cls, code): 1440 code('%import "stdint.i"') 1441 code('%import "base/types.hh"') 1442 1443 def __call__(self, value): 1444 self.__init__(value) 1445 return value 1446 1447 def getValue(self): 1448 return long(self.value) 1449 1450 @classmethod 1451 def cxx_ini_predecls(cls, code): 1452 code('#include <sstream>') 1453 1454 # Ticks are expressed in seconds in JSON files and in plain 1455 # Ticks in .ini files. Switch based on a config flag 1456 @classmethod 1457 def cxx_ini_parse(self, code, src, dest, ret): 1458 code('${ret} to_number(${src}, ${dest});') 1459 1460class Latency(TickParamValue): 1461 ex_str = "100ns" 1462 1463 def __init__(self, value): 1464 if isinstance(value, (Latency, Clock)): 1465 self.ticks = value.ticks 1466 self.value = value.value 1467 elif isinstance(value, Frequency): 1468 self.ticks = value.ticks 1469 self.value = 1.0 / value.value 1470 elif value.endswith('t'): 1471 self.ticks = True 1472 self.value = int(value[:-1]) 1473 else: 1474 self.ticks = False 1475 self.value = convert.toLatency(value) 1476 1477 def __call__(self, value): 1478 self.__init__(value) 1479 return value 1480 1481 def __getattr__(self, attr): 1482 if attr in ('latency', 'period'): 1483 return self 1484 if attr == 'frequency': 1485 return Frequency(self) 1486 raise AttributeError, "Latency object has no attribute '%s'" % attr 1487 1488 def getValue(self): 1489 if self.ticks or self.value == 0: 1490 value = self.value 1491 else: 1492 value = ticks.fromSeconds(self.value) 1493 return long(value) 1494 1495 def config_value(self): 1496 return self.getValue() 1497 1498 # convert latency to ticks 1499 def ini_str(self): 1500 return '%d' % self.getValue() 1501 1502class Frequency(TickParamValue): 1503 ex_str = "1GHz" 1504 1505 def __init__(self, value): 1506 if isinstance(value, (Latency, Clock)): 1507 if value.value == 0: 1508 self.value = 0 1509 else: 1510 self.value = 1.0 / value.value 1511 self.ticks = value.ticks 1512 elif isinstance(value, Frequency): 1513 self.value = value.value 1514 self.ticks = value.ticks 1515 else: 1516 self.ticks = False 1517 self.value = convert.toFrequency(value) 1518 1519 def __call__(self, value): 1520 self.__init__(value) 1521 return value 1522 1523 def __getattr__(self, attr): 1524 if attr == 'frequency': 1525 return self 1526 if attr in ('latency', 'period'): 1527 return Latency(self) 1528 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1529 1530 # convert latency to ticks 1531 def getValue(self): 1532 if self.ticks or self.value == 0: 1533 value = self.value 1534 else: 1535 value = ticks.fromSeconds(1.0 / self.value) 1536 return long(value) 1537 1538 def config_value(self): 1539 return self.getValue() 1540 1541 def ini_str(self): 1542 return '%d' % self.getValue() 1543 1544# A generic Frequency and/or Latency value. Value is stored as a 1545# latency, just like Latency and Frequency. 1546class Clock(TickParamValue): 1547 def __init__(self, value): 1548 if isinstance(value, (Latency, Clock)): 1549 self.ticks = value.ticks 1550 self.value = value.value 1551 elif isinstance(value, Frequency): 1552 self.ticks = value.ticks 1553 self.value = 1.0 / value.value 1554 elif value.endswith('t'): 1555 self.ticks = True 1556 self.value = int(value[:-1]) 1557 else: 1558 self.ticks = False 1559 self.value = convert.anyToLatency(value) 1560 1561 def __call__(self, value): 1562 self.__init__(value) 1563 return value 1564 1565 def __str__(self): 1566 return "%s" % Latency(self) 1567 1568 def __getattr__(self, attr): 1569 if attr == 'frequency': 1570 return Frequency(self) 1571 if attr in ('latency', 'period'): 1572 return Latency(self) 1573 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1574 1575 def getValue(self): 1576 return self.period.getValue() 1577 1578 def config_value(self): 1579 return self.period.config_value() 1580 1581 def ini_str(self): 1582 return self.period.ini_str() 1583 1584class Voltage(float,ParamValue): 1585 cxx_type = 'double' 1586 ex_str = "1V" 1587 cmd_line_settable = False 1588 1589 def __new__(cls, value): 1590 # convert to voltage 1591 val = convert.toVoltage(value) 1592 return super(cls, Voltage).__new__(cls, val) 1593 1594 def __call__(self, value): 1595 val = convert.toVoltage(value) 1596 self.__init__(val) 1597 return value 1598 1599 def __str__(self): 1600 return str(self.getValue()) 1601 1602 def getValue(self): 1603 value = float(self) 1604 return value 1605 1606 def ini_str(self): 1607 return '%f' % self.getValue() 1608 1609 @classmethod 1610 def cxx_ini_predecls(cls, code): 1611 code('#include <sstream>') 1612 1613 @classmethod 1614 def cxx_ini_parse(self, code, src, dest, ret): 1615 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1616 1617class Current(float, ParamValue): 1618 cxx_type = 'double' 1619 ex_str = "1mA" 1620 cmd_line_settable = False 1621 1622 def __new__(cls, value): 1623 # convert to current 1624 val = convert.toCurrent(value) 1625 return super(cls, Current).__new__(cls, val) 1626 1627 def __call__(self, value): 1628 val = convert.toCurrent(value) 1629 self.__init__(val) 1630 return value 1631 1632 def __str__(self): 1633 return str(self.getValue()) 1634 1635 def getValue(self): 1636 value = float(self) 1637 return value 1638 1639 def ini_str(self): 1640 return '%f' % self.getValue() 1641 1642 @classmethod 1643 def cxx_ini_predecls(cls, code): 1644 code('#include <sstream>') 1645 1646 @classmethod 1647 def cxx_ini_parse(self, code, src, dest, ret): 1648 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1649 1650class NetworkBandwidth(float,ParamValue): 1651 cxx_type = 'float' 1652 ex_str = "1Gbps" 1653 cmd_line_settable = True 1654 1655 def __new__(cls, value): 1656 # convert to bits per second 1657 val = convert.toNetworkBandwidth(value) 1658 return super(cls, NetworkBandwidth).__new__(cls, val) 1659 1660 def __str__(self): 1661 return str(self.val) 1662 1663 def __call__(self, value): 1664 val = convert.toNetworkBandwidth(value) 1665 self.__init__(val) 1666 return value 1667 1668 def getValue(self): 1669 # convert to seconds per byte 1670 value = 8.0 / float(self) 1671 # convert to ticks per byte 1672 value = ticks.fromSeconds(value) 1673 return float(value) 1674 1675 def ini_str(self): 1676 return '%f' % self.getValue() 1677 1678 def config_value(self): 1679 return '%f' % self.getValue() 1680 1681 @classmethod 1682 def cxx_ini_predecls(cls, code): 1683 code('#include <sstream>') 1684 1685 @classmethod 1686 def cxx_ini_parse(self, code, src, dest, ret): 1687 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1688 1689class MemoryBandwidth(float,ParamValue): 1690 cxx_type = 'float' 1691 ex_str = "1GB/s" 1692 cmd_line_settable = True 1693 1694 def __new__(cls, value): 1695 # convert to bytes per second 1696 val = convert.toMemoryBandwidth(value) 1697 return super(cls, MemoryBandwidth).__new__(cls, val) 1698 1699 def __call__(self, value): 1700 val = convert.toMemoryBandwidth(value) 1701 self.__init__(val) 1702 return value 1703 1704 def getValue(self): 1705 # convert to seconds per byte 1706 value = float(self) 1707 if value: 1708 value = 1.0 / float(self) 1709 # convert to ticks per byte 1710 value = ticks.fromSeconds(value) 1711 return float(value) 1712 1713 def ini_str(self): 1714 return '%f' % self.getValue() 1715 1716 def config_value(self): 1717 return '%f' % self.getValue() 1718 1719 @classmethod 1720 def cxx_ini_predecls(cls, code): 1721 code('#include <sstream>') 1722 1723 @classmethod 1724 def cxx_ini_parse(self, code, src, dest, ret): 1725 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1726 1727# 1728# "Constants"... handy aliases for various values. 1729# 1730 1731# Special class for NULL pointers. Note the special check in 1732# make_param_value() above that lets these be assigned where a 1733# SimObject is required. 1734# only one copy of a particular node 1735class NullSimObject(object): 1736 __metaclass__ = Singleton 1737 1738 def __call__(cls): 1739 return cls 1740 1741 def _instantiate(self, parent = None, path = ''): 1742 pass 1743 1744 def ini_str(self): 1745 return 'Null' 1746 1747 def unproxy(self, base): 1748 return self 1749 1750 def set_path(self, parent, name): 1751 pass 1752 1753 def __str__(self): 1754 return 'Null' 1755 1756 def config_value(self): 1757 return None 1758 1759 def getValue(self): 1760 return None 1761 1762# The only instance you'll ever need... 1763NULL = NullSimObject() 1764 1765def isNullPointer(value): 1766 return isinstance(value, NullSimObject) 1767 1768# Some memory range specifications use this as a default upper bound. 1769MaxAddr = Addr.max 1770MaxTick = Tick.max 1771AllMemory = AddrRange(0, MaxAddr) 1772 1773 1774##################################################################### 1775# 1776# Port objects 1777# 1778# Ports are used to interconnect objects in the memory system. 1779# 1780##################################################################### 1781 1782# Port reference: encapsulates a reference to a particular port on a 1783# particular SimObject. 1784class PortRef(object): 1785 def __init__(self, simobj, name, role): 1786 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1787 self.simobj = simobj 1788 self.name = name 1789 self.role = role 1790 self.peer = None # not associated with another port yet 1791 self.ccConnected = False # C++ port connection done? 1792 self.index = -1 # always -1 for non-vector ports 1793 1794 def __str__(self): 1795 return '%s.%s' % (self.simobj, self.name) 1796 1797 def __len__(self): 1798 # Return the number of connected ports, i.e. 0 is we have no 1799 # peer and 1 if we do. 1800 return int(self.peer != None) 1801 1802 # for config.ini, print peer's name (not ours) 1803 def ini_str(self): 1804 return str(self.peer) 1805 1806 # for config.json 1807 def get_config_as_dict(self): 1808 return {'role' : self.role, 'peer' : str(self.peer)} 1809 1810 def __getattr__(self, attr): 1811 if attr == 'peerObj': 1812 # shorthand for proxies 1813 return self.peer.simobj 1814 raise AttributeError, "'%s' object has no attribute '%s'" % \ 1815 (self.__class__.__name__, attr) 1816 1817 # Full connection is symmetric (both ways). Called via 1818 # SimObject.__setattr__ as a result of a port assignment, e.g., 1819 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 1820 # e.g., "obj1.portA[3] = obj2.portB". 1821 def connect(self, other): 1822 if isinstance(other, VectorPortRef): 1823 # reference to plain VectorPort is implicit append 1824 other = other._get_next() 1825 if self.peer and not proxy.isproxy(self.peer): 1826 fatal("Port %s is already connected to %s, cannot connect %s\n", 1827 self, self.peer, other); 1828 self.peer = other 1829 if proxy.isproxy(other): 1830 other.set_param_desc(PortParamDesc()) 1831 elif isinstance(other, PortRef): 1832 if other.peer is not self: 1833 other.connect(self) 1834 else: 1835 raise TypeError, \ 1836 "assigning non-port reference '%s' to port '%s'" \ 1837 % (other, self) 1838 1839 # Allow a master/slave port pair to be spliced between 1840 # a port and its connected peer. Useful operation for connecting 1841 # instrumentation structures into a system when it is necessary 1842 # to connect the instrumentation after the full system has been 1843 # constructed. 1844 def splice(self, new_master_peer, new_slave_peer): 1845 if self.peer and not proxy.isproxy(self.peer): 1846 if isinstance(new_master_peer, PortRef) and \ 1847 isinstance(new_slave_peer, PortRef): 1848 old_peer = self.peer 1849 if self.role == 'SLAVE': 1850 self.peer = new_master_peer 1851 old_peer.peer = new_slave_peer 1852 new_master_peer.connect(self) 1853 new_slave_peer.connect(old_peer) 1854 elif self.role == 'MASTER': 1855 self.peer = new_slave_peer 1856 old_peer.peer = new_master_peer 1857 new_slave_peer.connect(self) 1858 new_master_peer.connect(old_peer) 1859 else: 1860 panic("Port %s has unknown role, "+\ 1861 "cannot splice in new peers\n", self) 1862 else: 1863 raise TypeError, \ 1864 "Splicing non-port references '%s','%s' to port '%s'"\ 1865 % (new_peer, peers_new_peer, self) 1866 else: 1867 fatal("Port %s not connected, cannot splice in new peers\n", self) 1868 1869 def clone(self, simobj, memo): 1870 if memo.has_key(self): 1871 return memo[self] 1872 newRef = copy.copy(self) 1873 memo[self] = newRef 1874 newRef.simobj = simobj 1875 assert(isSimObject(newRef.simobj)) 1876 if self.peer and not proxy.isproxy(self.peer): 1877 peerObj = self.peer.simobj(_memo=memo) 1878 newRef.peer = self.peer.clone(peerObj, memo) 1879 assert(not isinstance(newRef.peer, VectorPortRef)) 1880 return newRef 1881 1882 def unproxy(self, simobj): 1883 assert(simobj is self.simobj) 1884 if proxy.isproxy(self.peer): 1885 try: 1886 realPeer = self.peer.unproxy(self.simobj) 1887 except: 1888 print "Error in unproxying port '%s' of %s" % \ 1889 (self.name, self.simobj.path()) 1890 raise 1891 self.connect(realPeer) 1892 1893 # Call C++ to create corresponding port connection between C++ objects 1894 def ccConnect(self): 1895 from m5.internal.pyobject import connectPorts 1896 1897 if self.role == 'SLAVE': 1898 # do nothing and let the master take care of it 1899 return 1900 1901 if self.ccConnected: # already done this 1902 return 1903 peer = self.peer 1904 if not self.peer: # nothing to connect to 1905 return 1906 1907 # check that we connect a master to a slave 1908 if self.role == peer.role: 1909 raise TypeError, \ 1910 "cannot connect '%s' and '%s' due to identical role '%s'" \ 1911 % (peer, self, self.role) 1912 1913 try: 1914 # self is always the master and peer the slave 1915 connectPorts(self.simobj.getCCObject(), self.name, self.index, 1916 peer.simobj.getCCObject(), peer.name, peer.index) 1917 except: 1918 print "Error connecting port %s.%s to %s.%s" % \ 1919 (self.simobj.path(), self.name, 1920 peer.simobj.path(), peer.name) 1921 raise 1922 self.ccConnected = True 1923 peer.ccConnected = True 1924 1925# A reference to an individual element of a VectorPort... much like a 1926# PortRef, but has an index. 1927class VectorPortElementRef(PortRef): 1928 def __init__(self, simobj, name, role, index): 1929 PortRef.__init__(self, simobj, name, role) 1930 self.index = index 1931 1932 def __str__(self): 1933 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1934 1935# A reference to a complete vector-valued port (not just a single element). 1936# Can be indexed to retrieve individual VectorPortElementRef instances. 1937class VectorPortRef(object): 1938 def __init__(self, simobj, name, role): 1939 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1940 self.simobj = simobj 1941 self.name = name 1942 self.role = role 1943 self.elements = [] 1944 1945 def __str__(self): 1946 return '%s.%s[:]' % (self.simobj, self.name) 1947 1948 def __len__(self): 1949 # Return the number of connected peers, corresponding the the 1950 # length of the elements. 1951 return len(self.elements) 1952 1953 # for config.ini, print peer's name (not ours) 1954 def ini_str(self): 1955 return ' '.join([el.ini_str() for el in self.elements]) 1956 1957 # for config.json 1958 def get_config_as_dict(self): 1959 return {'role' : self.role, 1960 'peer' : [el.ini_str() for el in self.elements]} 1961 1962 def __getitem__(self, key): 1963 if not isinstance(key, int): 1964 raise TypeError, "VectorPort index must be integer" 1965 if key >= len(self.elements): 1966 # need to extend list 1967 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i) 1968 for i in range(len(self.elements), key+1)] 1969 self.elements.extend(ext) 1970 return self.elements[key] 1971 1972 def _get_next(self): 1973 return self[len(self.elements)] 1974 1975 def __setitem__(self, key, value): 1976 if not isinstance(key, int): 1977 raise TypeError, "VectorPort index must be integer" 1978 self[key].connect(value) 1979 1980 def connect(self, other): 1981 if isinstance(other, (list, tuple)): 1982 # Assign list of port refs to vector port. 1983 # For now, append them... not sure if that's the right semantics 1984 # or if it should replace the current vector. 1985 for ref in other: 1986 self._get_next().connect(ref) 1987 else: 1988 # scalar assignment to plain VectorPort is implicit append 1989 self._get_next().connect(other) 1990 1991 def clone(self, simobj, memo): 1992 if memo.has_key(self): 1993 return memo[self] 1994 newRef = copy.copy(self) 1995 memo[self] = newRef 1996 newRef.simobj = simobj 1997 assert(isSimObject(newRef.simobj)) 1998 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 1999 return newRef 2000 2001 def unproxy(self, simobj): 2002 [el.unproxy(simobj) for el in self.elements] 2003 2004 def ccConnect(self): 2005 [el.ccConnect() for el in self.elements] 2006 2007# Port description object. Like a ParamDesc object, this represents a 2008# logical port in the SimObject class, not a particular port on a 2009# SimObject instance. The latter are represented by PortRef objects. 2010class Port(object): 2011 # Generate a PortRef for this port on the given SimObject with the 2012 # given name 2013 def makeRef(self, simobj): 2014 return PortRef(simobj, self.name, self.role) 2015 2016 # Connect an instance of this port (on the given SimObject with 2017 # the given name) with the port described by the supplied PortRef 2018 def connect(self, simobj, ref): 2019 self.makeRef(simobj).connect(ref) 2020 2021 # No need for any pre-declarations at the moment as we merely rely 2022 # on an unsigned int. 2023 def cxx_predecls(self, code): 2024 pass 2025 2026 # Declare an unsigned int with the same name as the port, that 2027 # will eventually hold the number of connected ports (and thus the 2028 # number of elements for a VectorPort). 2029 def cxx_decl(self, code): 2030 code('unsigned int port_${{self.name}}_connection_count;') 2031 2032class MasterPort(Port): 2033 # MasterPort("description") 2034 def __init__(self, *args): 2035 if len(args) == 1: 2036 self.desc = args[0] 2037 self.role = 'MASTER' 2038 else: 2039 raise TypeError, 'wrong number of arguments' 2040 2041class SlavePort(Port): 2042 # SlavePort("description") 2043 def __init__(self, *args): 2044 if len(args) == 1: 2045 self.desc = args[0] 2046 self.role = 'SLAVE' 2047 else: 2048 raise TypeError, 'wrong number of arguments' 2049 2050# VectorPort description object. Like Port, but represents a vector 2051# of connections (e.g., as on a XBar). 2052class VectorPort(Port): 2053 def __init__(self, *args): 2054 self.isVec = True 2055 2056 def makeRef(self, simobj): 2057 return VectorPortRef(simobj, self.name, self.role) 2058 2059class VectorMasterPort(VectorPort): 2060 # VectorMasterPort("description") 2061 def __init__(self, *args): 2062 if len(args) == 1: 2063 self.desc = args[0] 2064 self.role = 'MASTER' 2065 VectorPort.__init__(self, *args) 2066 else: 2067 raise TypeError, 'wrong number of arguments' 2068 2069class VectorSlavePort(VectorPort): 2070 # VectorSlavePort("description") 2071 def __init__(self, *args): 2072 if len(args) == 1: 2073 self.desc = args[0] 2074 self.role = 'SLAVE' 2075 VectorPort.__init__(self, *args) 2076 else: 2077 raise TypeError, 'wrong number of arguments' 2078 2079# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 2080# proxy objects (via set_param_desc()) so that proxy error messages 2081# make sense. 2082class PortParamDesc(object): 2083 __metaclass__ = Singleton 2084 2085 ptype_str = 'Port' 2086 ptype = Port 2087 2088baseEnums = allEnums.copy() 2089baseParams = allParams.copy() 2090 2091def clear(): 2092 global allEnums, allParams 2093 2094 allEnums = baseEnums.copy() 2095 allParams = baseParams.copy() 2096 2097__all__ = ['Param', 'VectorParam', 2098 'Enum', 'Bool', 'String', 'Float', 2099 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 2100 'Int32', 'UInt32', 'Int64', 'UInt64', 2101 'Counter', 'Addr', 'Tick', 'Percent', 2102 'TcpPort', 'UdpPort', 'EthernetAddr', 2103 'IpAddress', 'IpNetmask', 'IpWithPort', 2104 'MemorySize', 'MemorySize32', 2105 'Latency', 'Frequency', 'Clock', 'Voltage', 2106 'NetworkBandwidth', 'MemoryBandwidth', 2107 'AddrRange', 2108 'MaxAddr', 'MaxTick', 'AllMemory', 2109 'Time', 2110 'NextEthernetAddr', 'NULL', 2111 'MasterPort', 'SlavePort', 2112 'VectorMasterPort', 'VectorSlavePort'] 2113 2114import SimObject 2115