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