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