Python: Allow a range to be a python tuple/list.
[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 getValue(self):
383 return float(self.value)
384
385 class MemorySize(CheckedInt):
386 cxx_type = 'uint64_t'
387 size = 64
388 unsigned = True
389 def __init__(self, value):
390 if isinstance(value, MemorySize):
391 self.value = value.value
392 else:
393 self.value = convert.toMemorySize(value)
394 self._check()
395
396 class MemorySize32(CheckedInt):
397 cxx_type = 'uint32_t'
398 size = 32
399 unsigned = True
400 def __init__(self, value):
401 if isinstance(value, MemorySize):
402 self.value = value.value
403 else:
404 self.value = convert.toMemorySize(value)
405 self._check()
406
407 class Addr(CheckedInt):
408 cxx_type = 'Addr'
409 cxx_predecls = ['#include "arch/isa_traits.hh"']
410 size = 64
411 unsigned = True
412 def __init__(self, value):
413 if isinstance(value, Addr):
414 self.value = value.value
415 else:
416 try:
417 self.value = convert.toMemorySize(value)
418 except TypeError:
419 self.value = long(value)
420 self._check()
421 def __add__(self, other):
422 if isinstance(other, Addr):
423 return self.value + other.value
424 else:
425 return self.value + other
426
427
428 class MetaRange(MetaParamValue):
429 def __init__(cls, name, bases, dict):
430 super(MetaRange, cls).__init__(name, bases, dict)
431 if name == 'Range':
432 return
433 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
434 cls.cxx_predecls = \
435 ['#include "base/range.hh"'] + cls.type.cxx_predecls
436
437 class Range(ParamValue):
438 __metaclass__ = MetaRange
439 type = Int # default; can be overridden in subclasses
440 def __init__(self, *args, **kwargs):
441 def handle_kwargs(self, kwargs):
442 if 'end' in kwargs:
443 self.second = self.type(kwargs.pop('end'))
444 elif 'size' in kwargs:
445 self.second = self.first + self.type(kwargs.pop('size')) - 1
446 else:
447 raise TypeError, "Either end or size must be specified"
448
449 if len(args) == 0:
450 self.first = self.type(kwargs.pop('start'))
451 handle_kwargs(self, kwargs)
452
453 elif len(args) == 1:
454 if kwargs:
455 self.first = self.type(args[0])
456 handle_kwargs(self, kwargs)
457 elif isinstance(args[0], Range):
458 self.first = self.type(args[0].first)
459 self.second = self.type(args[0].second)
460 elif isinstance(args[0], (list, tuple)):
461 self.first = self.type(args[0][0])
462 self.second = self.type(args[0][1])
463 else:
464 self.first = self.type(0)
465 self.second = self.type(args[0]) - 1
466
467 elif len(args) == 2:
468 self.first = self.type(args[0])
469 self.second = self.type(args[1])
470 else:
471 raise TypeError, "Too many arguments specified"
472
473 if kwargs:
474 raise TypeError, "too many keywords: %s" % kwargs.keys()
475
476 def __str__(self):
477 return '%s:%s' % (self.first, self.second)
478
479 class AddrRange(Range):
480 type = Addr
481 swig_predecls = ['%include "python/swig/range.i"']
482
483 def getValue(self):
484 from m5.objects.params import AddrRange
485
486 value = AddrRange()
487 value.start = long(self.first)
488 value.end = long(self.second)
489 return value
490
491 class TickRange(Range):
492 type = Tick
493 swig_predecls = ['%include "python/swig/range.i"']
494
495 def getValue(self):
496 from m5.objects.params import TickRange
497
498 value = TickRange()
499 value.start = long(self.first)
500 value.end = long(self.second)
501 return value
502
503 # Boolean parameter type. Python doesn't let you subclass bool, since
504 # it doesn't want to let you create multiple instances of True and
505 # False. Thus this is a little more complicated than String.
506 class Bool(ParamValue):
507 cxx_type = 'bool'
508 def __init__(self, value):
509 try:
510 self.value = convert.toBool(value)
511 except TypeError:
512 self.value = bool(value)
513
514 def getValue(self):
515 return bool(self.value)
516
517 def __str__(self):
518 return str(self.value)
519
520 def ini_str(self):
521 if self.value:
522 return 'true'
523 return 'false'
524
525 def IncEthernetAddr(addr, val = 1):
526 bytes = map(lambda x: int(x, 16), addr.split(':'))
527 bytes[5] += val
528 for i in (5, 4, 3, 2, 1):
529 val,rem = divmod(bytes[i], 256)
530 bytes[i] = rem
531 if val == 0:
532 break
533 bytes[i - 1] += val
534 assert(bytes[0] <= 255)
535 return ':'.join(map(lambda x: '%02x' % x, bytes))
536
537 _NextEthernetAddr = "00:90:00:00:00:01"
538 def NextEthernetAddr():
539 global _NextEthernetAddr
540
541 value = _NextEthernetAddr
542 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
543 return value
544
545 class EthernetAddr(ParamValue):
546 cxx_type = 'Net::EthAddr'
547 cxx_predecls = ['#include "base/inet.hh"']
548 swig_predecls = ['%include "python/swig/inet.i"']
549 def __init__(self, value):
550 if value == NextEthernetAddr:
551 self.value = value
552 return
553
554 if not isinstance(value, str):
555 raise TypeError, "expected an ethernet address and didn't get one"
556
557 bytes = value.split(':')
558 if len(bytes) != 6:
559 raise TypeError, 'invalid ethernet address %s' % value
560
561 for byte in bytes:
562 if not 0 <= int(byte) <= 256:
563 raise TypeError, 'invalid ethernet address %s' % value
564
565 self.value = value
566
567 def unproxy(self, base):
568 if self.value == NextEthernetAddr:
569 return EthernetAddr(self.value())
570 return self
571
572 def getValue(self):
573 from m5.objects.params import EthAddr
574 return EthAddr(self.value)
575
576 def ini_str(self):
577 return self.value
578
579 time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
580 "%a %b %d %H:%M:%S %Z %Y",
581 "%Y/%m/%d %H:%M:%S",
582 "%Y/%m/%d %H:%M",
583 "%Y/%m/%d",
584 "%m/%d/%Y %H:%M:%S",
585 "%m/%d/%Y %H:%M",
586 "%m/%d/%Y",
587 "%m/%d/%y %H:%M:%S",
588 "%m/%d/%y %H:%M",
589 "%m/%d/%y"]
590
591
592 def parse_time(value):
593 from time import gmtime, strptime, struct_time, time
594 from datetime import datetime, date
595
596 if isinstance(value, struct_time):
597 return value
598
599 if isinstance(value, (int, long)):
600 return gmtime(value)
601
602 if isinstance(value, (datetime, date)):
603 return value.timetuple()
604
605 if isinstance(value, str):
606 if value in ('Now', 'Today'):
607 return time.gmtime(time.time())
608
609 for format in time_formats:
610 try:
611 return strptime(value, format)
612 except ValueError:
613 pass
614
615 raise ValueError, "Could not parse '%s' as a time" % value
616
617 class Time(ParamValue):
618 cxx_type = 'tm'
619 cxx_predecls = [ '#include <time.h>' ]
620 swig_predecls = [ '%include "python/swig/time.i"' ]
621 def __init__(self, value):
622 self.value = parse_time(value)
623
624 def getValue(self):
625 from m5.objects.params import tm
626
627 c_time = tm()
628 py_time = self.value
629
630 # UNIX is years since 1900
631 c_time.tm_year = py_time.tm_year - 1900;
632
633 # Python starts at 1, UNIX starts at 0
634 c_time.tm_mon = py_time.tm_mon - 1;
635 c_time.tm_mday = py_time.tm_mday;
636 c_time.tm_hour = py_time.tm_hour;
637 c_time.tm_min = py_time.tm_min;
638 c_time.tm_sec = py_time.tm_sec;
639
640 # Python has 0 as Monday, UNIX is 0 as sunday
641 c_time.tm_wday = py_time.tm_wday + 1
642 if c_time.tm_wday > 6:
643 c_time.tm_wday -= 7;
644
645 # Python starts at 1, Unix starts at 0
646 c_time.tm_yday = py_time.tm_yday - 1;
647
648 return c_time
649
650 def __str__(self):
651 return time.asctime(self.value)
652
653 def ini_str(self):
654 return str(self)
655
656 # Enumerated types are a little more complex. The user specifies the
657 # type as Enum(foo) where foo is either a list or dictionary of
658 # alternatives (typically strings, but not necessarily so). (In the
659 # long run, the integer value of the parameter will be the list index
660 # or the corresponding dictionary value. For now, since we only check
661 # that the alternative is valid and then spit it into a .ini file,
662 # there's not much point in using the dictionary.)
663
664 # What Enum() must do is generate a new type encapsulating the
665 # provided list/dictionary so that specific values of the parameter
666 # can be instances of that type. We define two hidden internal
667 # classes (_ListEnum and _DictEnum) to serve as base classes, then
668 # derive the new type from the appropriate base class on the fly.
669
670 allEnums = {}
671 # Metaclass for Enum types
672 class MetaEnum(MetaParamValue):
673 def __new__(mcls, name, bases, dict):
674 assert name not in allEnums
675
676 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
677 allEnums[name] = cls
678 return cls
679
680 def __init__(cls, name, bases, init_dict):
681 if init_dict.has_key('map'):
682 if not isinstance(cls.map, dict):
683 raise TypeError, "Enum-derived class attribute 'map' " \
684 "must be of type dict"
685 # build list of value strings from map
686 cls.vals = cls.map.keys()
687 cls.vals.sort()
688 elif init_dict.has_key('vals'):
689 if not isinstance(cls.vals, list):
690 raise TypeError, "Enum-derived class attribute 'vals' " \
691 "must be of type list"
692 # build string->value map from vals sequence
693 cls.map = {}
694 for idx,val in enumerate(cls.vals):
695 cls.map[val] = idx
696 else:
697 raise TypeError, "Enum-derived class must define "\
698 "attribute 'map' or 'vals'"
699
700 cls.cxx_type = 'Enums::%s' % name
701
702 super(MetaEnum, cls).__init__(name, bases, init_dict)
703
704 def __str__(cls):
705 return cls.__name__
706
707 # Generate C++ class declaration for this enum type.
708 # Note that we wrap the enum in a class/struct to act as a namespace,
709 # so that the enum strings can be brief w/o worrying about collisions.
710 def cxx_decl(cls):
711 code = "#ifndef __ENUM__%s\n" % cls
712 code += '#define __ENUM__%s\n' % cls
713 code += '\n'
714 code += 'namespace Enums {\n'
715 code += ' enum %s {\n' % cls
716 for val in cls.vals:
717 code += ' %s = %d,\n' % (val, cls.map[val])
718 code += ' Num_%s = %d,\n' % (cls, len(cls.vals))
719 code += ' };\n'
720 code += ' extern const char *%sStrings[Num_%s];\n' % (cls, cls)
721 code += '}\n'
722 code += '\n'
723 code += '#endif\n'
724 return code
725
726 def cxx_def(cls):
727 code = '#include "enums/%s.hh"\n' % cls
728 code += 'namespace Enums {\n'
729 code += ' const char *%sStrings[Num_%s] =\n' % (cls, cls)
730 code += ' {\n'
731 for val in cls.vals:
732 code += ' "%s",\n' % val
733 code += ' };\n'
734 code += '}\n'
735 return code
736
737 # Base class for enum types.
738 class Enum(ParamValue):
739 __metaclass__ = MetaEnum
740 vals = []
741
742 def __init__(self, value):
743 if value not in self.map:
744 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
745 % (value, self.vals)
746 self.value = value
747
748 def getValue(self):
749 return int(self.map[self.value])
750
751 def __str__(self):
752 return self.value
753
754 # how big does a rounding error need to be before we warn about it?
755 frequency_tolerance = 0.001 # 0.1%
756
757 class TickParamValue(NumericParamValue):
758 cxx_type = 'Tick'
759 cxx_predecls = ['#include "sim/host.hh"']
760 swig_predecls = ['%import "stdint.i"\n' +
761 '%import "sim/host.hh"']
762
763 def getValue(self):
764 return long(self.value)
765
766 class Latency(TickParamValue):
767 def __init__(self, value):
768 if isinstance(value, (Latency, Clock)):
769 self.ticks = value.ticks
770 self.value = value.value
771 elif isinstance(value, Frequency):
772 self.ticks = value.ticks
773 self.value = 1.0 / value.value
774 elif value.endswith('t'):
775 self.ticks = True
776 self.value = int(value[:-1])
777 else:
778 self.ticks = False
779 self.value = convert.toLatency(value)
780
781 def __getattr__(self, attr):
782 if attr in ('latency', 'period'):
783 return self
784 if attr == 'frequency':
785 return Frequency(self)
786 raise AttributeError, "Latency object has no attribute '%s'" % attr
787
788 def getValue(self):
789 if self.ticks or self.value == 0:
790 value = self.value
791 else:
792 value = ticks.fromSeconds(self.value)
793 return long(value)
794
795 # convert latency to ticks
796 def ini_str(self):
797 return '%d' % self.getValue()
798
799 class Frequency(TickParamValue):
800 def __init__(self, value):
801 if isinstance(value, (Latency, Clock)):
802 if value.value == 0:
803 self.value = 0
804 else:
805 self.value = 1.0 / value.value
806 self.ticks = value.ticks
807 elif isinstance(value, Frequency):
808 self.value = value.value
809 self.ticks = value.ticks
810 else:
811 self.ticks = False
812 self.value = convert.toFrequency(value)
813
814 def __getattr__(self, attr):
815 if attr == 'frequency':
816 return self
817 if attr in ('latency', 'period'):
818 return Latency(self)
819 raise AttributeError, "Frequency object has no attribute '%s'" % attr
820
821 # convert latency to ticks
822 def getValue(self):
823 if self.ticks or self.value == 0:
824 value = self.value
825 else:
826 value = ticks.fromSeconds(1.0 / self.value)
827 return long(value)
828
829 def ini_str(self):
830 return '%d' % self.getValue()
831
832 # A generic frequency and/or Latency value. Value is stored as a latency,
833 # but to avoid ambiguity this object does not support numeric ops (* or /).
834 # An explicit conversion to a Latency or Frequency must be made first.
835 class Clock(ParamValue):
836 cxx_type = 'Tick'
837 cxx_predecls = ['#include "sim/host.hh"']
838 swig_predecls = ['%import "stdint.i"\n' +
839 '%import "sim/host.hh"']
840 def __init__(self, value):
841 if isinstance(value, (Latency, Clock)):
842 self.ticks = value.ticks
843 self.value = value.value
844 elif isinstance(value, Frequency):
845 self.ticks = value.ticks
846 self.value = 1.0 / value.value
847 elif value.endswith('t'):
848 self.ticks = True
849 self.value = int(value[:-1])
850 else:
851 self.ticks = False
852 self.value = convert.anyToLatency(value)
853
854 def __getattr__(self, attr):
855 if attr == 'frequency':
856 return Frequency(self)
857 if attr in ('latency', 'period'):
858 return Latency(self)
859 raise AttributeError, "Frequency object has no attribute '%s'" % attr
860
861 def getValue(self):
862 return self.period.getValue()
863
864 def ini_str(self):
865 return self.period.ini_str()
866
867 class NetworkBandwidth(float,ParamValue):
868 cxx_type = 'float'
869 def __new__(cls, value):
870 # convert to bits per second
871 val = convert.toNetworkBandwidth(value)
872 return super(cls, NetworkBandwidth).__new__(cls, val)
873
874 def __str__(self):
875 return str(self.val)
876
877 def getValue(self):
878 # convert to seconds per byte
879 value = 8.0 / float(self)
880 # convert to ticks per byte
881 value = ticks.fromSeconds(value)
882 return float(value)
883
884 def ini_str(self):
885 return '%f' % self.getValue()
886
887 class MemoryBandwidth(float,ParamValue):
888 cxx_type = 'float'
889 def __new__(self, value):
890 # we want the number of ticks per byte of data
891 val = convert.toMemoryBandwidth(value)
892 return super(cls, MemoryBandwidth).__new__(cls, val)
893
894 def __str__(self):
895 return str(self.val)
896
897 def getValue(self):
898 # convert to seconds per byte
899 value = 1.0 / float(self)
900 # convert to ticks per byte
901 value = ticks.fromSeconds(value)
902 return float(value)
903
904 def ini_str(self):
905 return '%f' % self.getValue()
906
907 #
908 # "Constants"... handy aliases for various values.
909 #
910
911 # Special class for NULL pointers. Note the special check in
912 # make_param_value() above that lets these be assigned where a
913 # SimObject is required.
914 # only one copy of a particular node
915 class NullSimObject(object):
916 __metaclass__ = Singleton
917
918 def __call__(cls):
919 return cls
920
921 def _instantiate(self, parent = None, path = ''):
922 pass
923
924 def ini_str(self):
925 return 'Null'
926
927 def unproxy(self, base):
928 return self
929
930 def set_path(self, parent, name):
931 pass
932
933 def __str__(self):
934 return 'Null'
935
936 def getValue(self):
937 return None
938
939 # The only instance you'll ever need...
940 NULL = NullSimObject()
941
942 def isNullPointer(value):
943 return isinstance(value, NullSimObject)
944
945 # Some memory range specifications use this as a default upper bound.
946 MaxAddr = Addr.max
947 MaxTick = Tick.max
948 AllMemory = AddrRange(0, MaxAddr)
949
950
951 #####################################################################
952 #
953 # Port objects
954 #
955 # Ports are used to interconnect objects in the memory system.
956 #
957 #####################################################################
958
959 # Port reference: encapsulates a reference to a particular port on a
960 # particular SimObject.
961 class PortRef(object):
962 def __init__(self, simobj, name):
963 assert(isSimObject(simobj) or isSimObjectClass(simobj))
964 self.simobj = simobj
965 self.name = name
966 self.peer = None # not associated with another port yet
967 self.ccConnected = False # C++ port connection done?
968 self.index = -1 # always -1 for non-vector ports
969
970 def __str__(self):
971 return '%s.%s' % (self.simobj, self.name)
972
973 # for config.ini, print peer's name (not ours)
974 def ini_str(self):
975 return str(self.peer)
976
977 def __getattr__(self, attr):
978 if attr == 'peerObj':
979 # shorthand for proxies
980 return self.peer.simobj
981 raise AttributeError, "'%s' object has no attribute '%s'" % \
982 (self.__class__.__name__, attr)
983
984 # Full connection is symmetric (both ways). Called via
985 # SimObject.__setattr__ as a result of a port assignment, e.g.,
986 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
987 # e.g., "obj1.portA[3] = obj2.portB".
988 def connect(self, other):
989 if isinstance(other, VectorPortRef):
990 # reference to plain VectorPort is implicit append
991 other = other._get_next()
992 if self.peer and not proxy.isproxy(self.peer):
993 print "warning: overwriting port", self, \
994 "value", self.peer, "with", other
995 self.peer = other
996 if proxy.isproxy(other):
997 other.set_param_desc(PortParamDesc())
998 elif isinstance(other, PortRef):
999 if other.peer is not self:
1000 other.connect(self)
1001 else:
1002 raise TypeError, \
1003 "assigning non-port reference '%s' to port '%s'" \
1004 % (other, self)
1005
1006 def clone(self, simobj, memo):
1007 if memo.has_key(self):
1008 return memo[self]
1009 newRef = copy.copy(self)
1010 memo[self] = newRef
1011 newRef.simobj = simobj
1012 assert(isSimObject(newRef.simobj))
1013 if self.peer and not proxy.isproxy(self.peer):
1014 peerObj = self.peer.simobj(_memo=memo)
1015 newRef.peer = self.peer.clone(peerObj, memo)
1016 assert(not isinstance(newRef.peer, VectorPortRef))
1017 return newRef
1018
1019 def unproxy(self, simobj):
1020 assert(simobj is self.simobj)
1021 if proxy.isproxy(self.peer):
1022 try:
1023 realPeer = self.peer.unproxy(self.simobj)
1024 except:
1025 print "Error in unproxying port '%s' of %s" % \
1026 (self.name, self.simobj.path())
1027 raise
1028 self.connect(realPeer)
1029
1030 # Call C++ to create corresponding port connection between C++ objects
1031 def ccConnect(self):
1032 from m5.objects.params import connectPorts
1033
1034 if self.ccConnected: # already done this
1035 return
1036 peer = self.peer
1037 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1038 peer.simobj.getCCObject(), peer.name, peer.index)
1039 self.ccConnected = True
1040 peer.ccConnected = True
1041
1042 # A reference to an individual element of a VectorPort... much like a
1043 # PortRef, but has an index.
1044 class VectorPortElementRef(PortRef):
1045 def __init__(self, simobj, name, index):
1046 PortRef.__init__(self, simobj, name)
1047 self.index = index
1048
1049 def __str__(self):
1050 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1051
1052 # A reference to a complete vector-valued port (not just a single element).
1053 # Can be indexed to retrieve individual VectorPortElementRef instances.
1054 class VectorPortRef(object):
1055 def __init__(self, simobj, name):
1056 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1057 self.simobj = simobj
1058 self.name = name
1059 self.elements = []
1060
1061 def __str__(self):
1062 return '%s.%s[:]' % (self.simobj, self.name)
1063
1064 # for config.ini, print peer's name (not ours)
1065 def ini_str(self):
1066 return ' '.join([el.ini_str() for el in self.elements])
1067
1068 def __getitem__(self, key):
1069 if not isinstance(key, int):
1070 raise TypeError, "VectorPort index must be integer"
1071 if key >= len(self.elements):
1072 # need to extend list
1073 ext = [VectorPortElementRef(self.simobj, self.name, i)
1074 for i in range(len(self.elements), key+1)]
1075 self.elements.extend(ext)
1076 return self.elements[key]
1077
1078 def _get_next(self):
1079 return self[len(self.elements)]
1080
1081 def __setitem__(self, key, value):
1082 if not isinstance(key, int):
1083 raise TypeError, "VectorPort index must be integer"
1084 self[key].connect(value)
1085
1086 def connect(self, other):
1087 if isinstance(other, (list, tuple)):
1088 # Assign list of port refs to vector port.
1089 # For now, append them... not sure if that's the right semantics
1090 # or if it should replace the current vector.
1091 for ref in other:
1092 self._get_next().connect(ref)
1093 else:
1094 # scalar assignment to plain VectorPort is implicit append
1095 self._get_next().connect(other)
1096
1097 def clone(self, simobj, memo):
1098 if memo.has_key(self):
1099 return memo[self]
1100 newRef = copy.copy(self)
1101 memo[self] = newRef
1102 newRef.simobj = simobj
1103 assert(isSimObject(newRef.simobj))
1104 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1105 return newRef
1106
1107 def unproxy(self, simobj):
1108 [el.unproxy(simobj) for el in self.elements]
1109
1110 def ccConnect(self):
1111 [el.ccConnect() for el in self.elements]
1112
1113 # Port description object. Like a ParamDesc object, this represents a
1114 # logical port in the SimObject class, not a particular port on a
1115 # SimObject instance. The latter are represented by PortRef objects.
1116 class Port(object):
1117 # Port("description") or Port(default, "description")
1118 def __init__(self, *args):
1119 if len(args) == 1:
1120 self.desc = args[0]
1121 elif len(args) == 2:
1122 self.default = args[0]
1123 self.desc = args[1]
1124 else:
1125 raise TypeError, 'wrong number of arguments'
1126 # self.name is set by SimObject class on assignment
1127 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1128
1129 # Generate a PortRef for this port on the given SimObject with the
1130 # given name
1131 def makeRef(self, simobj):
1132 return PortRef(simobj, self.name)
1133
1134 # Connect an instance of this port (on the given SimObject with
1135 # the given name) with the port described by the supplied PortRef
1136 def connect(self, simobj, ref):
1137 self.makeRef(simobj).connect(ref)
1138
1139 # VectorPort description object. Like Port, but represents a vector
1140 # of connections (e.g., as on a Bus).
1141 class VectorPort(Port):
1142 def __init__(self, *args):
1143 Port.__init__(self, *args)
1144 self.isVec = True
1145
1146 def makeRef(self, simobj):
1147 return VectorPortRef(simobj, self.name)
1148
1149 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1150 # proxy objects (via set_param_desc()) so that proxy error messages
1151 # make sense.
1152 class PortParamDesc(object):
1153 __metaclass__ = Singleton
1154
1155 ptype_str = 'Port'
1156 ptype = Port
1157
1158 __all__ = ['Param', 'VectorParam',
1159 'Enum', 'Bool', 'String', 'Float',
1160 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1161 'Int32', 'UInt32', 'Int64', 'UInt64',
1162 'Counter', 'Addr', 'Tick', 'Percent',
1163 'TcpPort', 'UdpPort', 'EthernetAddr',
1164 'MemorySize', 'MemorySize32',
1165 'Latency', 'Frequency', 'Clock',
1166 'NetworkBandwidth', 'MemoryBandwidth',
1167 'Range', 'AddrRange', 'TickRange',
1168 'MaxAddr', 'MaxTick', 'AllMemory',
1169 'Time',
1170 'NextEthernetAddr', 'NULL',
1171 'Port', 'VectorPort']