201 else: 202 return VectorParamValue(tmp_list) 203 204 def swig_predecls(self): 205 return ['%%include "%s_vptype.i"' % self.ptype_str] 206 207 def swig_decl(self): 208 cxx_type = re.sub('std::', '', self.ptype.cxx_type) 209 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \ 210 (self.ptype_str, cxx_type) 211 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl] 212 213 def cxx_predecls(self): 214 return ['#include <vector>'] + self.ptype.cxx_predecls 215 216 def cxx_decl(self): 217 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) 218 219class ParamFactory(object): 220 def __init__(self, param_desc_class, ptype_str = None): 221 self.param_desc_class = param_desc_class 222 self.ptype_str = ptype_str 223 224 def __getattr__(self, attr): 225 if self.ptype_str: 226 attr = self.ptype_str + '.' + attr 227 return ParamFactory(self.param_desc_class, attr) 228 229 # E.g., Param.Int(5, "number of widgets") 230 def __call__(self, *args, **kwargs): 231 ptype = None 232 try: 233 ptype = allParams[self.ptype_str] 234 except KeyError: 235 # if name isn't defined yet, assume it's a SimObject, and 236 # try to resolve it later 237 pass 238 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 239 240Param = ParamFactory(ParamDesc) 241VectorParam = ParamFactory(VectorParamDesc) 242 243##################################################################### 244# 245# Parameter Types 246# 247# Though native Python types could be used to specify parameter types 248# (the 'ptype' field of the Param and VectorParam classes), it's more 249# flexible to define our own set of types. This gives us more control 250# over how Python expressions are converted to values (via the 251# __init__() constructor) and how these values are printed out (via 252# the __str__() conversion method). 253# 254##################################################################### 255 256# String-valued parameter. Just mixin the ParamValue class with the 257# built-in str class. 258class String(ParamValue,str): 259 cxx_type = 'std::string' 260 cxx_predecls = ['#include <string>'] 261 swig_predecls = ['%include "std_string.i"\n' + 262 '%apply const std::string& {std::string *};'] 263 swig_predecls = ['%include "std_string.i"' ] 264 265 def getValue(self): 266 return self 267 268# superclass for "numeric" parameter values, to emulate math 269# operations in a type-safe way. e.g., a Latency times an int returns 270# a new Latency object. 271class NumericParamValue(ParamValue): 272 def __str__(self): 273 return str(self.value) 274 275 def __float__(self): 276 return float(self.value) 277 278 def __long__(self): 279 return long(self.value) 280 281 def __int__(self): 282 return int(self.value) 283 284 # hook for bounds checking 285 def _check(self): 286 return 287 288 def __mul__(self, other): 289 newobj = self.__class__(self) 290 newobj.value *= other 291 newobj._check() 292 return newobj 293 294 __rmul__ = __mul__ 295 296 def __div__(self, other): 297 newobj = self.__class__(self) 298 newobj.value /= other 299 newobj._check() 300 return newobj 301 302 def __sub__(self, other): 303 newobj = self.__class__(self) 304 newobj.value -= other 305 newobj._check() 306 return newobj 307 308# Metaclass for bounds-checked integer parameters. See CheckedInt. 309class CheckedIntType(MetaParamValue): 310 def __init__(cls, name, bases, dict): 311 super(CheckedIntType, cls).__init__(name, bases, dict) 312 313 # CheckedInt is an abstract base class, so we actually don't 314 # want to do any processing on it... the rest of this code is 315 # just for classes that derive from CheckedInt. 316 if name == 'CheckedInt': 317 return 318 319 if not cls.cxx_predecls: 320 # most derived types require this, so we just do it here once 321 cls.cxx_predecls = ['#include "base/types.hh"'] 322 323 if not cls.swig_predecls: 324 # most derived types require this, so we just do it here once 325 cls.swig_predecls = ['%import "stdint.i"\n' + 326 '%import "base/types.hh"'] 327 328 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 329 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 330 panic("CheckedInt subclass %s must define either\n" \ 331 " 'min' and 'max' or 'size' and 'unsigned'\n", 332 name); 333 if cls.unsigned: 334 cls.min = 0 335 cls.max = 2 ** cls.size - 1 336 else: 337 cls.min = -(2 ** (cls.size - 1)) 338 cls.max = (2 ** (cls.size - 1)) - 1 339 340# Abstract superclass for bounds-checked integer parameters. This 341# class is subclassed to generate parameter classes with specific 342# bounds. Initialization of the min and max bounds is done in the 343# metaclass CheckedIntType.__init__. 344class CheckedInt(NumericParamValue): 345 __metaclass__ = CheckedIntType 346 347 def _check(self): 348 if not self.min <= self.value <= self.max: 349 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 350 (self.min, self.value, self.max) 351 352 def __init__(self, value): 353 if isinstance(value, str): 354 self.value = convert.toInteger(value) 355 elif isinstance(value, (int, long, float, NumericParamValue)): 356 self.value = long(value) 357 else: 358 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 359 % type(value).__name__ 360 self._check() 361 362 def getValue(self): 363 return long(self.value) 364 365class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 366class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 367 368class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 369class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 370class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 371class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 372class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 373class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 374class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 375class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 376 377class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 378class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 379class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 380class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 381 382class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 383 384class Float(ParamValue, float): 385 cxx_type = 'double' 386 387 def __init__(self, value): 388 if isinstance(value, (int, long, float, NumericParamValue, Float)): 389 self.value = float(value) 390 else: 391 raise TypeError, "Can't convert object of type %s to Float" \ 392 % type(value).__name__ 393 394 def getValue(self): 395 return float(self.value) 396 397class MemorySize(CheckedInt): 398 cxx_type = 'uint64_t' 399 size = 64 400 unsigned = True 401 def __init__(self, value): 402 if isinstance(value, MemorySize): 403 self.value = value.value 404 else: 405 self.value = convert.toMemorySize(value) 406 self._check() 407 408class MemorySize32(CheckedInt): 409 cxx_type = 'uint32_t' 410 size = 32 411 unsigned = True 412 def __init__(self, value): 413 if isinstance(value, MemorySize): 414 self.value = value.value 415 else: 416 self.value = convert.toMemorySize(value) 417 self._check() 418 419class Addr(CheckedInt): 420 cxx_type = 'Addr' 421 size = 64 422 unsigned = True 423 def __init__(self, value): 424 if isinstance(value, Addr): 425 self.value = value.value 426 else: 427 try: 428 self.value = convert.toMemorySize(value) 429 except TypeError: 430 self.value = long(value) 431 self._check() 432 def __add__(self, other): 433 if isinstance(other, Addr): 434 return self.value + other.value 435 else: 436 return self.value + other 437 438 439class MetaRange(MetaParamValue): 440 def __init__(cls, name, bases, dict): 441 super(MetaRange, cls).__init__(name, bases, dict) 442 if name == 'Range': 443 return 444 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 445 cls.cxx_predecls = \ 446 ['#include "base/range.hh"'] + cls.type.cxx_predecls 447 448class Range(ParamValue): 449 __metaclass__ = MetaRange 450 type = Int # default; can be overridden in subclasses 451 def __init__(self, *args, **kwargs): 452 def handle_kwargs(self, kwargs): 453 if 'end' in kwargs: 454 self.second = self.type(kwargs.pop('end')) 455 elif 'size' in kwargs: 456 self.second = self.first + self.type(kwargs.pop('size')) - 1 457 else: 458 raise TypeError, "Either end or size must be specified" 459 460 if len(args) == 0: 461 self.first = self.type(kwargs.pop('start')) 462 handle_kwargs(self, kwargs) 463 464 elif len(args) == 1: 465 if kwargs: 466 self.first = self.type(args[0]) 467 handle_kwargs(self, kwargs) 468 elif isinstance(args[0], Range): 469 self.first = self.type(args[0].first) 470 self.second = self.type(args[0].second) 471 elif isinstance(args[0], (list, tuple)): 472 self.first = self.type(args[0][0]) 473 self.second = self.type(args[0][1]) 474 else: 475 self.first = self.type(0) 476 self.second = self.type(args[0]) - 1 477 478 elif len(args) == 2: 479 self.first = self.type(args[0]) 480 self.second = self.type(args[1]) 481 else: 482 raise TypeError, "Too many arguments specified" 483 484 if kwargs: 485 raise TypeError, "too many keywords: %s" % kwargs.keys() 486 487 def __str__(self): 488 return '%s:%s' % (self.first, self.second) 489 490class AddrRange(Range): 491 type = Addr 492 swig_predecls = ['%include "python/swig/range.i"'] 493 494 def getValue(self): 495 from m5.objects.params import AddrRange 496 497 value = AddrRange() 498 value.start = long(self.first) 499 value.end = long(self.second) 500 return value 501 502class TickRange(Range): 503 type = Tick 504 swig_predecls = ['%include "python/swig/range.i"'] 505 506 def getValue(self): 507 from m5.objects.params import TickRange 508 509 value = TickRange() 510 value.start = long(self.first) 511 value.end = long(self.second) 512 return value 513 514# Boolean parameter type. Python doesn't let you subclass bool, since 515# it doesn't want to let you create multiple instances of True and 516# False. Thus this is a little more complicated than String. 517class Bool(ParamValue): 518 cxx_type = 'bool' 519 def __init__(self, value): 520 try: 521 self.value = convert.toBool(value) 522 except TypeError: 523 self.value = bool(value) 524 525 def getValue(self): 526 return bool(self.value) 527 528 def __str__(self): 529 return str(self.value) 530 531 def ini_str(self): 532 if self.value: 533 return 'true' 534 return 'false' 535 536def IncEthernetAddr(addr, val = 1): 537 bytes = map(lambda x: int(x, 16), addr.split(':')) 538 bytes[5] += val 539 for i in (5, 4, 3, 2, 1): 540 val,rem = divmod(bytes[i], 256) 541 bytes[i] = rem 542 if val == 0: 543 break 544 bytes[i - 1] += val 545 assert(bytes[0] <= 255) 546 return ':'.join(map(lambda x: '%02x' % x, bytes)) 547 548_NextEthernetAddr = "00:90:00:00:00:01" 549def NextEthernetAddr(): 550 global _NextEthernetAddr 551 552 value = _NextEthernetAddr 553 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 554 return value 555 556class EthernetAddr(ParamValue): 557 cxx_type = 'Net::EthAddr' 558 cxx_predecls = ['#include "base/inet.hh"'] 559 swig_predecls = ['%include "python/swig/inet.i"'] 560 def __init__(self, value): 561 if value == NextEthernetAddr: 562 self.value = value 563 return 564 565 if not isinstance(value, str): 566 raise TypeError, "expected an ethernet address and didn't get one" 567 568 bytes = value.split(':') 569 if len(bytes) != 6: 570 raise TypeError, 'invalid ethernet address %s' % value 571 572 for byte in bytes: 573 if not 0 <= int(byte) <= 256: 574 raise TypeError, 'invalid ethernet address %s' % value 575 576 self.value = value 577 578 def unproxy(self, base): 579 if self.value == NextEthernetAddr: 580 return EthernetAddr(self.value()) 581 return self 582 583 def getValue(self): 584 from m5.objects.params import EthAddr 585 return EthAddr(self.value) 586 587 def ini_str(self): 588 return self.value 589 590time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 591 "%a %b %d %H:%M:%S %Z %Y", 592 "%Y/%m/%d %H:%M:%S", 593 "%Y/%m/%d %H:%M", 594 "%Y/%m/%d", 595 "%m/%d/%Y %H:%M:%S", 596 "%m/%d/%Y %H:%M", 597 "%m/%d/%Y", 598 "%m/%d/%y %H:%M:%S", 599 "%m/%d/%y %H:%M", 600 "%m/%d/%y"] 601 602 603def parse_time(value): 604 from time import gmtime, strptime, struct_time, time 605 from datetime import datetime, date 606 607 if isinstance(value, struct_time): 608 return value 609 610 if isinstance(value, (int, long)): 611 return gmtime(value) 612 613 if isinstance(value, (datetime, date)): 614 return value.timetuple() 615 616 if isinstance(value, str): 617 if value in ('Now', 'Today'): 618 return time.gmtime(time.time()) 619 620 for format in time_formats: 621 try: 622 return strptime(value, format) 623 except ValueError: 624 pass 625 626 raise ValueError, "Could not parse '%s' as a time" % value 627 628class Time(ParamValue): 629 cxx_type = 'tm' 630 cxx_predecls = [ '#include <time.h>' ] 631 swig_predecls = [ '%include "python/swig/time.i"' ] 632 def __init__(self, value): 633 self.value = parse_time(value) 634 635 def getValue(self): 636 from m5.objects.params import tm 637 638 c_time = tm() 639 py_time = self.value 640 641 # UNIX is years since 1900 642 c_time.tm_year = py_time.tm_year - 1900; 643 644 # Python starts at 1, UNIX starts at 0 645 c_time.tm_mon = py_time.tm_mon - 1; 646 c_time.tm_mday = py_time.tm_mday; 647 c_time.tm_hour = py_time.tm_hour; 648 c_time.tm_min = py_time.tm_min; 649 c_time.tm_sec = py_time.tm_sec; 650 651 # Python has 0 as Monday, UNIX is 0 as sunday 652 c_time.tm_wday = py_time.tm_wday + 1 653 if c_time.tm_wday > 6: 654 c_time.tm_wday -= 7; 655 656 # Python starts at 1, Unix starts at 0 657 c_time.tm_yday = py_time.tm_yday - 1; 658 659 return c_time 660 661 def __str__(self): 662 return time.asctime(self.value) 663 664 def ini_str(self): 665 return str(self) 666 667# Enumerated types are a little more complex. The user specifies the 668# type as Enum(foo) where foo is either a list or dictionary of 669# alternatives (typically strings, but not necessarily so). (In the 670# long run, the integer value of the parameter will be the list index 671# or the corresponding dictionary value. For now, since we only check 672# that the alternative is valid and then spit it into a .ini file, 673# there's not much point in using the dictionary.) 674 675# What Enum() must do is generate a new type encapsulating the 676# provided list/dictionary so that specific values of the parameter 677# can be instances of that type. We define two hidden internal 678# classes (_ListEnum and _DictEnum) to serve as base classes, then 679# derive the new type from the appropriate base class on the fly. 680 681allEnums = {} 682# Metaclass for Enum types 683class MetaEnum(MetaParamValue): 684 def __new__(mcls, name, bases, dict): 685 assert name not in allEnums 686 687 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 688 allEnums[name] = cls 689 return cls 690 691 def __init__(cls, name, bases, init_dict): 692 if init_dict.has_key('map'): 693 if not isinstance(cls.map, dict): 694 raise TypeError, "Enum-derived class attribute 'map' " \ 695 "must be of type dict" 696 # build list of value strings from map 697 cls.vals = cls.map.keys() 698 cls.vals.sort() 699 elif init_dict.has_key('vals'): 700 if not isinstance(cls.vals, list): 701 raise TypeError, "Enum-derived class attribute 'vals' " \ 702 "must be of type list" 703 # build string->value map from vals sequence 704 cls.map = {} 705 for idx,val in enumerate(cls.vals): 706 cls.map[val] = idx 707 else: 708 raise TypeError, "Enum-derived class must define "\ 709 "attribute 'map' or 'vals'" 710 711 cls.cxx_type = 'Enums::%s' % name 712 713 super(MetaEnum, cls).__init__(name, bases, init_dict) 714 715 # Generate C++ class declaration for this enum type. 716 # Note that we wrap the enum in a class/struct to act as a namespace, 717 # so that the enum strings can be brief w/o worrying about collisions. 718 def cxx_decl(cls): 719 name = cls.__name__ 720 code = "#ifndef __ENUM__%s\n" % name 721 code += '#define __ENUM__%s\n' % name 722 code += '\n' 723 code += 'namespace Enums {\n' 724 code += ' enum %s {\n' % name 725 for val in cls.vals: 726 code += ' %s = %d,\n' % (val, cls.map[val]) 727 code += ' Num_%s = %d,\n' % (name, len(cls.vals)) 728 code += ' };\n' 729 code += ' extern const char *%sStrings[Num_%s];\n' % (name, name) 730 code += '}\n' 731 code += '\n' 732 code += '#endif\n' 733 return code 734 735 def cxx_def(cls): 736 name = cls.__name__ 737 code = '#include "enums/%s.hh"\n' % name 738 code += 'namespace Enums {\n' 739 code += ' const char *%sStrings[Num_%s] =\n' % (name, name) 740 code += ' {\n' 741 for val in cls.vals: 742 code += ' "%s",\n' % val 743 code += ' };\n' 744 code += '}\n' 745 return code 746 747# Base class for enum types. 748class Enum(ParamValue): 749 __metaclass__ = MetaEnum 750 vals = [] 751 752 def __init__(self, value): 753 if value not in self.map: 754 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 755 % (value, self.vals) 756 self.value = value 757 758 def getValue(self): 759 return int(self.map[self.value]) 760 761 def __str__(self): 762 return self.value 763 764# how big does a rounding error need to be before we warn about it? 765frequency_tolerance = 0.001 # 0.1% 766 767class TickParamValue(NumericParamValue): 768 cxx_type = 'Tick' 769 cxx_predecls = ['#include "base/types.hh"'] 770 swig_predecls = ['%import "stdint.i"\n' + 771 '%import "base/types.hh"'] 772 773 def getValue(self): 774 return long(self.value) 775 776class Latency(TickParamValue): 777 def __init__(self, value): 778 if isinstance(value, (Latency, Clock)): 779 self.ticks = value.ticks 780 self.value = value.value 781 elif isinstance(value, Frequency): 782 self.ticks = value.ticks 783 self.value = 1.0 / value.value 784 elif value.endswith('t'): 785 self.ticks = True 786 self.value = int(value[:-1]) 787 else: 788 self.ticks = False 789 self.value = convert.toLatency(value) 790 791 def __getattr__(self, attr): 792 if attr in ('latency', 'period'): 793 return self 794 if attr == 'frequency': 795 return Frequency(self) 796 raise AttributeError, "Latency object has no attribute '%s'" % attr 797 798 def getValue(self): 799 if self.ticks or self.value == 0: 800 value = self.value 801 else: 802 value = ticks.fromSeconds(self.value) 803 return long(value) 804 805 # convert latency to ticks 806 def ini_str(self): 807 return '%d' % self.getValue() 808 809class Frequency(TickParamValue): 810 def __init__(self, value): 811 if isinstance(value, (Latency, Clock)): 812 if value.value == 0: 813 self.value = 0 814 else: 815 self.value = 1.0 / value.value 816 self.ticks = value.ticks 817 elif isinstance(value, Frequency): 818 self.value = value.value 819 self.ticks = value.ticks 820 else: 821 self.ticks = False 822 self.value = convert.toFrequency(value) 823 824 def __getattr__(self, attr): 825 if attr == 'frequency': 826 return self 827 if attr in ('latency', 'period'): 828 return Latency(self) 829 raise AttributeError, "Frequency object has no attribute '%s'" % attr 830 831 # convert latency to ticks 832 def getValue(self): 833 if self.ticks or self.value == 0: 834 value = self.value 835 else: 836 value = ticks.fromSeconds(1.0 / self.value) 837 return long(value) 838 839 def ini_str(self): 840 return '%d' % self.getValue() 841 842# A generic frequency and/or Latency value. Value is stored as a latency, 843# but to avoid ambiguity this object does not support numeric ops (* or /). 844# An explicit conversion to a Latency or Frequency must be made first. 845class Clock(ParamValue): 846 cxx_type = 'Tick' 847 cxx_predecls = ['#include "base/types.hh"'] 848 swig_predecls = ['%import "stdint.i"\n' + 849 '%import "base/types.hh"'] 850 def __init__(self, value): 851 if isinstance(value, (Latency, Clock)): 852 self.ticks = value.ticks 853 self.value = value.value 854 elif isinstance(value, Frequency): 855 self.ticks = value.ticks 856 self.value = 1.0 / value.value 857 elif value.endswith('t'): 858 self.ticks = True 859 self.value = int(value[:-1]) 860 else: 861 self.ticks = False 862 self.value = convert.anyToLatency(value) 863 864 def __getattr__(self, attr): 865 if attr == 'frequency': 866 return Frequency(self) 867 if attr in ('latency', 'period'): 868 return Latency(self) 869 raise AttributeError, "Frequency object has no attribute '%s'" % attr 870 871 def getValue(self): 872 return self.period.getValue() 873 874 def ini_str(self): 875 return self.period.ini_str() 876 877class NetworkBandwidth(float,ParamValue): 878 cxx_type = 'float' 879 def __new__(cls, value): 880 # convert to bits per second 881 val = convert.toNetworkBandwidth(value) 882 return super(cls, NetworkBandwidth).__new__(cls, val) 883 884 def __str__(self): 885 return str(self.val) 886 887 def getValue(self): 888 # convert to seconds per byte 889 value = 8.0 / float(self) 890 # convert to ticks per byte 891 value = ticks.fromSeconds(value) 892 return float(value) 893 894 def ini_str(self): 895 return '%f' % self.getValue() 896 897class MemoryBandwidth(float,ParamValue): 898 cxx_type = 'float' 899 def __new__(cls, value): 900 # we want the number of ticks per byte of data 901 val = convert.toMemoryBandwidth(value) 902 return super(cls, MemoryBandwidth).__new__(cls, val) 903 904 def __str__(self): 905 return str(self.val) 906 907 def getValue(self): 908 # convert to seconds per byte 909 value = float(self) 910 if value: 911 value = 1.0 / float(self) 912 # convert to ticks per byte 913 value = ticks.fromSeconds(value) 914 return float(value) 915 916 def ini_str(self): 917 return '%f' % self.getValue() 918 919# 920# "Constants"... handy aliases for various values. 921# 922 923# Special class for NULL pointers. Note the special check in 924# make_param_value() above that lets these be assigned where a 925# SimObject is required. 926# only one copy of a particular node 927class NullSimObject(object): 928 __metaclass__ = Singleton 929 930 def __call__(cls): 931 return cls 932 933 def _instantiate(self, parent = None, path = ''): 934 pass 935 936 def ini_str(self): 937 return 'Null' 938 939 def unproxy(self, base): 940 return self 941 942 def set_path(self, parent, name): 943 pass 944 945 def __str__(self): 946 return 'Null' 947 948 def getValue(self): 949 return None 950 951# The only instance you'll ever need... 952NULL = NullSimObject() 953 954def isNullPointer(value): 955 return isinstance(value, NullSimObject) 956 957# Some memory range specifications use this as a default upper bound. 958MaxAddr = Addr.max 959MaxTick = Tick.max 960AllMemory = AddrRange(0, MaxAddr) 961 962 963##################################################################### 964# 965# Port objects 966# 967# Ports are used to interconnect objects in the memory system. 968# 969##################################################################### 970 971# Port reference: encapsulates a reference to a particular port on a 972# particular SimObject. 973class PortRef(object): 974 def __init__(self, simobj, name): 975 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 976 self.simobj = simobj 977 self.name = name 978 self.peer = None # not associated with another port yet 979 self.ccConnected = False # C++ port connection done? 980 self.index = -1 # always -1 for non-vector ports 981 982 def __str__(self): 983 return '%s.%s' % (self.simobj, self.name) 984 985 # for config.ini, print peer's name (not ours) 986 def ini_str(self): 987 return str(self.peer) 988 989 def __getattr__(self, attr): 990 if attr == 'peerObj': 991 # shorthand for proxies 992 return self.peer.simobj 993 raise AttributeError, "'%s' object has no attribute '%s'" % \ 994 (self.__class__.__name__, attr) 995 996 # Full connection is symmetric (both ways). Called via 997 # SimObject.__setattr__ as a result of a port assignment, e.g., 998 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 999 # e.g., "obj1.portA[3] = obj2.portB". 1000 def connect(self, other): 1001 if isinstance(other, VectorPortRef): 1002 # reference to plain VectorPort is implicit append 1003 other = other._get_next() 1004 if self.peer and not proxy.isproxy(self.peer): 1005 print "warning: overwriting port", self, \ 1006 "value", self.peer, "with", other 1007 self.peer.peer = None 1008 self.peer = other 1009 if proxy.isproxy(other): 1010 other.set_param_desc(PortParamDesc()) 1011 elif isinstance(other, PortRef): 1012 if other.peer is not self: 1013 other.connect(self) 1014 else: 1015 raise TypeError, \ 1016 "assigning non-port reference '%s' to port '%s'" \ 1017 % (other, self) 1018 1019 def clone(self, simobj, memo): 1020 if memo.has_key(self): 1021 return memo[self] 1022 newRef = copy.copy(self) 1023 memo[self] = newRef 1024 newRef.simobj = simobj 1025 assert(isSimObject(newRef.simobj)) 1026 if self.peer and not proxy.isproxy(self.peer): 1027 peerObj = self.peer.simobj(_memo=memo) 1028 newRef.peer = self.peer.clone(peerObj, memo) 1029 assert(not isinstance(newRef.peer, VectorPortRef)) 1030 return newRef 1031 1032 def unproxy(self, simobj): 1033 assert(simobj is self.simobj) 1034 if proxy.isproxy(self.peer): 1035 try: 1036 realPeer = self.peer.unproxy(self.simobj) 1037 except: 1038 print "Error in unproxying port '%s' of %s" % \ 1039 (self.name, self.simobj.path()) 1040 raise 1041 self.connect(realPeer) 1042 1043 # Call C++ to create corresponding port connection between C++ objects 1044 def ccConnect(self): 1045 from m5.objects.params import connectPorts 1046 1047 if self.ccConnected: # already done this 1048 return 1049 peer = self.peer 1050 if not self.peer: # nothing to connect to 1051 return 1052 try: 1053 connectPorts(self.simobj.getCCObject(), self.name, self.index, 1054 peer.simobj.getCCObject(), peer.name, peer.index) 1055 except: 1056 print "Error connecting port %s.%s to %s.%s" % \ 1057 (self.simobj.path(), self.name, 1058 peer.simobj.path(), peer.name) 1059 raise 1060 self.ccConnected = True 1061 peer.ccConnected = True 1062 1063# A reference to an individual element of a VectorPort... much like a 1064# PortRef, but has an index. 1065class VectorPortElementRef(PortRef): 1066 def __init__(self, simobj, name, index): 1067 PortRef.__init__(self, simobj, name) 1068 self.index = index 1069 1070 def __str__(self): 1071 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1072 1073# A reference to a complete vector-valued port (not just a single element). 1074# Can be indexed to retrieve individual VectorPortElementRef instances. 1075class VectorPortRef(object): 1076 def __init__(self, simobj, name): 1077 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1078 self.simobj = simobj 1079 self.name = name 1080 self.elements = [] 1081 1082 def __str__(self): 1083 return '%s.%s[:]' % (self.simobj, self.name) 1084 1085 # for config.ini, print peer's name (not ours) 1086 def ini_str(self): 1087 return ' '.join([el.ini_str() for el in self.elements]) 1088 1089 def __getitem__(self, key): 1090 if not isinstance(key, int): 1091 raise TypeError, "VectorPort index must be integer" 1092 if key >= len(self.elements): 1093 # need to extend list 1094 ext = [VectorPortElementRef(self.simobj, self.name, i) 1095 for i in range(len(self.elements), key+1)] 1096 self.elements.extend(ext) 1097 return self.elements[key] 1098 1099 def _get_next(self): 1100 return self[len(self.elements)] 1101 1102 def __setitem__(self, key, value): 1103 if not isinstance(key, int): 1104 raise TypeError, "VectorPort index must be integer" 1105 self[key].connect(value) 1106 1107 def connect(self, other): 1108 if isinstance(other, (list, tuple)): 1109 # Assign list of port refs to vector port. 1110 # For now, append them... not sure if that's the right semantics 1111 # or if it should replace the current vector. 1112 for ref in other: 1113 self._get_next().connect(ref) 1114 else: 1115 # scalar assignment to plain VectorPort is implicit append 1116 self._get_next().connect(other) 1117 1118 def clone(self, simobj, memo): 1119 if memo.has_key(self): 1120 return memo[self] 1121 newRef = copy.copy(self) 1122 memo[self] = newRef 1123 newRef.simobj = simobj 1124 assert(isSimObject(newRef.simobj)) 1125 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 1126 return newRef 1127 1128 def unproxy(self, simobj): 1129 [el.unproxy(simobj) for el in self.elements] 1130 1131 def ccConnect(self): 1132 [el.ccConnect() for el in self.elements] 1133 1134# Port description object. Like a ParamDesc object, this represents a 1135# logical port in the SimObject class, not a particular port on a 1136# SimObject instance. The latter are represented by PortRef objects. 1137class Port(object): 1138 # Port("description") or Port(default, "description") 1139 def __init__(self, *args): 1140 if len(args) == 1: 1141 self.desc = args[0] 1142 elif len(args) == 2: 1143 self.default = args[0] 1144 self.desc = args[1] 1145 else: 1146 raise TypeError, 'wrong number of arguments' 1147 # self.name is set by SimObject class on assignment 1148 # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 1149 1150 # Generate a PortRef for this port on the given SimObject with the 1151 # given name 1152 def makeRef(self, simobj): 1153 return PortRef(simobj, self.name) 1154 1155 # Connect an instance of this port (on the given SimObject with 1156 # the given name) with the port described by the supplied PortRef 1157 def connect(self, simobj, ref): 1158 self.makeRef(simobj).connect(ref) 1159 1160# VectorPort description object. Like Port, but represents a vector 1161# of connections (e.g., as on a Bus). 1162class VectorPort(Port): 1163 def __init__(self, *args): 1164 Port.__init__(self, *args) 1165 self.isVec = True 1166 1167 def makeRef(self, simobj): 1168 return VectorPortRef(simobj, self.name) 1169 1170# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 1171# proxy objects (via set_param_desc()) so that proxy error messages 1172# make sense. 1173class PortParamDesc(object): 1174 __metaclass__ = Singleton 1175 1176 ptype_str = 'Port' 1177 ptype = Port 1178 1179baseEnums = allEnums.copy() 1180baseParams = allParams.copy() 1181 1182def clear(): 1183 global allEnums, allParams 1184 1185 allEnums = baseEnums.copy() 1186 allParams = baseParams.copy() 1187 1188__all__ = ['Param', 'VectorParam', 1189 'Enum', 'Bool', 'String', 'Float', 1190 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1191 'Int32', 'UInt32', 'Int64', 'UInt64', 1192 'Counter', 'Addr', 'Tick', 'Percent', 1193 'TcpPort', 'UdpPort', 'EthernetAddr', 1194 'MemorySize', 'MemorySize32', 1195 'Latency', 'Frequency', 'Clock', 1196 'NetworkBandwidth', 'MemoryBandwidth', 1197 'Range', 'AddrRange', 'TickRange', 1198 'MaxAddr', 'MaxTick', 'AllMemory', 1199 'Time', 1200 'NextEthernetAddr', 'NULL', 1201 'Port', 'VectorPort'] 1202 1203import SimObject
| 234 else: 235 return VectorParamValue(tmp_list) 236 237 def swig_predecls(self): 238 return ['%%include "%s_vptype.i"' % self.ptype_str] 239 240 def swig_decl(self): 241 cxx_type = re.sub('std::', '', self.ptype.cxx_type) 242 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \ 243 (self.ptype_str, cxx_type) 244 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl] 245 246 def cxx_predecls(self): 247 return ['#include <vector>'] + self.ptype.cxx_predecls 248 249 def cxx_decl(self): 250 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) 251 252class ParamFactory(object): 253 def __init__(self, param_desc_class, ptype_str = None): 254 self.param_desc_class = param_desc_class 255 self.ptype_str = ptype_str 256 257 def __getattr__(self, attr): 258 if self.ptype_str: 259 attr = self.ptype_str + '.' + attr 260 return ParamFactory(self.param_desc_class, attr) 261 262 # E.g., Param.Int(5, "number of widgets") 263 def __call__(self, *args, **kwargs): 264 ptype = None 265 try: 266 ptype = allParams[self.ptype_str] 267 except KeyError: 268 # if name isn't defined yet, assume it's a SimObject, and 269 # try to resolve it later 270 pass 271 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 272 273Param = ParamFactory(ParamDesc) 274VectorParam = ParamFactory(VectorParamDesc) 275 276##################################################################### 277# 278# Parameter Types 279# 280# Though native Python types could be used to specify parameter types 281# (the 'ptype' field of the Param and VectorParam classes), it's more 282# flexible to define our own set of types. This gives us more control 283# over how Python expressions are converted to values (via the 284# __init__() constructor) and how these values are printed out (via 285# the __str__() conversion method). 286# 287##################################################################### 288 289# String-valued parameter. Just mixin the ParamValue class with the 290# built-in str class. 291class String(ParamValue,str): 292 cxx_type = 'std::string' 293 cxx_predecls = ['#include <string>'] 294 swig_predecls = ['%include "std_string.i"\n' + 295 '%apply const std::string& {std::string *};'] 296 swig_predecls = ['%include "std_string.i"' ] 297 298 def getValue(self): 299 return self 300 301# superclass for "numeric" parameter values, to emulate math 302# operations in a type-safe way. e.g., a Latency times an int returns 303# a new Latency object. 304class NumericParamValue(ParamValue): 305 def __str__(self): 306 return str(self.value) 307 308 def __float__(self): 309 return float(self.value) 310 311 def __long__(self): 312 return long(self.value) 313 314 def __int__(self): 315 return int(self.value) 316 317 # hook for bounds checking 318 def _check(self): 319 return 320 321 def __mul__(self, other): 322 newobj = self.__class__(self) 323 newobj.value *= other 324 newobj._check() 325 return newobj 326 327 __rmul__ = __mul__ 328 329 def __div__(self, other): 330 newobj = self.__class__(self) 331 newobj.value /= other 332 newobj._check() 333 return newobj 334 335 def __sub__(self, other): 336 newobj = self.__class__(self) 337 newobj.value -= other 338 newobj._check() 339 return newobj 340 341# Metaclass for bounds-checked integer parameters. See CheckedInt. 342class CheckedIntType(MetaParamValue): 343 def __init__(cls, name, bases, dict): 344 super(CheckedIntType, cls).__init__(name, bases, dict) 345 346 # CheckedInt is an abstract base class, so we actually don't 347 # want to do any processing on it... the rest of this code is 348 # just for classes that derive from CheckedInt. 349 if name == 'CheckedInt': 350 return 351 352 if not cls.cxx_predecls: 353 # most derived types require this, so we just do it here once 354 cls.cxx_predecls = ['#include "base/types.hh"'] 355 356 if not cls.swig_predecls: 357 # most derived types require this, so we just do it here once 358 cls.swig_predecls = ['%import "stdint.i"\n' + 359 '%import "base/types.hh"'] 360 361 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 362 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 363 panic("CheckedInt subclass %s must define either\n" \ 364 " 'min' and 'max' or 'size' and 'unsigned'\n", 365 name); 366 if cls.unsigned: 367 cls.min = 0 368 cls.max = 2 ** cls.size - 1 369 else: 370 cls.min = -(2 ** (cls.size - 1)) 371 cls.max = (2 ** (cls.size - 1)) - 1 372 373# Abstract superclass for bounds-checked integer parameters. This 374# class is subclassed to generate parameter classes with specific 375# bounds. Initialization of the min and max bounds is done in the 376# metaclass CheckedIntType.__init__. 377class CheckedInt(NumericParamValue): 378 __metaclass__ = CheckedIntType 379 380 def _check(self): 381 if not self.min <= self.value <= self.max: 382 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 383 (self.min, self.value, self.max) 384 385 def __init__(self, value): 386 if isinstance(value, str): 387 self.value = convert.toInteger(value) 388 elif isinstance(value, (int, long, float, NumericParamValue)): 389 self.value = long(value) 390 else: 391 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 392 % type(value).__name__ 393 self._check() 394 395 def getValue(self): 396 return long(self.value) 397 398class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 399class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 400 401class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 402class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 403class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 404class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 405class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 406class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 407class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 408class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 409 410class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 411class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 412class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 413class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 414 415class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 416 417class Float(ParamValue, float): 418 cxx_type = 'double' 419 420 def __init__(self, value): 421 if isinstance(value, (int, long, float, NumericParamValue, Float)): 422 self.value = float(value) 423 else: 424 raise TypeError, "Can't convert object of type %s to Float" \ 425 % type(value).__name__ 426 427 def getValue(self): 428 return float(self.value) 429 430class MemorySize(CheckedInt): 431 cxx_type = 'uint64_t' 432 size = 64 433 unsigned = True 434 def __init__(self, value): 435 if isinstance(value, MemorySize): 436 self.value = value.value 437 else: 438 self.value = convert.toMemorySize(value) 439 self._check() 440 441class MemorySize32(CheckedInt): 442 cxx_type = 'uint32_t' 443 size = 32 444 unsigned = True 445 def __init__(self, value): 446 if isinstance(value, MemorySize): 447 self.value = value.value 448 else: 449 self.value = convert.toMemorySize(value) 450 self._check() 451 452class Addr(CheckedInt): 453 cxx_type = 'Addr' 454 size = 64 455 unsigned = True 456 def __init__(self, value): 457 if isinstance(value, Addr): 458 self.value = value.value 459 else: 460 try: 461 self.value = convert.toMemorySize(value) 462 except TypeError: 463 self.value = long(value) 464 self._check() 465 def __add__(self, other): 466 if isinstance(other, Addr): 467 return self.value + other.value 468 else: 469 return self.value + other 470 471 472class MetaRange(MetaParamValue): 473 def __init__(cls, name, bases, dict): 474 super(MetaRange, cls).__init__(name, bases, dict) 475 if name == 'Range': 476 return 477 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 478 cls.cxx_predecls = \ 479 ['#include "base/range.hh"'] + cls.type.cxx_predecls 480 481class Range(ParamValue): 482 __metaclass__ = MetaRange 483 type = Int # default; can be overridden in subclasses 484 def __init__(self, *args, **kwargs): 485 def handle_kwargs(self, kwargs): 486 if 'end' in kwargs: 487 self.second = self.type(kwargs.pop('end')) 488 elif 'size' in kwargs: 489 self.second = self.first + self.type(kwargs.pop('size')) - 1 490 else: 491 raise TypeError, "Either end or size must be specified" 492 493 if len(args) == 0: 494 self.first = self.type(kwargs.pop('start')) 495 handle_kwargs(self, kwargs) 496 497 elif len(args) == 1: 498 if kwargs: 499 self.first = self.type(args[0]) 500 handle_kwargs(self, kwargs) 501 elif isinstance(args[0], Range): 502 self.first = self.type(args[0].first) 503 self.second = self.type(args[0].second) 504 elif isinstance(args[0], (list, tuple)): 505 self.first = self.type(args[0][0]) 506 self.second = self.type(args[0][1]) 507 else: 508 self.first = self.type(0) 509 self.second = self.type(args[0]) - 1 510 511 elif len(args) == 2: 512 self.first = self.type(args[0]) 513 self.second = self.type(args[1]) 514 else: 515 raise TypeError, "Too many arguments specified" 516 517 if kwargs: 518 raise TypeError, "too many keywords: %s" % kwargs.keys() 519 520 def __str__(self): 521 return '%s:%s' % (self.first, self.second) 522 523class AddrRange(Range): 524 type = Addr 525 swig_predecls = ['%include "python/swig/range.i"'] 526 527 def getValue(self): 528 from m5.objects.params import AddrRange 529 530 value = AddrRange() 531 value.start = long(self.first) 532 value.end = long(self.second) 533 return value 534 535class TickRange(Range): 536 type = Tick 537 swig_predecls = ['%include "python/swig/range.i"'] 538 539 def getValue(self): 540 from m5.objects.params import TickRange 541 542 value = TickRange() 543 value.start = long(self.first) 544 value.end = long(self.second) 545 return value 546 547# Boolean parameter type. Python doesn't let you subclass bool, since 548# it doesn't want to let you create multiple instances of True and 549# False. Thus this is a little more complicated than String. 550class Bool(ParamValue): 551 cxx_type = 'bool' 552 def __init__(self, value): 553 try: 554 self.value = convert.toBool(value) 555 except TypeError: 556 self.value = bool(value) 557 558 def getValue(self): 559 return bool(self.value) 560 561 def __str__(self): 562 return str(self.value) 563 564 def ini_str(self): 565 if self.value: 566 return 'true' 567 return 'false' 568 569def IncEthernetAddr(addr, val = 1): 570 bytes = map(lambda x: int(x, 16), addr.split(':')) 571 bytes[5] += val 572 for i in (5, 4, 3, 2, 1): 573 val,rem = divmod(bytes[i], 256) 574 bytes[i] = rem 575 if val == 0: 576 break 577 bytes[i - 1] += val 578 assert(bytes[0] <= 255) 579 return ':'.join(map(lambda x: '%02x' % x, bytes)) 580 581_NextEthernetAddr = "00:90:00:00:00:01" 582def NextEthernetAddr(): 583 global _NextEthernetAddr 584 585 value = _NextEthernetAddr 586 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 587 return value 588 589class EthernetAddr(ParamValue): 590 cxx_type = 'Net::EthAddr' 591 cxx_predecls = ['#include "base/inet.hh"'] 592 swig_predecls = ['%include "python/swig/inet.i"'] 593 def __init__(self, value): 594 if value == NextEthernetAddr: 595 self.value = value 596 return 597 598 if not isinstance(value, str): 599 raise TypeError, "expected an ethernet address and didn't get one" 600 601 bytes = value.split(':') 602 if len(bytes) != 6: 603 raise TypeError, 'invalid ethernet address %s' % value 604 605 for byte in bytes: 606 if not 0 <= int(byte) <= 256: 607 raise TypeError, 'invalid ethernet address %s' % value 608 609 self.value = value 610 611 def unproxy(self, base): 612 if self.value == NextEthernetAddr: 613 return EthernetAddr(self.value()) 614 return self 615 616 def getValue(self): 617 from m5.objects.params import EthAddr 618 return EthAddr(self.value) 619 620 def ini_str(self): 621 return self.value 622 623time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 624 "%a %b %d %H:%M:%S %Z %Y", 625 "%Y/%m/%d %H:%M:%S", 626 "%Y/%m/%d %H:%M", 627 "%Y/%m/%d", 628 "%m/%d/%Y %H:%M:%S", 629 "%m/%d/%Y %H:%M", 630 "%m/%d/%Y", 631 "%m/%d/%y %H:%M:%S", 632 "%m/%d/%y %H:%M", 633 "%m/%d/%y"] 634 635 636def parse_time(value): 637 from time import gmtime, strptime, struct_time, time 638 from datetime import datetime, date 639 640 if isinstance(value, struct_time): 641 return value 642 643 if isinstance(value, (int, long)): 644 return gmtime(value) 645 646 if isinstance(value, (datetime, date)): 647 return value.timetuple() 648 649 if isinstance(value, str): 650 if value in ('Now', 'Today'): 651 return time.gmtime(time.time()) 652 653 for format in time_formats: 654 try: 655 return strptime(value, format) 656 except ValueError: 657 pass 658 659 raise ValueError, "Could not parse '%s' as a time" % value 660 661class Time(ParamValue): 662 cxx_type = 'tm' 663 cxx_predecls = [ '#include <time.h>' ] 664 swig_predecls = [ '%include "python/swig/time.i"' ] 665 def __init__(self, value): 666 self.value = parse_time(value) 667 668 def getValue(self): 669 from m5.objects.params import tm 670 671 c_time = tm() 672 py_time = self.value 673 674 # UNIX is years since 1900 675 c_time.tm_year = py_time.tm_year - 1900; 676 677 # Python starts at 1, UNIX starts at 0 678 c_time.tm_mon = py_time.tm_mon - 1; 679 c_time.tm_mday = py_time.tm_mday; 680 c_time.tm_hour = py_time.tm_hour; 681 c_time.tm_min = py_time.tm_min; 682 c_time.tm_sec = py_time.tm_sec; 683 684 # Python has 0 as Monday, UNIX is 0 as sunday 685 c_time.tm_wday = py_time.tm_wday + 1 686 if c_time.tm_wday > 6: 687 c_time.tm_wday -= 7; 688 689 # Python starts at 1, Unix starts at 0 690 c_time.tm_yday = py_time.tm_yday - 1; 691 692 return c_time 693 694 def __str__(self): 695 return time.asctime(self.value) 696 697 def ini_str(self): 698 return str(self) 699 700# Enumerated types are a little more complex. The user specifies the 701# type as Enum(foo) where foo is either a list or dictionary of 702# alternatives (typically strings, but not necessarily so). (In the 703# long run, the integer value of the parameter will be the list index 704# or the corresponding dictionary value. For now, since we only check 705# that the alternative is valid and then spit it into a .ini file, 706# there's not much point in using the dictionary.) 707 708# What Enum() must do is generate a new type encapsulating the 709# provided list/dictionary so that specific values of the parameter 710# can be instances of that type. We define two hidden internal 711# classes (_ListEnum and _DictEnum) to serve as base classes, then 712# derive the new type from the appropriate base class on the fly. 713 714allEnums = {} 715# Metaclass for Enum types 716class MetaEnum(MetaParamValue): 717 def __new__(mcls, name, bases, dict): 718 assert name not in allEnums 719 720 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 721 allEnums[name] = cls 722 return cls 723 724 def __init__(cls, name, bases, init_dict): 725 if init_dict.has_key('map'): 726 if not isinstance(cls.map, dict): 727 raise TypeError, "Enum-derived class attribute 'map' " \ 728 "must be of type dict" 729 # build list of value strings from map 730 cls.vals = cls.map.keys() 731 cls.vals.sort() 732 elif init_dict.has_key('vals'): 733 if not isinstance(cls.vals, list): 734 raise TypeError, "Enum-derived class attribute 'vals' " \ 735 "must be of type list" 736 # build string->value map from vals sequence 737 cls.map = {} 738 for idx,val in enumerate(cls.vals): 739 cls.map[val] = idx 740 else: 741 raise TypeError, "Enum-derived class must define "\ 742 "attribute 'map' or 'vals'" 743 744 cls.cxx_type = 'Enums::%s' % name 745 746 super(MetaEnum, cls).__init__(name, bases, init_dict) 747 748 # Generate C++ class declaration for this enum type. 749 # Note that we wrap the enum in a class/struct to act as a namespace, 750 # so that the enum strings can be brief w/o worrying about collisions. 751 def cxx_decl(cls): 752 name = cls.__name__ 753 code = "#ifndef __ENUM__%s\n" % name 754 code += '#define __ENUM__%s\n' % name 755 code += '\n' 756 code += 'namespace Enums {\n' 757 code += ' enum %s {\n' % name 758 for val in cls.vals: 759 code += ' %s = %d,\n' % (val, cls.map[val]) 760 code += ' Num_%s = %d,\n' % (name, len(cls.vals)) 761 code += ' };\n' 762 code += ' extern const char *%sStrings[Num_%s];\n' % (name, name) 763 code += '}\n' 764 code += '\n' 765 code += '#endif\n' 766 return code 767 768 def cxx_def(cls): 769 name = cls.__name__ 770 code = '#include "enums/%s.hh"\n' % name 771 code += 'namespace Enums {\n' 772 code += ' const char *%sStrings[Num_%s] =\n' % (name, name) 773 code += ' {\n' 774 for val in cls.vals: 775 code += ' "%s",\n' % val 776 code += ' };\n' 777 code += '}\n' 778 return code 779 780# Base class for enum types. 781class Enum(ParamValue): 782 __metaclass__ = MetaEnum 783 vals = [] 784 785 def __init__(self, value): 786 if value not in self.map: 787 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 788 % (value, self.vals) 789 self.value = value 790 791 def getValue(self): 792 return int(self.map[self.value]) 793 794 def __str__(self): 795 return self.value 796 797# how big does a rounding error need to be before we warn about it? 798frequency_tolerance = 0.001 # 0.1% 799 800class TickParamValue(NumericParamValue): 801 cxx_type = 'Tick' 802 cxx_predecls = ['#include "base/types.hh"'] 803 swig_predecls = ['%import "stdint.i"\n' + 804 '%import "base/types.hh"'] 805 806 def getValue(self): 807 return long(self.value) 808 809class Latency(TickParamValue): 810 def __init__(self, value): 811 if isinstance(value, (Latency, Clock)): 812 self.ticks = value.ticks 813 self.value = value.value 814 elif isinstance(value, Frequency): 815 self.ticks = value.ticks 816 self.value = 1.0 / value.value 817 elif value.endswith('t'): 818 self.ticks = True 819 self.value = int(value[:-1]) 820 else: 821 self.ticks = False 822 self.value = convert.toLatency(value) 823 824 def __getattr__(self, attr): 825 if attr in ('latency', 'period'): 826 return self 827 if attr == 'frequency': 828 return Frequency(self) 829 raise AttributeError, "Latency object has no attribute '%s'" % attr 830 831 def getValue(self): 832 if self.ticks or self.value == 0: 833 value = self.value 834 else: 835 value = ticks.fromSeconds(self.value) 836 return long(value) 837 838 # convert latency to ticks 839 def ini_str(self): 840 return '%d' % self.getValue() 841 842class Frequency(TickParamValue): 843 def __init__(self, value): 844 if isinstance(value, (Latency, Clock)): 845 if value.value == 0: 846 self.value = 0 847 else: 848 self.value = 1.0 / value.value 849 self.ticks = value.ticks 850 elif isinstance(value, Frequency): 851 self.value = value.value 852 self.ticks = value.ticks 853 else: 854 self.ticks = False 855 self.value = convert.toFrequency(value) 856 857 def __getattr__(self, attr): 858 if attr == 'frequency': 859 return self 860 if attr in ('latency', 'period'): 861 return Latency(self) 862 raise AttributeError, "Frequency object has no attribute '%s'" % attr 863 864 # convert latency to ticks 865 def getValue(self): 866 if self.ticks or self.value == 0: 867 value = self.value 868 else: 869 value = ticks.fromSeconds(1.0 / self.value) 870 return long(value) 871 872 def ini_str(self): 873 return '%d' % self.getValue() 874 875# A generic frequency and/or Latency value. Value is stored as a latency, 876# but to avoid ambiguity this object does not support numeric ops (* or /). 877# An explicit conversion to a Latency or Frequency must be made first. 878class Clock(ParamValue): 879 cxx_type = 'Tick' 880 cxx_predecls = ['#include "base/types.hh"'] 881 swig_predecls = ['%import "stdint.i"\n' + 882 '%import "base/types.hh"'] 883 def __init__(self, value): 884 if isinstance(value, (Latency, Clock)): 885 self.ticks = value.ticks 886 self.value = value.value 887 elif isinstance(value, Frequency): 888 self.ticks = value.ticks 889 self.value = 1.0 / value.value 890 elif value.endswith('t'): 891 self.ticks = True 892 self.value = int(value[:-1]) 893 else: 894 self.ticks = False 895 self.value = convert.anyToLatency(value) 896 897 def __getattr__(self, attr): 898 if attr == 'frequency': 899 return Frequency(self) 900 if attr in ('latency', 'period'): 901 return Latency(self) 902 raise AttributeError, "Frequency object has no attribute '%s'" % attr 903 904 def getValue(self): 905 return self.period.getValue() 906 907 def ini_str(self): 908 return self.period.ini_str() 909 910class NetworkBandwidth(float,ParamValue): 911 cxx_type = 'float' 912 def __new__(cls, value): 913 # convert to bits per second 914 val = convert.toNetworkBandwidth(value) 915 return super(cls, NetworkBandwidth).__new__(cls, val) 916 917 def __str__(self): 918 return str(self.val) 919 920 def getValue(self): 921 # convert to seconds per byte 922 value = 8.0 / float(self) 923 # convert to ticks per byte 924 value = ticks.fromSeconds(value) 925 return float(value) 926 927 def ini_str(self): 928 return '%f' % self.getValue() 929 930class MemoryBandwidth(float,ParamValue): 931 cxx_type = 'float' 932 def __new__(cls, value): 933 # we want the number of ticks per byte of data 934 val = convert.toMemoryBandwidth(value) 935 return super(cls, MemoryBandwidth).__new__(cls, val) 936 937 def __str__(self): 938 return str(self.val) 939 940 def getValue(self): 941 # convert to seconds per byte 942 value = float(self) 943 if value: 944 value = 1.0 / float(self) 945 # convert to ticks per byte 946 value = ticks.fromSeconds(value) 947 return float(value) 948 949 def ini_str(self): 950 return '%f' % self.getValue() 951 952# 953# "Constants"... handy aliases for various values. 954# 955 956# Special class for NULL pointers. Note the special check in 957# make_param_value() above that lets these be assigned where a 958# SimObject is required. 959# only one copy of a particular node 960class NullSimObject(object): 961 __metaclass__ = Singleton 962 963 def __call__(cls): 964 return cls 965 966 def _instantiate(self, parent = None, path = ''): 967 pass 968 969 def ini_str(self): 970 return 'Null' 971 972 def unproxy(self, base): 973 return self 974 975 def set_path(self, parent, name): 976 pass 977 978 def __str__(self): 979 return 'Null' 980 981 def getValue(self): 982 return None 983 984# The only instance you'll ever need... 985NULL = NullSimObject() 986 987def isNullPointer(value): 988 return isinstance(value, NullSimObject) 989 990# Some memory range specifications use this as a default upper bound. 991MaxAddr = Addr.max 992MaxTick = Tick.max 993AllMemory = AddrRange(0, MaxAddr) 994 995 996##################################################################### 997# 998# Port objects 999# 1000# Ports are used to interconnect objects in the memory system. 1001# 1002##################################################################### 1003 1004# Port reference: encapsulates a reference to a particular port on a 1005# particular SimObject. 1006class PortRef(object): 1007 def __init__(self, simobj, name): 1008 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1009 self.simobj = simobj 1010 self.name = name 1011 self.peer = None # not associated with another port yet 1012 self.ccConnected = False # C++ port connection done? 1013 self.index = -1 # always -1 for non-vector ports 1014 1015 def __str__(self): 1016 return '%s.%s' % (self.simobj, self.name) 1017 1018 # for config.ini, print peer's name (not ours) 1019 def ini_str(self): 1020 return str(self.peer) 1021 1022 def __getattr__(self, attr): 1023 if attr == 'peerObj': 1024 # shorthand for proxies 1025 return self.peer.simobj 1026 raise AttributeError, "'%s' object has no attribute '%s'" % \ 1027 (self.__class__.__name__, attr) 1028 1029 # Full connection is symmetric (both ways). Called via 1030 # SimObject.__setattr__ as a result of a port assignment, e.g., 1031 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 1032 # e.g., "obj1.portA[3] = obj2.portB". 1033 def connect(self, other): 1034 if isinstance(other, VectorPortRef): 1035 # reference to plain VectorPort is implicit append 1036 other = other._get_next() 1037 if self.peer and not proxy.isproxy(self.peer): 1038 print "warning: overwriting port", self, \ 1039 "value", self.peer, "with", other 1040 self.peer.peer = None 1041 self.peer = other 1042 if proxy.isproxy(other): 1043 other.set_param_desc(PortParamDesc()) 1044 elif isinstance(other, PortRef): 1045 if other.peer is not self: 1046 other.connect(self) 1047 else: 1048 raise TypeError, \ 1049 "assigning non-port reference '%s' to port '%s'" \ 1050 % (other, self) 1051 1052 def clone(self, simobj, memo): 1053 if memo.has_key(self): 1054 return memo[self] 1055 newRef = copy.copy(self) 1056 memo[self] = newRef 1057 newRef.simobj = simobj 1058 assert(isSimObject(newRef.simobj)) 1059 if self.peer and not proxy.isproxy(self.peer): 1060 peerObj = self.peer.simobj(_memo=memo) 1061 newRef.peer = self.peer.clone(peerObj, memo) 1062 assert(not isinstance(newRef.peer, VectorPortRef)) 1063 return newRef 1064 1065 def unproxy(self, simobj): 1066 assert(simobj is self.simobj) 1067 if proxy.isproxy(self.peer): 1068 try: 1069 realPeer = self.peer.unproxy(self.simobj) 1070 except: 1071 print "Error in unproxying port '%s' of %s" % \ 1072 (self.name, self.simobj.path()) 1073 raise 1074 self.connect(realPeer) 1075 1076 # Call C++ to create corresponding port connection between C++ objects 1077 def ccConnect(self): 1078 from m5.objects.params import connectPorts 1079 1080 if self.ccConnected: # already done this 1081 return 1082 peer = self.peer 1083 if not self.peer: # nothing to connect to 1084 return 1085 try: 1086 connectPorts(self.simobj.getCCObject(), self.name, self.index, 1087 peer.simobj.getCCObject(), peer.name, peer.index) 1088 except: 1089 print "Error connecting port %s.%s to %s.%s" % \ 1090 (self.simobj.path(), self.name, 1091 peer.simobj.path(), peer.name) 1092 raise 1093 self.ccConnected = True 1094 peer.ccConnected = True 1095 1096# A reference to an individual element of a VectorPort... much like a 1097# PortRef, but has an index. 1098class VectorPortElementRef(PortRef): 1099 def __init__(self, simobj, name, index): 1100 PortRef.__init__(self, simobj, name) 1101 self.index = index 1102 1103 def __str__(self): 1104 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1105 1106# A reference to a complete vector-valued port (not just a single element). 1107# Can be indexed to retrieve individual VectorPortElementRef instances. 1108class VectorPortRef(object): 1109 def __init__(self, simobj, name): 1110 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1111 self.simobj = simobj 1112 self.name = name 1113 self.elements = [] 1114 1115 def __str__(self): 1116 return '%s.%s[:]' % (self.simobj, self.name) 1117 1118 # for config.ini, print peer's name (not ours) 1119 def ini_str(self): 1120 return ' '.join([el.ini_str() for el in self.elements]) 1121 1122 def __getitem__(self, key): 1123 if not isinstance(key, int): 1124 raise TypeError, "VectorPort index must be integer" 1125 if key >= len(self.elements): 1126 # need to extend list 1127 ext = [VectorPortElementRef(self.simobj, self.name, i) 1128 for i in range(len(self.elements), key+1)] 1129 self.elements.extend(ext) 1130 return self.elements[key] 1131 1132 def _get_next(self): 1133 return self[len(self.elements)] 1134 1135 def __setitem__(self, key, value): 1136 if not isinstance(key, int): 1137 raise TypeError, "VectorPort index must be integer" 1138 self[key].connect(value) 1139 1140 def connect(self, other): 1141 if isinstance(other, (list, tuple)): 1142 # Assign list of port refs to vector port. 1143 # For now, append them... not sure if that's the right semantics 1144 # or if it should replace the current vector. 1145 for ref in other: 1146 self._get_next().connect(ref) 1147 else: 1148 # scalar assignment to plain VectorPort is implicit append 1149 self._get_next().connect(other) 1150 1151 def clone(self, simobj, memo): 1152 if memo.has_key(self): 1153 return memo[self] 1154 newRef = copy.copy(self) 1155 memo[self] = newRef 1156 newRef.simobj = simobj 1157 assert(isSimObject(newRef.simobj)) 1158 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 1159 return newRef 1160 1161 def unproxy(self, simobj): 1162 [el.unproxy(simobj) for el in self.elements] 1163 1164 def ccConnect(self): 1165 [el.ccConnect() for el in self.elements] 1166 1167# Port description object. Like a ParamDesc object, this represents a 1168# logical port in the SimObject class, not a particular port on a 1169# SimObject instance. The latter are represented by PortRef objects. 1170class Port(object): 1171 # Port("description") or Port(default, "description") 1172 def __init__(self, *args): 1173 if len(args) == 1: 1174 self.desc = args[0] 1175 elif len(args) == 2: 1176 self.default = args[0] 1177 self.desc = args[1] 1178 else: 1179 raise TypeError, 'wrong number of arguments' 1180 # self.name is set by SimObject class on assignment 1181 # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 1182 1183 # Generate a PortRef for this port on the given SimObject with the 1184 # given name 1185 def makeRef(self, simobj): 1186 return PortRef(simobj, self.name) 1187 1188 # Connect an instance of this port (on the given SimObject with 1189 # the given name) with the port described by the supplied PortRef 1190 def connect(self, simobj, ref): 1191 self.makeRef(simobj).connect(ref) 1192 1193# VectorPort description object. Like Port, but represents a vector 1194# of connections (e.g., as on a Bus). 1195class VectorPort(Port): 1196 def __init__(self, *args): 1197 Port.__init__(self, *args) 1198 self.isVec = True 1199 1200 def makeRef(self, simobj): 1201 return VectorPortRef(simobj, self.name) 1202 1203# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 1204# proxy objects (via set_param_desc()) so that proxy error messages 1205# make sense. 1206class PortParamDesc(object): 1207 __metaclass__ = Singleton 1208 1209 ptype_str = 'Port' 1210 ptype = Port 1211 1212baseEnums = allEnums.copy() 1213baseParams = allParams.copy() 1214 1215def clear(): 1216 global allEnums, allParams 1217 1218 allEnums = baseEnums.copy() 1219 allParams = baseParams.copy() 1220 1221__all__ = ['Param', 'VectorParam', 1222 'Enum', 'Bool', 'String', 'Float', 1223 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1224 'Int32', 'UInt32', 'Int64', 'UInt64', 1225 'Counter', 'Addr', 'Tick', 'Percent', 1226 'TcpPort', 'UdpPort', 'EthernetAddr', 1227 'MemorySize', 'MemorySize32', 1228 'Latency', 'Frequency', 'Clock', 1229 'NetworkBandwidth', 'MemoryBandwidth', 1230 'Range', 'AddrRange', 'TickRange', 1231 'MaxAddr', 'MaxTick', 'AllMemory', 1232 'Time', 1233 'NextEthernetAddr', 'NULL', 1234 'Port', 'VectorPort'] 1235 1236import SimObject
|