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