3a3a300148219ab24f69c624504a6cd97787c4e8
[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 proxy
54 import ticks
55 from util import *
56
57 def isSimObject(*args, **kwargs):
58 return SimObject.isSimObject(*args, **kwargs)
59
60 def isSimObjectSequence(*args, **kwargs):
61 return SimObject.isSimObjectSequence(*args, **kwargs)
62
63 def isSimObjectClass(*args, **kwargs):
64 return SimObject.isSimObjectClass(*args, **kwargs)
65
66 allParams = {}
67
68 class MetaParamValue(type):
69 def __new__(mcls, name, bases, dct):
70 cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
71 assert name not in allParams
72 allParams[name] = cls
73 return cls
74
75
76 # Dummy base class to identify types that are legitimate for SimObject
77 # parameters.
78 class ParamValue(object):
79 __metaclass__ = MetaParamValue
80
81 cxx_predecls = []
82 swig_predecls = []
83
84 # default for printing to .ini file is regular string conversion.
85 # will be overridden in some cases
86 def ini_str(self):
87 return str(self)
88
89 # allows us to blithely call unproxy() on things without checking
90 # if they're really proxies or not
91 def unproxy(self, base):
92 return self
93
94 # Regular parameter description.
95 class ParamDesc(object):
96 def __init__(self, ptype_str, ptype, *args, **kwargs):
97 self.ptype_str = ptype_str
98 # remember ptype only if it is provided
99 if ptype != None:
100 self.ptype = ptype
101
102 if args:
103 if len(args) == 1:
104 self.desc = args[0]
105 elif len(args) == 2:
106 self.default = args[0]
107 self.desc = args[1]
108 else:
109 raise TypeError, 'too many arguments'
110
111 if kwargs.has_key('desc'):
112 assert(not hasattr(self, 'desc'))
113 self.desc = kwargs['desc']
114 del kwargs['desc']
115
116 if kwargs.has_key('default'):
117 assert(not hasattr(self, 'default'))
118 self.default = kwargs['default']
119 del kwargs['default']
120
121 if kwargs:
122 raise TypeError, 'extra unknown kwargs %s' % kwargs
123
124 if not hasattr(self, 'desc'):
125 raise TypeError, 'desc attribute missing'
126
127 def __getattr__(self, attr):
128 if attr == 'ptype':
129 ptype = SimObject.allClasses[self.ptype_str]
130 assert issubclass(ptype, SimObject.SimObject)
131 self.ptype = ptype
132 return ptype
133
134 raise AttributeError, "'%s' object has no attribute '%s'" % \
135 (type(self).__name__, attr)
136
137 def convert(self, value):
138 if isinstance(value, proxy.BaseProxy):
139 value.set_param_desc(self)
140 return value
141 if not hasattr(self, 'ptype') and isNullPointer(value):
142 # deferred evaluation of SimObject; continue to defer if
143 # we're just assigning a null pointer
144 return value
145 if isinstance(value, self.ptype):
146 return value
147 if isNullPointer(value) and isSimObjectClass(self.ptype):
148 return value
149 return self.ptype(value)
150
151 def cxx_predecls(self):
152 return self.ptype.cxx_predecls
153
154 def swig_predecls(self):
155 return self.ptype.swig_predecls
156
157 def cxx_decl(self):
158 return '%s %s;' % (self.ptype.cxx_type, self.name)
159
160 # Vector-valued parameter description. Just like ParamDesc, except
161 # that the value is a vector (list) of the specified type instead of a
162 # single value.
163
164 class VectorParamValue(list):
165 __metaclass__ = MetaParamValue
166 def __setattr__(self, attr, value):
167 raise AttributeError, \
168 "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
169
170 def ini_str(self):
171 return ' '.join([v.ini_str() for v in self])
172
173 def getValue(self):
174 return [ v.getValue() for v in self ]
175
176 def unproxy(self, base):
177 return [v.unproxy(base) for v in self]
178
179 class SimObjVector(VectorParamValue):
180 def print_ini(self, ini_file):
181 for v in self:
182 v.print_ini(ini_file)
183
184 class VectorParamDesc(ParamDesc):
185 # Convert assigned value to appropriate type. If the RHS is not a
186 # list or tuple, it generates a single-element list.
187 def convert(self, value):
188 if isinstance(value, (list, tuple)):
189 # list: coerce each element into new list
190 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
191 else:
192 # singleton: coerce to a single-element list
193 tmp_list = [ ParamDesc.convert(self, value) ]
194
195 if isSimObjectSequence(tmp_list):
196 return SimObjVector(tmp_list)
197 else:
198 return VectorParamValue(tmp_list)
199
200 def swig_predecls(self):
201 return ['%%include "%s_vptype.i"' % self.ptype_str]
202
203 def swig_decl(self):
204 cxx_type = re.sub('std::', '', self.ptype.cxx_type)
205 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
206 (self.ptype_str, cxx_type)
207 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
208
209 def cxx_predecls(self):
210 return ['#include <vector>'] + self.ptype.cxx_predecls
211
212 def cxx_decl(self):
213 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
214
215 class ParamFactory(object):
216 def __init__(self, param_desc_class, ptype_str = None):
217 self.param_desc_class = param_desc_class
218 self.ptype_str = ptype_str
219
220 def __getattr__(self, attr):
221 if self.ptype_str:
222 attr = self.ptype_str + '.' + attr
223 return ParamFactory(self.param_desc_class, attr)
224
225 # E.g., Param.Int(5, "number of widgets")
226 def __call__(self, *args, **kwargs):
227 ptype = None
228 try:
229 ptype = allParams[self.ptype_str]
230 except KeyError:
231 # if name isn't defined yet, assume it's a SimObject, and
232 # try to resolve it later
233 pass
234 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
235
236 Param = ParamFactory(ParamDesc)
237 VectorParam = ParamFactory(VectorParamDesc)
238
239 #####################################################################
240 #
241 # Parameter Types
242 #
243 # Though native Python types could be used to specify parameter types
244 # (the 'ptype' field of the Param and VectorParam classes), it's more
245 # flexible to define our own set of types. This gives us more control
246 # over how Python expressions are converted to values (via the
247 # __init__() constructor) and how these values are printed out (via
248 # the __str__() conversion method).
249 #
250 #####################################################################
251
252 # String-valued parameter. Just mixin the ParamValue class with the
253 # built-in str class.
254 class String(ParamValue,str):
255 cxx_type = 'std::string'
256 cxx_predecls = ['#include <string>']
257 swig_predecls = ['%include "std_string.i"\n' +
258 '%apply const std::string& {std::string *};']
259 swig_predecls = ['%include "std_string.i"' ]
260
261 def getValue(self):
262 return self
263
264 # superclass for "numeric" parameter values, to emulate math
265 # operations in a type-safe way. e.g., a Latency times an int returns
266 # a new Latency object.
267 class NumericParamValue(ParamValue):
268 def __str__(self):
269 return str(self.value)
270
271 def __float__(self):
272 return float(self.value)
273
274 def __long__(self):
275 return long(self.value)
276
277 def __int__(self):
278 return int(self.value)
279
280 # hook for bounds checking
281 def _check(self):
282 return
283
284 def __mul__(self, other):
285 newobj = self.__class__(self)
286 newobj.value *= other
287 newobj._check()
288 return newobj
289
290 __rmul__ = __mul__
291
292 def __div__(self, other):
293 newobj = self.__class__(self)
294 newobj.value /= other
295 newobj._check()
296 return newobj
297
298 def __sub__(self, other):
299 newobj = self.__class__(self)
300 newobj.value -= other
301 newobj._check()
302 return newobj
303
304 # Metaclass for bounds-checked integer parameters. See CheckedInt.
305 class CheckedIntType(MetaParamValue):
306 def __init__(cls, name, bases, dict):
307 super(CheckedIntType, cls).__init__(name, bases, dict)
308
309 # CheckedInt is an abstract base class, so we actually don't
310 # want to do any processing on it... the rest of this code is
311 # just for classes that derive from CheckedInt.
312 if name == 'CheckedInt':
313 return
314
315 if not cls.cxx_predecls:
316 # most derived types require this, so we just do it here once
317 cls.cxx_predecls = ['#include "base/types.hh"']
318
319 if not cls.swig_predecls:
320 # most derived types require this, so we just do it here once
321 cls.swig_predecls = ['%import "stdint.i"\n' +
322 '%import "base/types.hh"']
323
324 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
325 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
326 panic("CheckedInt subclass %s must define either\n" \
327 " 'min' and 'max' or 'size' and 'unsigned'\n",
328 name);
329 if cls.unsigned:
330 cls.min = 0
331 cls.max = 2 ** cls.size - 1
332 else:
333 cls.min = -(2 ** (cls.size - 1))
334 cls.max = (2 ** (cls.size - 1)) - 1
335
336 # Abstract superclass for bounds-checked integer parameters. This
337 # class is subclassed to generate parameter classes with specific
338 # bounds. Initialization of the min and max bounds is done in the
339 # metaclass CheckedIntType.__init__.
340 class CheckedInt(NumericParamValue):
341 __metaclass__ = CheckedIntType
342
343 def _check(self):
344 if not self.min <= self.value <= self.max:
345 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
346 (self.min, self.value, self.max)
347
348 def __init__(self, value):
349 if isinstance(value, str):
350 self.value = convert.toInteger(value)
351 elif isinstance(value, (int, long, float, NumericParamValue)):
352 self.value = long(value)
353 else:
354 raise TypeError, "Can't convert object of type %s to CheckedInt" \
355 % type(value).__name__
356 self._check()
357
358 def getValue(self):
359 return long(self.value)
360
361 class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
362 class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
363
364 class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
365 class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
366 class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
367 class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
368 class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
369 class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
370 class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
371 class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
372
373 class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
374 class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
375 class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
376 class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
377
378 class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
379
380 class Float(ParamValue, float):
381 cxx_type = 'double'
382
383 def __init__(self, value):
384 if isinstance(value, (int, long, float, NumericParamValue, Float)):
385 self.value = float(value)
386 else:
387 raise TypeError, "Can't convert object of type %s to Float" \
388 % type(value).__name__
389
390 def getValue(self):
391 return float(self.value)
392
393 class MemorySize(CheckedInt):
394 cxx_type = 'uint64_t'
395 size = 64
396 unsigned = True
397 def __init__(self, value):
398 if isinstance(value, MemorySize):
399 self.value = value.value
400 else:
401 self.value = convert.toMemorySize(value)
402 self._check()
403
404 class MemorySize32(CheckedInt):
405 cxx_type = 'uint32_t'
406 size = 32
407 unsigned = True
408 def __init__(self, value):
409 if isinstance(value, MemorySize):
410 self.value = value.value
411 else:
412 self.value = convert.toMemorySize(value)
413 self._check()
414
415 class Addr(CheckedInt):
416 cxx_type = 'Addr'
417 size = 64
418 unsigned = True
419 def __init__(self, value):
420 if isinstance(value, Addr):
421 self.value = value.value
422 else:
423 try:
424 self.value = convert.toMemorySize(value)
425 except TypeError:
426 self.value = long(value)
427 self._check()
428 def __add__(self, other):
429 if isinstance(other, Addr):
430 return self.value + other.value
431 else:
432 return self.value + other
433
434
435 class MetaRange(MetaParamValue):
436 def __init__(cls, name, bases, dict):
437 super(MetaRange, cls).__init__(name, bases, dict)
438 if name == 'Range':
439 return
440 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
441 cls.cxx_predecls = \
442 ['#include "base/range.hh"'] + cls.type.cxx_predecls
443
444 class Range(ParamValue):
445 __metaclass__ = MetaRange
446 type = Int # default; can be overridden in subclasses
447 def __init__(self, *args, **kwargs):
448 def handle_kwargs(self, kwargs):
449 if 'end' in kwargs:
450 self.second = self.type(kwargs.pop('end'))
451 elif 'size' in kwargs:
452 self.second = self.first + self.type(kwargs.pop('size')) - 1
453 else:
454 raise TypeError, "Either end or size must be specified"
455
456 if len(args) == 0:
457 self.first = self.type(kwargs.pop('start'))
458 handle_kwargs(self, kwargs)
459
460 elif len(args) == 1:
461 if kwargs:
462 self.first = self.type(args[0])
463 handle_kwargs(self, kwargs)
464 elif isinstance(args[0], Range):
465 self.first = self.type(args[0].first)
466 self.second = self.type(args[0].second)
467 elif isinstance(args[0], (list, tuple)):
468 self.first = self.type(args[0][0])
469 self.second = self.type(args[0][1])
470 else:
471 self.first = self.type(0)
472 self.second = self.type(args[0]) - 1
473
474 elif len(args) == 2:
475 self.first = self.type(args[0])
476 self.second = self.type(args[1])
477 else:
478 raise TypeError, "Too many arguments specified"
479
480 if kwargs:
481 raise TypeError, "too many keywords: %s" % kwargs.keys()
482
483 def __str__(self):
484 return '%s:%s' % (self.first, self.second)
485
486 class AddrRange(Range):
487 type = Addr
488 swig_predecls = ['%include "python/swig/range.i"']
489
490 def getValue(self):
491 from m5.objects.params import AddrRange
492
493 value = AddrRange()
494 value.start = long(self.first)
495 value.end = long(self.second)
496 return value
497
498 class TickRange(Range):
499 type = Tick
500 swig_predecls = ['%include "python/swig/range.i"']
501
502 def getValue(self):
503 from m5.objects.params import TickRange
504
505 value = TickRange()
506 value.start = long(self.first)
507 value.end = long(self.second)
508 return value
509
510 # Boolean parameter type. Python doesn't let you subclass bool, since
511 # it doesn't want to let you create multiple instances of True and
512 # False. Thus this is a little more complicated than String.
513 class Bool(ParamValue):
514 cxx_type = 'bool'
515 def __init__(self, value):
516 try:
517 self.value = convert.toBool(value)
518 except TypeError:
519 self.value = bool(value)
520
521 def getValue(self):
522 return bool(self.value)
523
524 def __str__(self):
525 return str(self.value)
526
527 def ini_str(self):
528 if self.value:
529 return 'true'
530 return 'false'
531
532 def IncEthernetAddr(addr, val = 1):
533 bytes = map(lambda x: int(x, 16), addr.split(':'))
534 bytes[5] += val
535 for i in (5, 4, 3, 2, 1):
536 val,rem = divmod(bytes[i], 256)
537 bytes[i] = rem
538 if val == 0:
539 break
540 bytes[i - 1] += val
541 assert(bytes[0] <= 255)
542 return ':'.join(map(lambda x: '%02x' % x, bytes))
543
544 _NextEthernetAddr = "00:90:00:00:00:01"
545 def NextEthernetAddr():
546 global _NextEthernetAddr
547
548 value = _NextEthernetAddr
549 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
550 return value
551
552 class EthernetAddr(ParamValue):
553 cxx_type = 'Net::EthAddr'
554 cxx_predecls = ['#include "base/inet.hh"']
555 swig_predecls = ['%include "python/swig/inet.i"']
556 def __init__(self, value):
557 if value == NextEthernetAddr:
558 self.value = value
559 return
560
561 if not isinstance(value, str):
562 raise TypeError, "expected an ethernet address and didn't get one"
563
564 bytes = value.split(':')
565 if len(bytes) != 6:
566 raise TypeError, 'invalid ethernet address %s' % value
567
568 for byte in bytes:
569 if not 0 <= int(byte) <= 256:
570 raise TypeError, 'invalid ethernet address %s' % value
571
572 self.value = value
573
574 def unproxy(self, base):
575 if self.value == NextEthernetAddr:
576 return EthernetAddr(self.value())
577 return self
578
579 def getValue(self):
580 from m5.objects.params import EthAddr
581 return EthAddr(self.value)
582
583 def ini_str(self):
584 return self.value
585
586 time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
587 "%a %b %d %H:%M:%S %Z %Y",
588 "%Y/%m/%d %H:%M:%S",
589 "%Y/%m/%d %H:%M",
590 "%Y/%m/%d",
591 "%m/%d/%Y %H:%M:%S",
592 "%m/%d/%Y %H:%M",
593 "%m/%d/%Y",
594 "%m/%d/%y %H:%M:%S",
595 "%m/%d/%y %H:%M",
596 "%m/%d/%y"]
597
598
599 def parse_time(value):
600 from time import gmtime, strptime, struct_time, time
601 from datetime import datetime, date
602
603 if isinstance(value, struct_time):
604 return value
605
606 if isinstance(value, (int, long)):
607 return gmtime(value)
608
609 if isinstance(value, (datetime, date)):
610 return value.timetuple()
611
612 if isinstance(value, str):
613 if value in ('Now', 'Today'):
614 return time.gmtime(time.time())
615
616 for format in time_formats:
617 try:
618 return strptime(value, format)
619 except ValueError:
620 pass
621
622 raise ValueError, "Could not parse '%s' as a time" % value
623
624 class Time(ParamValue):
625 cxx_type = 'tm'
626 cxx_predecls = [ '#include <time.h>' ]
627 swig_predecls = [ '%include "python/swig/time.i"' ]
628 def __init__(self, value):
629 self.value = parse_time(value)
630
631 def getValue(self):
632 from m5.objects.params import tm
633
634 c_time = tm()
635 py_time = self.value
636
637 # UNIX is years since 1900
638 c_time.tm_year = py_time.tm_year - 1900;
639
640 # Python starts at 1, UNIX starts at 0
641 c_time.tm_mon = py_time.tm_mon - 1;
642 c_time.tm_mday = py_time.tm_mday;
643 c_time.tm_hour = py_time.tm_hour;
644 c_time.tm_min = py_time.tm_min;
645 c_time.tm_sec = py_time.tm_sec;
646
647 # Python has 0 as Monday, UNIX is 0 as sunday
648 c_time.tm_wday = py_time.tm_wday + 1
649 if c_time.tm_wday > 6:
650 c_time.tm_wday -= 7;
651
652 # Python starts at 1, Unix starts at 0
653 c_time.tm_yday = py_time.tm_yday - 1;
654
655 return c_time
656
657 def __str__(self):
658 return time.asctime(self.value)
659
660 def ini_str(self):
661 return str(self)
662
663 # Enumerated types are a little more complex. The user specifies the
664 # type as Enum(foo) where foo is either a list or dictionary of
665 # alternatives (typically strings, but not necessarily so). (In the
666 # long run, the integer value of the parameter will be the list index
667 # or the corresponding dictionary value. For now, since we only check
668 # that the alternative is valid and then spit it into a .ini file,
669 # there's not much point in using the dictionary.)
670
671 # What Enum() must do is generate a new type encapsulating the
672 # provided list/dictionary so that specific values of the parameter
673 # can be instances of that type. We define two hidden internal
674 # classes (_ListEnum and _DictEnum) to serve as base classes, then
675 # derive the new type from the appropriate base class on the fly.
676
677 allEnums = {}
678 # Metaclass for Enum types
679 class MetaEnum(MetaParamValue):
680 def __new__(mcls, name, bases, dict):
681 assert name not in allEnums
682
683 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
684 allEnums[name] = cls
685 return cls
686
687 def __init__(cls, name, bases, init_dict):
688 if init_dict.has_key('map'):
689 if not isinstance(cls.map, dict):
690 raise TypeError, "Enum-derived class attribute 'map' " \
691 "must be of type dict"
692 # build list of value strings from map
693 cls.vals = cls.map.keys()
694 cls.vals.sort()
695 elif init_dict.has_key('vals'):
696 if not isinstance(cls.vals, list):
697 raise TypeError, "Enum-derived class attribute 'vals' " \
698 "must be of type list"
699 # build string->value map from vals sequence
700 cls.map = {}
701 for idx,val in enumerate(cls.vals):
702 cls.map[val] = idx
703 else:
704 raise TypeError, "Enum-derived class must define "\
705 "attribute 'map' or 'vals'"
706
707 cls.cxx_type = 'Enums::%s' % name
708
709 super(MetaEnum, cls).__init__(name, bases, init_dict)
710
711 # Generate C++ class declaration for this enum type.
712 # Note that we wrap the enum in a class/struct to act as a namespace,
713 # so that the enum strings can be brief w/o worrying about collisions.
714 def cxx_decl(cls):
715 name = cls.__name__
716 code = "#ifndef __ENUM__%s\n" % name
717 code += '#define __ENUM__%s\n' % name
718 code += '\n'
719 code += 'namespace Enums {\n'
720 code += ' enum %s {\n' % name
721 for val in cls.vals:
722 code += ' %s = %d,\n' % (val, cls.map[val])
723 code += ' Num_%s = %d,\n' % (name, len(cls.vals))
724 code += ' };\n'
725 code += ' extern const char *%sStrings[Num_%s];\n' % (name, name)
726 code += '}\n'
727 code += '\n'
728 code += '#endif\n'
729 return code
730
731 def cxx_def(cls):
732 name = cls.__name__
733 code = '#include "enums/%s.hh"\n' % name
734 code += 'namespace Enums {\n'
735 code += ' const char *%sStrings[Num_%s] =\n' % (name, name)
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 "base/types.hh"']
766 swig_predecls = ['%import "stdint.i"\n' +
767 '%import "base/types.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 "base/types.hh"']
844 swig_predecls = ['%import "stdint.i"\n' +
845 '%import "base/types.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__(cls, 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.peer = None
1004 self.peer = other
1005 if proxy.isproxy(other):
1006 other.set_param_desc(PortParamDesc())
1007 elif isinstance(other, PortRef):
1008 if other.peer is not self:
1009 other.connect(self)
1010 else:
1011 raise TypeError, \
1012 "assigning non-port reference '%s' to port '%s'" \
1013 % (other, self)
1014
1015 def clone(self, simobj, memo):
1016 if memo.has_key(self):
1017 return memo[self]
1018 newRef = copy.copy(self)
1019 memo[self] = newRef
1020 newRef.simobj = simobj
1021 assert(isSimObject(newRef.simobj))
1022 if self.peer and not proxy.isproxy(self.peer):
1023 peerObj = self.peer.simobj(_memo=memo)
1024 newRef.peer = self.peer.clone(peerObj, memo)
1025 assert(not isinstance(newRef.peer, VectorPortRef))
1026 return newRef
1027
1028 def unproxy(self, simobj):
1029 assert(simobj is self.simobj)
1030 if proxy.isproxy(self.peer):
1031 try:
1032 realPeer = self.peer.unproxy(self.simobj)
1033 except:
1034 print "Error in unproxying port '%s' of %s" % \
1035 (self.name, self.simobj.path())
1036 raise
1037 self.connect(realPeer)
1038
1039 # Call C++ to create corresponding port connection between C++ objects
1040 def ccConnect(self):
1041 from m5.objects.params import connectPorts
1042
1043 if self.ccConnected: # already done this
1044 return
1045 peer = self.peer
1046 if not self.peer: # nothing to connect to
1047 return
1048 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1049 peer.simobj.getCCObject(), peer.name, peer.index)
1050 self.ccConnected = True
1051 peer.ccConnected = True
1052
1053 # A reference to an individual element of a VectorPort... much like a
1054 # PortRef, but has an index.
1055 class VectorPortElementRef(PortRef):
1056 def __init__(self, simobj, name, index):
1057 PortRef.__init__(self, simobj, name)
1058 self.index = index
1059
1060 def __str__(self):
1061 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1062
1063 # A reference to a complete vector-valued port (not just a single element).
1064 # Can be indexed to retrieve individual VectorPortElementRef instances.
1065 class VectorPortRef(object):
1066 def __init__(self, simobj, name):
1067 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1068 self.simobj = simobj
1069 self.name = name
1070 self.elements = []
1071
1072 def __str__(self):
1073 return '%s.%s[:]' % (self.simobj, self.name)
1074
1075 # for config.ini, print peer's name (not ours)
1076 def ini_str(self):
1077 return ' '.join([el.ini_str() for el in self.elements])
1078
1079 def __getitem__(self, key):
1080 if not isinstance(key, int):
1081 raise TypeError, "VectorPort index must be integer"
1082 if key >= len(self.elements):
1083 # need to extend list
1084 ext = [VectorPortElementRef(self.simobj, self.name, i)
1085 for i in range(len(self.elements), key+1)]
1086 self.elements.extend(ext)
1087 return self.elements[key]
1088
1089 def _get_next(self):
1090 return self[len(self.elements)]
1091
1092 def __setitem__(self, key, value):
1093 if not isinstance(key, int):
1094 raise TypeError, "VectorPort index must be integer"
1095 self[key].connect(value)
1096
1097 def connect(self, other):
1098 if isinstance(other, (list, tuple)):
1099 # Assign list of port refs to vector port.
1100 # For now, append them... not sure if that's the right semantics
1101 # or if it should replace the current vector.
1102 for ref in other:
1103 self._get_next().connect(ref)
1104 else:
1105 # scalar assignment to plain VectorPort is implicit append
1106 self._get_next().connect(other)
1107
1108 def clone(self, simobj, memo):
1109 if memo.has_key(self):
1110 return memo[self]
1111 newRef = copy.copy(self)
1112 memo[self] = newRef
1113 newRef.simobj = simobj
1114 assert(isSimObject(newRef.simobj))
1115 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1116 return newRef
1117
1118 def unproxy(self, simobj):
1119 [el.unproxy(simobj) for el in self.elements]
1120
1121 def ccConnect(self):
1122 [el.ccConnect() for el in self.elements]
1123
1124 # Port description object. Like a ParamDesc object, this represents a
1125 # logical port in the SimObject class, not a particular port on a
1126 # SimObject instance. The latter are represented by PortRef objects.
1127 class Port(object):
1128 # Port("description") or Port(default, "description")
1129 def __init__(self, *args):
1130 if len(args) == 1:
1131 self.desc = args[0]
1132 elif len(args) == 2:
1133 self.default = args[0]
1134 self.desc = args[1]
1135 else:
1136 raise TypeError, 'wrong number of arguments'
1137 # self.name is set by SimObject class on assignment
1138 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1139
1140 # Generate a PortRef for this port on the given SimObject with the
1141 # given name
1142 def makeRef(self, simobj):
1143 return PortRef(simobj, self.name)
1144
1145 # Connect an instance of this port (on the given SimObject with
1146 # the given name) with the port described by the supplied PortRef
1147 def connect(self, simobj, ref):
1148 self.makeRef(simobj).connect(ref)
1149
1150 # VectorPort description object. Like Port, but represents a vector
1151 # of connections (e.g., as on a Bus).
1152 class VectorPort(Port):
1153 def __init__(self, *args):
1154 Port.__init__(self, *args)
1155 self.isVec = True
1156
1157 def makeRef(self, simobj):
1158 return VectorPortRef(simobj, self.name)
1159
1160 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1161 # proxy objects (via set_param_desc()) so that proxy error messages
1162 # make sense.
1163 class PortParamDesc(object):
1164 __metaclass__ = Singleton
1165
1166 ptype_str = 'Port'
1167 ptype = Port
1168
1169 baseEnums = allEnums.copy()
1170 baseParams = allParams.copy()
1171
1172 def clear():
1173 global allEnums, allParams
1174
1175 allEnums = baseEnums.copy()
1176 allParams = baseParams.copy()
1177
1178 __all__ = ['Param', 'VectorParam',
1179 'Enum', 'Bool', 'String', 'Float',
1180 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1181 'Int32', 'UInt32', 'Int64', 'UInt64',
1182 'Counter', 'Addr', 'Tick', 'Percent',
1183 'TcpPort', 'UdpPort', 'EthernetAddr',
1184 'MemorySize', 'MemorySize32',
1185 'Latency', 'Frequency', 'Clock',
1186 'NetworkBandwidth', 'MemoryBandwidth',
1187 'Range', 'AddrRange', 'TickRange',
1188 'MaxAddr', 'MaxTick', 'AllMemory',
1189 'Time',
1190 'NextEthernetAddr', 'NULL',
1191 'Port', 'VectorPort']
1192
1193 import SimObject