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