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