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