params: Fix floating point parameters
[gem5.git] / src / python / m5 / params.py
1 # Copyright (c) 2004-2006 The Regents of The University of Michigan
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met: redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer;
8 # redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution;
11 # neither the name of the copyright holders nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #
27 # Authors: Steve Reinhardt
28 # Nathan Binkert
29
30 #####################################################################
31 #
32 # Parameter description classes
33 #
34 # The _params dictionary in each class maps parameter names to either
35 # a Param or a VectorParam object. These objects contain the
36 # parameter description string, the parameter type, and the default
37 # value (if any). The convert() method on these objects is used to
38 # force whatever value is assigned to the parameter to the appropriate
39 # type.
40 #
41 # Note that the default values are loaded into the class's attribute
42 # space when the parameter dictionary is initialized (in
43 # MetaSimObject._new_param()); after that point they aren't used.
44 #
45 #####################################################################
46
47 import copy
48 import datetime
49 import re
50 import sys
51 import time
52
53 import convert
54 import proxy
55 import ticks
56 from util import *
57
58 import SimObject
59
60 def isSimObject(*args, **kwargs):
61 return SimObject.isSimObject(*args, **kwargs)
62
63 def isSimObjectSequence(*args, **kwargs):
64 return SimObject.isSimObjectSequence(*args, **kwargs)
65
66 def isSimObjectClass(*args, **kwargs):
67 return SimObject.isSimObjectClass(*args, **kwargs)
68
69 allParams = {}
70
71 class MetaParamValue(type):
72 def __new__(mcls, name, bases, dct):
73 cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
74 assert name not in allParams
75 allParams[name] = cls
76 return cls
77
78
79 # Dummy base class to identify types that are legitimate for SimObject
80 # parameters.
81 class ParamValue(object):
82 __metaclass__ = MetaParamValue
83
84 cxx_predecls = []
85 swig_predecls = []
86
87 # default for printing to .ini file is regular string conversion.
88 # will be overridden in some cases
89 def ini_str(self):
90 return str(self)
91
92 # allows us to blithely call unproxy() on things without checking
93 # if they're really proxies or not
94 def unproxy(self, base):
95 return self
96
97 # Regular parameter description.
98 class ParamDesc(object):
99 def __init__(self, ptype_str, ptype, *args, **kwargs):
100 self.ptype_str = ptype_str
101 # remember ptype only if it is provided
102 if ptype != None:
103 self.ptype = ptype
104
105 if args:
106 if len(args) == 1:
107 self.desc = args[0]
108 elif len(args) == 2:
109 self.default = args[0]
110 self.desc = args[1]
111 else:
112 raise TypeError, 'too many arguments'
113
114 if kwargs.has_key('desc'):
115 assert(not hasattr(self, 'desc'))
116 self.desc = kwargs['desc']
117 del kwargs['desc']
118
119 if kwargs.has_key('default'):
120 assert(not hasattr(self, 'default'))
121 self.default = kwargs['default']
122 del kwargs['default']
123
124 if kwargs:
125 raise TypeError, 'extra unknown kwargs %s' % kwargs
126
127 if not hasattr(self, 'desc'):
128 raise TypeError, 'desc attribute missing'
129
130 def __getattr__(self, attr):
131 if attr == 'ptype':
132 ptype = SimObject.allClasses[self.ptype_str]
133 assert issubclass(ptype, SimObject.SimObject)
134 self.ptype = ptype
135 return ptype
136
137 raise AttributeError, "'%s' object has no attribute '%s'" % \
138 (type(self).__name__, attr)
139
140 def convert(self, value):
141 if isinstance(value, proxy.BaseProxy):
142 value.set_param_desc(self)
143 return value
144 if not hasattr(self, 'ptype') and isNullPointer(value):
145 # deferred evaluation of SimObject; continue to defer if
146 # we're just assigning a null pointer
147 return value
148 if isinstance(value, self.ptype):
149 return value
150 if isNullPointer(value) and isSimObjectClass(self.ptype):
151 return value
152 return self.ptype(value)
153
154 def cxx_predecls(self):
155 return self.ptype.cxx_predecls
156
157 def swig_predecls(self):
158 return self.ptype.swig_predecls
159
160 def cxx_decl(self):
161 return '%s %s;' % (self.ptype.cxx_type, self.name)
162
163 # Vector-valued parameter description. Just like ParamDesc, except
164 # that the value is a vector (list) of the specified type instead of a
165 # single value.
166
167 class VectorParamValue(list):
168 __metaclass__ = MetaParamValue
169 def ini_str(self):
170 return ' '.join([v.ini_str() for v in self])
171
172 def getValue(self):
173 return [ v.getValue() for v in self ]
174
175 def unproxy(self, base):
176 return [v.unproxy(base) for v in self]
177
178 class SimObjVector(VectorParamValue):
179 def print_ini(self, ini_file):
180 for v in self:
181 v.print_ini(ini_file)
182
183 class VectorParamDesc(ParamDesc):
184 # Convert assigned value to appropriate type. If the RHS is not a
185 # list or tuple, it generates a single-element list.
186 def convert(self, value):
187 if isinstance(value, (list, tuple)):
188 # list: coerce each element into new list
189 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
190 else:
191 # singleton: coerce to a single-element list
192 tmp_list = [ ParamDesc.convert(self, value) ]
193
194 if isSimObjectSequence(tmp_list):
195 return SimObjVector(tmp_list)
196 else:
197 return VectorParamValue(tmp_list)
198
199 def swig_predecls(self):
200 return ['%%include "%s_vptype.i"' % self.ptype_str]
201
202 def swig_decl(self):
203 cxx_type = re.sub('std::', '', self.ptype.cxx_type)
204 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
205 (self.ptype_str, cxx_type)
206 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
207
208 def cxx_predecls(self):
209 return ['#include <vector>'] + self.ptype.cxx_predecls
210
211 def cxx_decl(self):
212 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
213
214 class ParamFactory(object):
215 def __init__(self, param_desc_class, ptype_str = None):
216 self.param_desc_class = param_desc_class
217 self.ptype_str = ptype_str
218
219 def __getattr__(self, attr):
220 if self.ptype_str:
221 attr = self.ptype_str + '.' + attr
222 return ParamFactory(self.param_desc_class, attr)
223
224 # E.g., Param.Int(5, "number of widgets")
225 def __call__(self, *args, **kwargs):
226 ptype = None
227 try:
228 ptype = allParams[self.ptype_str]
229 except KeyError:
230 # if name isn't defined yet, assume it's a SimObject, and
231 # try to resolve it later
232 pass
233 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
234
235 Param = ParamFactory(ParamDesc)
236 VectorParam = ParamFactory(VectorParamDesc)
237
238 #####################################################################
239 #
240 # Parameter Types
241 #
242 # Though native Python types could be used to specify parameter types
243 # (the 'ptype' field of the Param and VectorParam classes), it's more
244 # flexible to define our own set of types. This gives us more control
245 # over how Python expressions are converted to values (via the
246 # __init__() constructor) and how these values are printed out (via
247 # the __str__() conversion method).
248 #
249 #####################################################################
250
251 # String-valued parameter. Just mixin the ParamValue class with the
252 # built-in str class.
253 class String(ParamValue,str):
254 cxx_type = 'std::string'
255 cxx_predecls = ['#include <string>']
256 swig_predecls = ['%include "std_string.i"\n' +
257 '%apply const std::string& {std::string *};']
258 swig_predecls = ['%include "std_string.i"' ]
259
260 def getValue(self):
261 return self
262
263 # superclass for "numeric" parameter values, to emulate math
264 # operations in a type-safe way. e.g., a Latency times an int returns
265 # a new Latency object.
266 class NumericParamValue(ParamValue):
267 def __str__(self):
268 return str(self.value)
269
270 def __float__(self):
271 return float(self.value)
272
273 def __long__(self):
274 return long(self.value)
275
276 def __int__(self):
277 return int(self.value)
278
279 # hook for bounds checking
280 def _check(self):
281 return
282
283 def __mul__(self, other):
284 newobj = self.__class__(self)
285 newobj.value *= other
286 newobj._check()
287 return newobj
288
289 __rmul__ = __mul__
290
291 def __div__(self, other):
292 newobj = self.__class__(self)
293 newobj.value /= other
294 newobj._check()
295 return newobj
296
297 def __sub__(self, other):
298 newobj = self.__class__(self)
299 newobj.value -= other
300 newobj._check()
301 return newobj
302
303 # Metaclass for bounds-checked integer parameters. See CheckedInt.
304 class CheckedIntType(MetaParamValue):
305 def __init__(cls, name, bases, dict):
306 super(CheckedIntType, cls).__init__(name, bases, dict)
307
308 # CheckedInt is an abstract base class, so we actually don't
309 # want to do any processing on it... the rest of this code is
310 # just for classes that derive from CheckedInt.
311 if name == 'CheckedInt':
312 return
313
314 if not cls.cxx_predecls:
315 # most derived types require this, so we just do it here once
316 cls.cxx_predecls = ['#include "sim/host.hh"']
317
318 if not cls.swig_predecls:
319 # most derived types require this, so we just do it here once
320 cls.swig_predecls = ['%import "stdint.i"\n' +
321 '%import "sim/host.hh"']
322
323 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
324 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
325 panic("CheckedInt subclass %s must define either\n" \
326 " 'min' and 'max' or 'size' and 'unsigned'\n" \
327 % name);
328 if cls.unsigned:
329 cls.min = 0
330 cls.max = 2 ** cls.size - 1
331 else:
332 cls.min = -(2 ** (cls.size - 1))
333 cls.max = (2 ** (cls.size - 1)) - 1
334
335 # Abstract superclass for bounds-checked integer parameters. This
336 # class is subclassed to generate parameter classes with specific
337 # bounds. Initialization of the min and max bounds is done in the
338 # metaclass CheckedIntType.__init__.
339 class CheckedInt(NumericParamValue):
340 __metaclass__ = CheckedIntType
341
342 def _check(self):
343 if not self.min <= self.value <= self.max:
344 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
345 (self.min, self.value, self.max)
346
347 def __init__(self, value):
348 if isinstance(value, str):
349 self.value = convert.toInteger(value)
350 elif isinstance(value, (int, long, float, NumericParamValue)):
351 self.value = long(value)
352 else:
353 raise TypeError, "Can't convert object of type %s to CheckedInt" \
354 % type(value).__name__
355 self._check()
356
357 def getValue(self):
358 return long(self.value)
359
360 class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
361 class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
362
363 class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
364 class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
365 class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
366 class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
367 class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
368 class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
369 class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
370 class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
371
372 class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
373 class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
374 class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
375 class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
376
377 class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
378
379 class Float(ParamValue, float):
380 cxx_type = 'double'
381
382 def __init__(self, value):
383 if isinstance(value, (int, long, float, NumericParamValue, Float)):
384 self.value = float(value)
385 else:
386 raise TypeError, "Can't convert object of type %s to Float" \
387 % type(value).__name__
388
389 def getValue(self):
390 return float(self.value)
391
392 class MemorySize(CheckedInt):
393 cxx_type = 'uint64_t'
394 size = 64
395 unsigned = True
396 def __init__(self, value):
397 if isinstance(value, MemorySize):
398 self.value = value.value
399 else:
400 self.value = convert.toMemorySize(value)
401 self._check()
402
403 class MemorySize32(CheckedInt):
404 cxx_type = 'uint32_t'
405 size = 32
406 unsigned = True
407 def __init__(self, value):
408 if isinstance(value, MemorySize):
409 self.value = value.value
410 else:
411 self.value = convert.toMemorySize(value)
412 self._check()
413
414 class Addr(CheckedInt):
415 cxx_type = 'Addr'
416 size = 64
417 unsigned = True
418 def __init__(self, value):
419 if isinstance(value, Addr):
420 self.value = value.value
421 else:
422 try:
423 self.value = convert.toMemorySize(value)
424 except TypeError:
425 self.value = long(value)
426 self._check()
427 def __add__(self, other):
428 if isinstance(other, Addr):
429 return self.value + other.value
430 else:
431 return self.value + other
432
433
434 class MetaRange(MetaParamValue):
435 def __init__(cls, name, bases, dict):
436 super(MetaRange, cls).__init__(name, bases, dict)
437 if name == 'Range':
438 return
439 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
440 cls.cxx_predecls = \
441 ['#include "base/range.hh"'] + cls.type.cxx_predecls
442
443 class Range(ParamValue):
444 __metaclass__ = MetaRange
445 type = Int # default; can be overridden in subclasses
446 def __init__(self, *args, **kwargs):
447 def handle_kwargs(self, kwargs):
448 if 'end' in kwargs:
449 self.second = self.type(kwargs.pop('end'))
450 elif 'size' in kwargs:
451 self.second = self.first + self.type(kwargs.pop('size')) - 1
452 else:
453 raise TypeError, "Either end or size must be specified"
454
455 if len(args) == 0:
456 self.first = self.type(kwargs.pop('start'))
457 handle_kwargs(self, kwargs)
458
459 elif len(args) == 1:
460 if kwargs:
461 self.first = self.type(args[0])
462 handle_kwargs(self, kwargs)
463 elif isinstance(args[0], Range):
464 self.first = self.type(args[0].first)
465 self.second = self.type(args[0].second)
466 elif isinstance(args[0], (list, tuple)):
467 self.first = self.type(args[0][0])
468 self.second = self.type(args[0][1])
469 else:
470 self.first = self.type(0)
471 self.second = self.type(args[0]) - 1
472
473 elif len(args) == 2:
474 self.first = self.type(args[0])
475 self.second = self.type(args[1])
476 else:
477 raise TypeError, "Too many arguments specified"
478
479 if kwargs:
480 raise TypeError, "too many keywords: %s" % kwargs.keys()
481
482 def __str__(self):
483 return '%s:%s' % (self.first, self.second)
484
485 class AddrRange(Range):
486 type = Addr
487 swig_predecls = ['%include "python/swig/range.i"']
488
489 def getValue(self):
490 from m5.objects.params import AddrRange
491
492 value = AddrRange()
493 value.start = long(self.first)
494 value.end = long(self.second)
495 return value
496
497 class TickRange(Range):
498 type = Tick
499 swig_predecls = ['%include "python/swig/range.i"']
500
501 def getValue(self):
502 from m5.objects.params import TickRange
503
504 value = TickRange()
505 value.start = long(self.first)
506 value.end = long(self.second)
507 return value
508
509 # Boolean parameter type. Python doesn't let you subclass bool, since
510 # it doesn't want to let you create multiple instances of True and
511 # False. Thus this is a little more complicated than String.
512 class Bool(ParamValue):
513 cxx_type = 'bool'
514 def __init__(self, value):
515 try:
516 self.value = convert.toBool(value)
517 except TypeError:
518 self.value = bool(value)
519
520 def getValue(self):
521 return bool(self.value)
522
523 def __str__(self):
524 return str(self.value)
525
526 def ini_str(self):
527 if self.value:
528 return 'true'
529 return 'false'
530
531 def IncEthernetAddr(addr, val = 1):
532 bytes = map(lambda x: int(x, 16), addr.split(':'))
533 bytes[5] += val
534 for i in (5, 4, 3, 2, 1):
535 val,rem = divmod(bytes[i], 256)
536 bytes[i] = rem
537 if val == 0:
538 break
539 bytes[i - 1] += val
540 assert(bytes[0] <= 255)
541 return ':'.join(map(lambda x: '%02x' % x, bytes))
542
543 _NextEthernetAddr = "00:90:00:00:00:01"
544 def NextEthernetAddr():
545 global _NextEthernetAddr
546
547 value = _NextEthernetAddr
548 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
549 return value
550
551 class EthernetAddr(ParamValue):
552 cxx_type = 'Net::EthAddr'
553 cxx_predecls = ['#include "base/inet.hh"']
554 swig_predecls = ['%include "python/swig/inet.i"']
555 def __init__(self, value):
556 if value == NextEthernetAddr:
557 self.value = value
558 return
559
560 if not isinstance(value, str):
561 raise TypeError, "expected an ethernet address and didn't get one"
562
563 bytes = value.split(':')
564 if len(bytes) != 6:
565 raise TypeError, 'invalid ethernet address %s' % value
566
567 for byte in bytes:
568 if not 0 <= int(byte) <= 256:
569 raise TypeError, 'invalid ethernet address %s' % value
570
571 self.value = value
572
573 def unproxy(self, base):
574 if self.value == NextEthernetAddr:
575 return EthernetAddr(self.value())
576 return self
577
578 def getValue(self):
579 from m5.objects.params import EthAddr
580 return EthAddr(self.value)
581
582 def ini_str(self):
583 return self.value
584
585 time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
586 "%a %b %d %H:%M:%S %Z %Y",
587 "%Y/%m/%d %H:%M:%S",
588 "%Y/%m/%d %H:%M",
589 "%Y/%m/%d",
590 "%m/%d/%Y %H:%M:%S",
591 "%m/%d/%Y %H:%M",
592 "%m/%d/%Y",
593 "%m/%d/%y %H:%M:%S",
594 "%m/%d/%y %H:%M",
595 "%m/%d/%y"]
596
597
598 def parse_time(value):
599 from time import gmtime, strptime, struct_time, time
600 from datetime import datetime, date
601
602 if isinstance(value, struct_time):
603 return value
604
605 if isinstance(value, (int, long)):
606 return gmtime(value)
607
608 if isinstance(value, (datetime, date)):
609 return value.timetuple()
610
611 if isinstance(value, str):
612 if value in ('Now', 'Today'):
613 return time.gmtime(time.time())
614
615 for format in time_formats:
616 try:
617 return strptime(value, format)
618 except ValueError:
619 pass
620
621 raise ValueError, "Could not parse '%s' as a time" % value
622
623 class Time(ParamValue):
624 cxx_type = 'tm'
625 cxx_predecls = [ '#include <time.h>' ]
626 swig_predecls = [ '%include "python/swig/time.i"' ]
627 def __init__(self, value):
628 self.value = parse_time(value)
629
630 def getValue(self):
631 from m5.objects.params import tm
632
633 c_time = tm()
634 py_time = self.value
635
636 # UNIX is years since 1900
637 c_time.tm_year = py_time.tm_year - 1900;
638
639 # Python starts at 1, UNIX starts at 0
640 c_time.tm_mon = py_time.tm_mon - 1;
641 c_time.tm_mday = py_time.tm_mday;
642 c_time.tm_hour = py_time.tm_hour;
643 c_time.tm_min = py_time.tm_min;
644 c_time.tm_sec = py_time.tm_sec;
645
646 # Python has 0 as Monday, UNIX is 0 as sunday
647 c_time.tm_wday = py_time.tm_wday + 1
648 if c_time.tm_wday > 6:
649 c_time.tm_wday -= 7;
650
651 # Python starts at 1, Unix starts at 0
652 c_time.tm_yday = py_time.tm_yday - 1;
653
654 return c_time
655
656 def __str__(self):
657 return time.asctime(self.value)
658
659 def ini_str(self):
660 return str(self)
661
662 # Enumerated types are a little more complex. The user specifies the
663 # type as Enum(foo) where foo is either a list or dictionary of
664 # alternatives (typically strings, but not necessarily so). (In the
665 # long run, the integer value of the parameter will be the list index
666 # or the corresponding dictionary value. For now, since we only check
667 # that the alternative is valid and then spit it into a .ini file,
668 # there's not much point in using the dictionary.)
669
670 # What Enum() must do is generate a new type encapsulating the
671 # provided list/dictionary so that specific values of the parameter
672 # can be instances of that type. We define two hidden internal
673 # classes (_ListEnum and _DictEnum) to serve as base classes, then
674 # derive the new type from the appropriate base class on the fly.
675
676 allEnums = {}
677 # Metaclass for Enum types
678 class MetaEnum(MetaParamValue):
679 def __new__(mcls, name, bases, dict):
680 assert name not in allEnums
681
682 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
683 allEnums[name] = cls
684 return cls
685
686 def __init__(cls, name, bases, init_dict):
687 if init_dict.has_key('map'):
688 if not isinstance(cls.map, dict):
689 raise TypeError, "Enum-derived class attribute 'map' " \
690 "must be of type dict"
691 # build list of value strings from map
692 cls.vals = cls.map.keys()
693 cls.vals.sort()
694 elif init_dict.has_key('vals'):
695 if not isinstance(cls.vals, list):
696 raise TypeError, "Enum-derived class attribute 'vals' " \
697 "must be of type list"
698 # build string->value map from vals sequence
699 cls.map = {}
700 for idx,val in enumerate(cls.vals):
701 cls.map[val] = idx
702 else:
703 raise TypeError, "Enum-derived class must define "\
704 "attribute 'map' or 'vals'"
705
706 cls.cxx_type = 'Enums::%s' % name
707
708 super(MetaEnum, cls).__init__(name, bases, init_dict)
709
710 def __str__(cls):
711 return cls.__name__
712
713 # Generate C++ class declaration for this enum type.
714 # Note that we wrap the enum in a class/struct to act as a namespace,
715 # so that the enum strings can be brief w/o worrying about collisions.
716 def cxx_decl(cls):
717 code = "#ifndef __ENUM__%s\n" % cls
718 code += '#define __ENUM__%s\n' % cls
719 code += '\n'
720 code += 'namespace Enums {\n'
721 code += ' enum %s {\n' % cls
722 for val in cls.vals:
723 code += ' %s = %d,\n' % (val, cls.map[val])
724 code += ' Num_%s = %d,\n' % (cls, len(cls.vals))
725 code += ' };\n'
726 code += ' extern const char *%sStrings[Num_%s];\n' % (cls, cls)
727 code += '}\n'
728 code += '\n'
729 code += '#endif\n'
730 return code
731
732 def cxx_def(cls):
733 code = '#include "enums/%s.hh"\n' % cls
734 code += 'namespace Enums {\n'
735 code += ' const char *%sStrings[Num_%s] =\n' % (cls, cls)
736 code += ' {\n'
737 for val in cls.vals:
738 code += ' "%s",\n' % val
739 code += ' };\n'
740 code += '}\n'
741 return code
742
743 # Base class for enum types.
744 class Enum(ParamValue):
745 __metaclass__ = MetaEnum
746 vals = []
747
748 def __init__(self, value):
749 if value not in self.map:
750 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
751 % (value, self.vals)
752 self.value = value
753
754 def getValue(self):
755 return int(self.map[self.value])
756
757 def __str__(self):
758 return self.value
759
760 # how big does a rounding error need to be before we warn about it?
761 frequency_tolerance = 0.001 # 0.1%
762
763 class TickParamValue(NumericParamValue):
764 cxx_type = 'Tick'
765 cxx_predecls = ['#include "sim/host.hh"']
766 swig_predecls = ['%import "stdint.i"\n' +
767 '%import "sim/host.hh"']
768
769 def getValue(self):
770 return long(self.value)
771
772 class Latency(TickParamValue):
773 def __init__(self, value):
774 if isinstance(value, (Latency, Clock)):
775 self.ticks = value.ticks
776 self.value = value.value
777 elif isinstance(value, Frequency):
778 self.ticks = value.ticks
779 self.value = 1.0 / value.value
780 elif value.endswith('t'):
781 self.ticks = True
782 self.value = int(value[:-1])
783 else:
784 self.ticks = False
785 self.value = convert.toLatency(value)
786
787 def __getattr__(self, attr):
788 if attr in ('latency', 'period'):
789 return self
790 if attr == 'frequency':
791 return Frequency(self)
792 raise AttributeError, "Latency object has no attribute '%s'" % attr
793
794 def getValue(self):
795 if self.ticks or self.value == 0:
796 value = self.value
797 else:
798 value = ticks.fromSeconds(self.value)
799 return long(value)
800
801 # convert latency to ticks
802 def ini_str(self):
803 return '%d' % self.getValue()
804
805 class Frequency(TickParamValue):
806 def __init__(self, value):
807 if isinstance(value, (Latency, Clock)):
808 if value.value == 0:
809 self.value = 0
810 else:
811 self.value = 1.0 / value.value
812 self.ticks = value.ticks
813 elif isinstance(value, Frequency):
814 self.value = value.value
815 self.ticks = value.ticks
816 else:
817 self.ticks = False
818 self.value = convert.toFrequency(value)
819
820 def __getattr__(self, attr):
821 if attr == 'frequency':
822 return self
823 if attr in ('latency', 'period'):
824 return Latency(self)
825 raise AttributeError, "Frequency object has no attribute '%s'" % attr
826
827 # convert latency to ticks
828 def getValue(self):
829 if self.ticks or self.value == 0:
830 value = self.value
831 else:
832 value = ticks.fromSeconds(1.0 / self.value)
833 return long(value)
834
835 def ini_str(self):
836 return '%d' % self.getValue()
837
838 # A generic frequency and/or Latency value. Value is stored as a latency,
839 # but to avoid ambiguity this object does not support numeric ops (* or /).
840 # An explicit conversion to a Latency or Frequency must be made first.
841 class Clock(ParamValue):
842 cxx_type = 'Tick'
843 cxx_predecls = ['#include "sim/host.hh"']
844 swig_predecls = ['%import "stdint.i"\n' +
845 '%import "sim/host.hh"']
846 def __init__(self, value):
847 if isinstance(value, (Latency, Clock)):
848 self.ticks = value.ticks
849 self.value = value.value
850 elif isinstance(value, Frequency):
851 self.ticks = value.ticks
852 self.value = 1.0 / value.value
853 elif value.endswith('t'):
854 self.ticks = True
855 self.value = int(value[:-1])
856 else:
857 self.ticks = False
858 self.value = convert.anyToLatency(value)
859
860 def __getattr__(self, attr):
861 if attr == 'frequency':
862 return Frequency(self)
863 if attr in ('latency', 'period'):
864 return Latency(self)
865 raise AttributeError, "Frequency object has no attribute '%s'" % attr
866
867 def getValue(self):
868 return self.period.getValue()
869
870 def ini_str(self):
871 return self.period.ini_str()
872
873 class NetworkBandwidth(float,ParamValue):
874 cxx_type = 'float'
875 def __new__(cls, value):
876 # convert to bits per second
877 val = convert.toNetworkBandwidth(value)
878 return super(cls, NetworkBandwidth).__new__(cls, val)
879
880 def __str__(self):
881 return str(self.val)
882
883 def getValue(self):
884 # convert to seconds per byte
885 value = 8.0 / float(self)
886 # convert to ticks per byte
887 value = ticks.fromSeconds(value)
888 return float(value)
889
890 def ini_str(self):
891 return '%f' % self.getValue()
892
893 class MemoryBandwidth(float,ParamValue):
894 cxx_type = 'float'
895 def __new__(self, value):
896 # we want the number of ticks per byte of data
897 val = convert.toMemoryBandwidth(value)
898 return super(cls, MemoryBandwidth).__new__(cls, val)
899
900 def __str__(self):
901 return str(self.val)
902
903 def getValue(self):
904 # convert to seconds per byte
905 value = float(self)
906 if value:
907 value = 1.0 / float(self)
908 # convert to ticks per byte
909 value = ticks.fromSeconds(value)
910 return float(value)
911
912 def ini_str(self):
913 return '%f' % self.getValue()
914
915 #
916 # "Constants"... handy aliases for various values.
917 #
918
919 # Special class for NULL pointers. Note the special check in
920 # make_param_value() above that lets these be assigned where a
921 # SimObject is required.
922 # only one copy of a particular node
923 class NullSimObject(object):
924 __metaclass__ = Singleton
925
926 def __call__(cls):
927 return cls
928
929 def _instantiate(self, parent = None, path = ''):
930 pass
931
932 def ini_str(self):
933 return 'Null'
934
935 def unproxy(self, base):
936 return self
937
938 def set_path(self, parent, name):
939 pass
940
941 def __str__(self):
942 return 'Null'
943
944 def getValue(self):
945 return None
946
947 # The only instance you'll ever need...
948 NULL = NullSimObject()
949
950 def isNullPointer(value):
951 return isinstance(value, NullSimObject)
952
953 # Some memory range specifications use this as a default upper bound.
954 MaxAddr = Addr.max
955 MaxTick = Tick.max
956 AllMemory = AddrRange(0, MaxAddr)
957
958
959 #####################################################################
960 #
961 # Port objects
962 #
963 # Ports are used to interconnect objects in the memory system.
964 #
965 #####################################################################
966
967 # Port reference: encapsulates a reference to a particular port on a
968 # particular SimObject.
969 class PortRef(object):
970 def __init__(self, simobj, name):
971 assert(isSimObject(simobj) or isSimObjectClass(simobj))
972 self.simobj = simobj
973 self.name = name
974 self.peer = None # not associated with another port yet
975 self.ccConnected = False # C++ port connection done?
976 self.index = -1 # always -1 for non-vector ports
977
978 def __str__(self):
979 return '%s.%s' % (self.simobj, self.name)
980
981 # for config.ini, print peer's name (not ours)
982 def ini_str(self):
983 return str(self.peer)
984
985 def __getattr__(self, attr):
986 if attr == 'peerObj':
987 # shorthand for proxies
988 return self.peer.simobj
989 raise AttributeError, "'%s' object has no attribute '%s'" % \
990 (self.__class__.__name__, attr)
991
992 # Full connection is symmetric (both ways). Called via
993 # SimObject.__setattr__ as a result of a port assignment, e.g.,
994 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
995 # e.g., "obj1.portA[3] = obj2.portB".
996 def connect(self, other):
997 if isinstance(other, VectorPortRef):
998 # reference to plain VectorPort is implicit append
999 other = other._get_next()
1000 if self.peer and not proxy.isproxy(self.peer):
1001 print "warning: overwriting port", self, \
1002 "value", self.peer, "with", other
1003 self.peer = other
1004 if proxy.isproxy(other):
1005 other.set_param_desc(PortParamDesc())
1006 elif isinstance(other, PortRef):
1007 if other.peer is not self:
1008 other.connect(self)
1009 else:
1010 raise TypeError, \
1011 "assigning non-port reference '%s' to port '%s'" \
1012 % (other, self)
1013
1014 def clone(self, simobj, memo):
1015 if memo.has_key(self):
1016 return memo[self]
1017 newRef = copy.copy(self)
1018 memo[self] = newRef
1019 newRef.simobj = simobj
1020 assert(isSimObject(newRef.simobj))
1021 if self.peer and not proxy.isproxy(self.peer):
1022 peerObj = self.peer.simobj(_memo=memo)
1023 newRef.peer = self.peer.clone(peerObj, memo)
1024 assert(not isinstance(newRef.peer, VectorPortRef))
1025 return newRef
1026
1027 def unproxy(self, simobj):
1028 assert(simobj is self.simobj)
1029 if proxy.isproxy(self.peer):
1030 try:
1031 realPeer = self.peer.unproxy(self.simobj)
1032 except:
1033 print "Error in unproxying port '%s' of %s" % \
1034 (self.name, self.simobj.path())
1035 raise
1036 self.connect(realPeer)
1037
1038 # Call C++ to create corresponding port connection between C++ objects
1039 def ccConnect(self):
1040 from m5.objects.params import connectPorts
1041
1042 if self.ccConnected: # already done this
1043 return
1044 peer = self.peer
1045 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1046 peer.simobj.getCCObject(), peer.name, peer.index)
1047 self.ccConnected = True
1048 peer.ccConnected = True
1049
1050 # A reference to an individual element of a VectorPort... much like a
1051 # PortRef, but has an index.
1052 class VectorPortElementRef(PortRef):
1053 def __init__(self, simobj, name, index):
1054 PortRef.__init__(self, simobj, name)
1055 self.index = index
1056
1057 def __str__(self):
1058 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1059
1060 # A reference to a complete vector-valued port (not just a single element).
1061 # Can be indexed to retrieve individual VectorPortElementRef instances.
1062 class VectorPortRef(object):
1063 def __init__(self, simobj, name):
1064 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1065 self.simobj = simobj
1066 self.name = name
1067 self.elements = []
1068
1069 def __str__(self):
1070 return '%s.%s[:]' % (self.simobj, self.name)
1071
1072 # for config.ini, print peer's name (not ours)
1073 def ini_str(self):
1074 return ' '.join([el.ini_str() for el in self.elements])
1075
1076 def __getitem__(self, key):
1077 if not isinstance(key, int):
1078 raise TypeError, "VectorPort index must be integer"
1079 if key >= len(self.elements):
1080 # need to extend list
1081 ext = [VectorPortElementRef(self.simobj, self.name, i)
1082 for i in range(len(self.elements), key+1)]
1083 self.elements.extend(ext)
1084 return self.elements[key]
1085
1086 def _get_next(self):
1087 return self[len(self.elements)]
1088
1089 def __setitem__(self, key, value):
1090 if not isinstance(key, int):
1091 raise TypeError, "VectorPort index must be integer"
1092 self[key].connect(value)
1093
1094 def connect(self, other):
1095 if isinstance(other, (list, tuple)):
1096 # Assign list of port refs to vector port.
1097 # For now, append them... not sure if that's the right semantics
1098 # or if it should replace the current vector.
1099 for ref in other:
1100 self._get_next().connect(ref)
1101 else:
1102 # scalar assignment to plain VectorPort is implicit append
1103 self._get_next().connect(other)
1104
1105 def clone(self, simobj, memo):
1106 if memo.has_key(self):
1107 return memo[self]
1108 newRef = copy.copy(self)
1109 memo[self] = newRef
1110 newRef.simobj = simobj
1111 assert(isSimObject(newRef.simobj))
1112 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1113 return newRef
1114
1115 def unproxy(self, simobj):
1116 [el.unproxy(simobj) for el in self.elements]
1117
1118 def ccConnect(self):
1119 [el.ccConnect() for el in self.elements]
1120
1121 # Port description object. Like a ParamDesc object, this represents a
1122 # logical port in the SimObject class, not a particular port on a
1123 # SimObject instance. The latter are represented by PortRef objects.
1124 class Port(object):
1125 # Port("description") or Port(default, "description")
1126 def __init__(self, *args):
1127 if len(args) == 1:
1128 self.desc = args[0]
1129 elif len(args) == 2:
1130 self.default = args[0]
1131 self.desc = args[1]
1132 else:
1133 raise TypeError, 'wrong number of arguments'
1134 # self.name is set by SimObject class on assignment
1135 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1136
1137 # Generate a PortRef for this port on the given SimObject with the
1138 # given name
1139 def makeRef(self, simobj):
1140 return PortRef(simobj, self.name)
1141
1142 # Connect an instance of this port (on the given SimObject with
1143 # the given name) with the port described by the supplied PortRef
1144 def connect(self, simobj, ref):
1145 self.makeRef(simobj).connect(ref)
1146
1147 # VectorPort description object. Like Port, but represents a vector
1148 # of connections (e.g., as on a Bus).
1149 class VectorPort(Port):
1150 def __init__(self, *args):
1151 Port.__init__(self, *args)
1152 self.isVec = True
1153
1154 def makeRef(self, simobj):
1155 return VectorPortRef(simobj, self.name)
1156
1157 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1158 # proxy objects (via set_param_desc()) so that proxy error messages
1159 # make sense.
1160 class PortParamDesc(object):
1161 __metaclass__ = Singleton
1162
1163 ptype_str = 'Port'
1164 ptype = Port
1165
1166 __all__ = ['Param', 'VectorParam',
1167 'Enum', 'Bool', 'String', 'Float',
1168 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1169 'Int32', 'UInt32', 'Int64', 'UInt64',
1170 'Counter', 'Addr', 'Tick', 'Percent',
1171 'TcpPort', 'UdpPort', 'EthernetAddr',
1172 'MemorySize', 'MemorySize32',
1173 'Latency', 'Frequency', 'Clock',
1174 'NetworkBandwidth', 'MemoryBandwidth',
1175 'Range', 'AddrRange', 'TickRange',
1176 'MaxAddr', 'MaxTick', 'AllMemory',
1177 'Time',
1178 'NextEthernetAddr', 'NULL',
1179 'Port', 'VectorPort']