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