params.py (3103:330ec058b026) params.py (3105:993f1abefd67)
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
373
374class MetaRange(type):
375 def __init__(cls, name, bases, dict):
376 super(MetaRange, cls).__init__(name, bases, dict)
377 if name == 'Range':
378 return
379 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
380 cls.cxx_predecls = \
381 ['#include "base/range.hh"'] + cls.type.cxx_predecls
382
383class Range(ParamValue):
384 __metaclass__ = MetaRange
385 type = Int # default; can be overridden in subclasses
386 def __init__(self, *args, **kwargs):
387 def handle_kwargs(self, kwargs):
388 if 'end' in kwargs:
389 self.second = self.type(kwargs.pop('end'))
390 elif 'size' in kwargs:
391 self.second = self.first + self.type(kwargs.pop('size')) - 1
392 else:
393 raise TypeError, "Either end or size must be specified"
394
395 if len(args) == 0:
396 self.first = self.type(kwargs.pop('start'))
397 handle_kwargs(self, kwargs)
398
399 elif len(args) == 1:
400 if kwargs:
401 self.first = self.type(args[0])
402 handle_kwargs(self, kwargs)
403 elif isinstance(args[0], Range):
404 self.first = self.type(args[0].first)
405 self.second = self.type(args[0].second)
406 else:
407 self.first = self.type(0)
408 self.second = self.type(args[0]) - 1
409
410 elif len(args) == 2:
411 self.first = self.type(args[0])
412 self.second = self.type(args[1])
413 else:
414 raise TypeError, "Too many arguments specified"
415
416 if kwargs:
417 raise TypeError, "too many keywords: %s" % kwargs.keys()
418
419 def __str__(self):
420 return '%s:%s' % (self.first, self.second)
421
422class AddrRange(Range):
423 type = Addr
424
425class TickRange(Range):
426 type = Tick
427
428# Boolean parameter type. Python doesn't let you subclass bool, since
429# it doesn't want to let you create multiple instances of True and
430# False. Thus this is a little more complicated than String.
431class Bool(ParamValue):
432 cxx_type = 'bool'
433 def __init__(self, value):
434 try:
435 self.value = convert.toBool(value)
436 except TypeError:
437 self.value = bool(value)
438
439 def __str__(self):
440 return str(self.value)
441
442 def ini_str(self):
443 if self.value:
444 return 'true'
445 return 'false'
446
447def IncEthernetAddr(addr, val = 1):
448 bytes = map(lambda x: int(x, 16), addr.split(':'))
449 bytes[5] += val
450 for i in (5, 4, 3, 2, 1):
451 val,rem = divmod(bytes[i], 256)
452 bytes[i] = rem
453 if val == 0:
454 break
455 bytes[i - 1] += val
456 assert(bytes[0] <= 255)
457 return ':'.join(map(lambda x: '%02x' % x, bytes))
458
459class NextEthernetAddr(object):
460 addr = "00:90:00:00:00:01"
461
462 def __init__(self, inc = 1):
463 self.value = NextEthernetAddr.addr
464 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
465
466class EthernetAddr(ParamValue):
467 cxx_type = 'Net::EthAddr'
468 cxx_predecls = ['#include "base/inet.hh"']
469 swig_predecls = ['class Net::EthAddr;']
470 def __init__(self, value):
471 if value == NextEthernetAddr:
472 self.value = value
473 return
474
475 if not isinstance(value, str):
476 raise TypeError, "expected an ethernet address and didn't get one"
477
478 bytes = value.split(':')
479 if len(bytes) != 6:
480 raise TypeError, 'invalid ethernet address %s' % value
481
482 for byte in bytes:
483 if not 0 <= int(byte) <= 256:
484 raise TypeError, 'invalid ethernet address %s' % value
485
486 self.value = value
487
488 def unproxy(self, base):
489 if self.value == NextEthernetAddr:
490 self.addr = self.value().value
491 return self
492
493 def __str__(self):
494 if self.value == NextEthernetAddr:
495 if hasattr(self, 'addr'):
496 return self.addr
497 else:
498 return "NextEthernetAddr (unresolved)"
499 else:
500 return self.value
501
502# Enumerated types are a little more complex. The user specifies the
503# type as Enum(foo) where foo is either a list or dictionary of
504# alternatives (typically strings, but not necessarily so). (In the
505# long run, the integer value of the parameter will be the list index
506# or the corresponding dictionary value. For now, since we only check
507# that the alternative is valid and then spit it into a .ini file,
508# there's not much point in using the dictionary.)
509
510# What Enum() must do is generate a new type encapsulating the
511# provided list/dictionary so that specific values of the parameter
512# can be instances of that type. We define two hidden internal
513# classes (_ListEnum and _DictEnum) to serve as base classes, then
514# derive the new type from the appropriate base class on the fly.
515
516
517# Metaclass for Enum types
518class MetaEnum(type):
519 def __init__(cls, name, bases, init_dict):
520 if init_dict.has_key('map'):
521 if not isinstance(cls.map, dict):
522 raise TypeError, "Enum-derived class attribute 'map' " \
523 "must be of type dict"
524 # build list of value strings from map
525 cls.vals = cls.map.keys()
526 cls.vals.sort()
527 elif init_dict.has_key('vals'):
528 if not isinstance(cls.vals, list):
529 raise TypeError, "Enum-derived class attribute 'vals' " \
530 "must be of type list"
531 # build string->value map from vals sequence
532 cls.map = {}
533 for idx,val in enumerate(cls.vals):
534 cls.map[val] = idx
535 else:
536 raise TypeError, "Enum-derived class must define "\
537 "attribute 'map' or 'vals'"
538
539 cls.cxx_type = name + '::Enum'
540
541 super(MetaEnum, cls).__init__(name, bases, init_dict)
542
543 # Generate C++ class declaration for this enum type.
544 # Note that we wrap the enum in a class/struct to act as a namespace,
545 # so that the enum strings can be brief w/o worrying about collisions.
546 def cxx_decl(cls):
547 s = 'struct %s {\n enum Enum {\n ' % cls.__name__
548 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
549 s += '\n };\n};\n'
550 return s
551
552# Base class for enum types.
553class Enum(ParamValue):
554 __metaclass__ = MetaEnum
555 vals = []
556
557 def __init__(self, value):
558 if value not in self.map:
559 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
560 % (value, self.vals)
561 self.value = value
562
563 def __str__(self):
564 return self.value
565
566ticks_per_sec = None
567
568# how big does a rounding error need to be before we warn about it?
569frequency_tolerance = 0.001 # 0.1%
570
571# convert a floting-point # of ticks to integer, and warn if rounding
572# discards too much precision
573def tick_check(float_ticks):
574 if float_ticks == 0:
575 return 0
576 int_ticks = int(round(float_ticks))
577 err = (float_ticks - int_ticks) / float_ticks
578 if err > frequency_tolerance:
579 print >> sys.stderr, "Warning: rounding error > tolerance"
580 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
581 #raise ValueError
582 return int_ticks
583
584def getLatency(value):
585 if isinstance(value, Latency) or isinstance(value, Clock):
586 return value.value
587 elif isinstance(value, Frequency) or isinstance(value, RootClock):
588 return 1 / value.value
589 elif isinstance(value, str):
590 try:
591 return convert.toLatency(value)
592 except ValueError:
593 try:
594 return 1 / convert.toFrequency(value)
595 except ValueError:
596 pass # fall through
597 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
598
599
600class Latency(NumericParamValue):
601 cxx_type = 'Tick'
602 cxx_predecls = ['#include "sim/host.hh"']
603 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
604 '%import "sim/host.hh"']
605 def __init__(self, value):
606 self.value = getLatency(value)
607
608 def __getattr__(self, attr):
609 if attr in ('latency', 'period'):
610 return self
611 if attr == 'frequency':
612 return Frequency(self)
613 raise AttributeError, "Latency object has no attribute '%s'" % attr
614
615 # convert latency to ticks
616 def ini_str(self):
617 return str(tick_check(self.value * ticks_per_sec))
618
619class Frequency(NumericParamValue):
620 cxx_type = 'Tick'
621 cxx_predecls = ['#include "sim/host.hh"']
622 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
623 '%import "sim/host.hh"']
624 def __init__(self, value):
625 self.value = 1 / getLatency(value)
626
627 def __getattr__(self, attr):
628 if attr == 'frequency':
629 return self
630 if attr in ('latency', 'period'):
631 return Latency(self)
632 raise AttributeError, "Frequency object has no attribute '%s'" % attr
633
634 # convert frequency to ticks per period
635 def ini_str(self):
636 return self.period.ini_str()
637
638# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
639# We can't inherit from Frequency because we don't want it to be directly
640# assignable to a regular Frequency parameter.
641class RootClock(ParamValue):
642 cxx_type = 'Tick'
643 cxx_predecls = ['#include "sim/host.hh"']
644 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
645 '%import "sim/host.hh"']
646 def __init__(self, value):
647 self.value = 1 / getLatency(value)
648
649 def __getattr__(self, attr):
650 if attr == 'frequency':
651 return Frequency(self)
652 if attr in ('latency', 'period'):
653 return Latency(self)
654 raise AttributeError, "Frequency object has no attribute '%s'" % attr
655
656 def ini_str(self):
657 return str(tick_check(self.value))
658
659# A generic frequency and/or Latency value. Value is stored as a latency,
660# but to avoid ambiguity this object does not support numeric ops (* or /).
661# An explicit conversion to a Latency or Frequency must be made first.
662class Clock(ParamValue):
663 cxx_type = 'Tick'
664 cxx_predecls = ['#include "sim/host.hh"']
665 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
666 '%import "sim/host.hh"']
667 def __init__(self, value):
668 self.value = getLatency(value)
669
670 def __getattr__(self, attr):
671 if attr == 'frequency':
672 return Frequency(self)
673 if attr in ('latency', 'period'):
674 return Latency(self)
675 raise AttributeError, "Frequency object has no attribute '%s'" % attr
676
677 def ini_str(self):
678 return self.period.ini_str()
679
680class NetworkBandwidth(float,ParamValue):
681 cxx_type = 'float'
682 def __new__(cls, value):
683 val = convert.toNetworkBandwidth(value) / 8.0
684 return super(cls, NetworkBandwidth).__new__(cls, val)
685
686 def __str__(self):
687 return str(self.val)
688
689 def ini_str(self):
690 return '%f' % (ticks_per_sec / float(self))
691
692class MemoryBandwidth(float,ParamValue):
693 cxx_type = 'float'
694 def __new__(self, value):
695 val = convert.toMemoryBandwidth(value)
696 return super(cls, MemoryBandwidth).__new__(cls, val)
697
698 def __str__(self):
699 return str(self.val)
700
701 def ini_str(self):
702 return '%f' % (ticks_per_sec / float(self))
703
704#
705# "Constants"... handy aliases for various values.
706#
707
708# Special class for NULL pointers. Note the special check in
709# make_param_value() above that lets these be assigned where a
710# SimObject is required.
711# only one copy of a particular node
712class NullSimObject(object):
713 __metaclass__ = Singleton
714
715 def __call__(cls):
716 return cls
717
718 def _instantiate(self, parent = None, path = ''):
719 pass
720
721 def ini_str(self):
722 return 'Null'
723
724 def unproxy(self, base):
725 return self
726
727 def set_path(self, parent, name):
728 pass
729 def __str__(self):
730 return 'Null'
731
732# The only instance you'll ever need...
733NULL = NullSimObject()
734
735def isNullPointer(value):
736 return isinstance(value, NullSimObject)
737
738# Some memory range specifications use this as a default upper bound.
739MaxAddr = Addr.max
740MaxTick = Tick.max
741AllMemory = AddrRange(0, MaxAddr)
742
743
744#####################################################################
745#
746# Port objects
747#
748# Ports are used to interconnect objects in the memory system.
749#
750#####################################################################
751
752# Port reference: encapsulates a reference to a particular port on a
753# particular SimObject.
754class PortRef(object):
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
373
374class MetaRange(type):
375 def __init__(cls, name, bases, dict):
376 super(MetaRange, cls).__init__(name, bases, dict)
377 if name == 'Range':
378 return
379 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
380 cls.cxx_predecls = \
381 ['#include "base/range.hh"'] + cls.type.cxx_predecls
382
383class Range(ParamValue):
384 __metaclass__ = MetaRange
385 type = Int # default; can be overridden in subclasses
386 def __init__(self, *args, **kwargs):
387 def handle_kwargs(self, kwargs):
388 if 'end' in kwargs:
389 self.second = self.type(kwargs.pop('end'))
390 elif 'size' in kwargs:
391 self.second = self.first + self.type(kwargs.pop('size')) - 1
392 else:
393 raise TypeError, "Either end or size must be specified"
394
395 if len(args) == 0:
396 self.first = self.type(kwargs.pop('start'))
397 handle_kwargs(self, kwargs)
398
399 elif len(args) == 1:
400 if kwargs:
401 self.first = self.type(args[0])
402 handle_kwargs(self, kwargs)
403 elif isinstance(args[0], Range):
404 self.first = self.type(args[0].first)
405 self.second = self.type(args[0].second)
406 else:
407 self.first = self.type(0)
408 self.second = self.type(args[0]) - 1
409
410 elif len(args) == 2:
411 self.first = self.type(args[0])
412 self.second = self.type(args[1])
413 else:
414 raise TypeError, "Too many arguments specified"
415
416 if kwargs:
417 raise TypeError, "too many keywords: %s" % kwargs.keys()
418
419 def __str__(self):
420 return '%s:%s' % (self.first, self.second)
421
422class AddrRange(Range):
423 type = Addr
424
425class TickRange(Range):
426 type = Tick
427
428# Boolean parameter type. Python doesn't let you subclass bool, since
429# it doesn't want to let you create multiple instances of True and
430# False. Thus this is a little more complicated than String.
431class Bool(ParamValue):
432 cxx_type = 'bool'
433 def __init__(self, value):
434 try:
435 self.value = convert.toBool(value)
436 except TypeError:
437 self.value = bool(value)
438
439 def __str__(self):
440 return str(self.value)
441
442 def ini_str(self):
443 if self.value:
444 return 'true'
445 return 'false'
446
447def IncEthernetAddr(addr, val = 1):
448 bytes = map(lambda x: int(x, 16), addr.split(':'))
449 bytes[5] += val
450 for i in (5, 4, 3, 2, 1):
451 val,rem = divmod(bytes[i], 256)
452 bytes[i] = rem
453 if val == 0:
454 break
455 bytes[i - 1] += val
456 assert(bytes[0] <= 255)
457 return ':'.join(map(lambda x: '%02x' % x, bytes))
458
459class NextEthernetAddr(object):
460 addr = "00:90:00:00:00:01"
461
462 def __init__(self, inc = 1):
463 self.value = NextEthernetAddr.addr
464 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
465
466class EthernetAddr(ParamValue):
467 cxx_type = 'Net::EthAddr'
468 cxx_predecls = ['#include "base/inet.hh"']
469 swig_predecls = ['class Net::EthAddr;']
470 def __init__(self, value):
471 if value == NextEthernetAddr:
472 self.value = value
473 return
474
475 if not isinstance(value, str):
476 raise TypeError, "expected an ethernet address and didn't get one"
477
478 bytes = value.split(':')
479 if len(bytes) != 6:
480 raise TypeError, 'invalid ethernet address %s' % value
481
482 for byte in bytes:
483 if not 0 <= int(byte) <= 256:
484 raise TypeError, 'invalid ethernet address %s' % value
485
486 self.value = value
487
488 def unproxy(self, base):
489 if self.value == NextEthernetAddr:
490 self.addr = self.value().value
491 return self
492
493 def __str__(self):
494 if self.value == NextEthernetAddr:
495 if hasattr(self, 'addr'):
496 return self.addr
497 else:
498 return "NextEthernetAddr (unresolved)"
499 else:
500 return self.value
501
502# Enumerated types are a little more complex. The user specifies the
503# type as Enum(foo) where foo is either a list or dictionary of
504# alternatives (typically strings, but not necessarily so). (In the
505# long run, the integer value of the parameter will be the list index
506# or the corresponding dictionary value. For now, since we only check
507# that the alternative is valid and then spit it into a .ini file,
508# there's not much point in using the dictionary.)
509
510# What Enum() must do is generate a new type encapsulating the
511# provided list/dictionary so that specific values of the parameter
512# can be instances of that type. We define two hidden internal
513# classes (_ListEnum and _DictEnum) to serve as base classes, then
514# derive the new type from the appropriate base class on the fly.
515
516
517# Metaclass for Enum types
518class MetaEnum(type):
519 def __init__(cls, name, bases, init_dict):
520 if init_dict.has_key('map'):
521 if not isinstance(cls.map, dict):
522 raise TypeError, "Enum-derived class attribute 'map' " \
523 "must be of type dict"
524 # build list of value strings from map
525 cls.vals = cls.map.keys()
526 cls.vals.sort()
527 elif init_dict.has_key('vals'):
528 if not isinstance(cls.vals, list):
529 raise TypeError, "Enum-derived class attribute 'vals' " \
530 "must be of type list"
531 # build string->value map from vals sequence
532 cls.map = {}
533 for idx,val in enumerate(cls.vals):
534 cls.map[val] = idx
535 else:
536 raise TypeError, "Enum-derived class must define "\
537 "attribute 'map' or 'vals'"
538
539 cls.cxx_type = name + '::Enum'
540
541 super(MetaEnum, cls).__init__(name, bases, init_dict)
542
543 # Generate C++ class declaration for this enum type.
544 # Note that we wrap the enum in a class/struct to act as a namespace,
545 # so that the enum strings can be brief w/o worrying about collisions.
546 def cxx_decl(cls):
547 s = 'struct %s {\n enum Enum {\n ' % cls.__name__
548 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
549 s += '\n };\n};\n'
550 return s
551
552# Base class for enum types.
553class Enum(ParamValue):
554 __metaclass__ = MetaEnum
555 vals = []
556
557 def __init__(self, value):
558 if value not in self.map:
559 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
560 % (value, self.vals)
561 self.value = value
562
563 def __str__(self):
564 return self.value
565
566ticks_per_sec = None
567
568# how big does a rounding error need to be before we warn about it?
569frequency_tolerance = 0.001 # 0.1%
570
571# convert a floting-point # of ticks to integer, and warn if rounding
572# discards too much precision
573def tick_check(float_ticks):
574 if float_ticks == 0:
575 return 0
576 int_ticks = int(round(float_ticks))
577 err = (float_ticks - int_ticks) / float_ticks
578 if err > frequency_tolerance:
579 print >> sys.stderr, "Warning: rounding error > tolerance"
580 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
581 #raise ValueError
582 return int_ticks
583
584def getLatency(value):
585 if isinstance(value, Latency) or isinstance(value, Clock):
586 return value.value
587 elif isinstance(value, Frequency) or isinstance(value, RootClock):
588 return 1 / value.value
589 elif isinstance(value, str):
590 try:
591 return convert.toLatency(value)
592 except ValueError:
593 try:
594 return 1 / convert.toFrequency(value)
595 except ValueError:
596 pass # fall through
597 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
598
599
600class Latency(NumericParamValue):
601 cxx_type = 'Tick'
602 cxx_predecls = ['#include "sim/host.hh"']
603 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
604 '%import "sim/host.hh"']
605 def __init__(self, value):
606 self.value = getLatency(value)
607
608 def __getattr__(self, attr):
609 if attr in ('latency', 'period'):
610 return self
611 if attr == 'frequency':
612 return Frequency(self)
613 raise AttributeError, "Latency object has no attribute '%s'" % attr
614
615 # convert latency to ticks
616 def ini_str(self):
617 return str(tick_check(self.value * ticks_per_sec))
618
619class Frequency(NumericParamValue):
620 cxx_type = 'Tick'
621 cxx_predecls = ['#include "sim/host.hh"']
622 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
623 '%import "sim/host.hh"']
624 def __init__(self, value):
625 self.value = 1 / getLatency(value)
626
627 def __getattr__(self, attr):
628 if attr == 'frequency':
629 return self
630 if attr in ('latency', 'period'):
631 return Latency(self)
632 raise AttributeError, "Frequency object has no attribute '%s'" % attr
633
634 # convert frequency to ticks per period
635 def ini_str(self):
636 return self.period.ini_str()
637
638# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
639# We can't inherit from Frequency because we don't want it to be directly
640# assignable to a regular Frequency parameter.
641class RootClock(ParamValue):
642 cxx_type = 'Tick'
643 cxx_predecls = ['#include "sim/host.hh"']
644 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
645 '%import "sim/host.hh"']
646 def __init__(self, value):
647 self.value = 1 / getLatency(value)
648
649 def __getattr__(self, attr):
650 if attr == 'frequency':
651 return Frequency(self)
652 if attr in ('latency', 'period'):
653 return Latency(self)
654 raise AttributeError, "Frequency object has no attribute '%s'" % attr
655
656 def ini_str(self):
657 return str(tick_check(self.value))
658
659# A generic frequency and/or Latency value. Value is stored as a latency,
660# but to avoid ambiguity this object does not support numeric ops (* or /).
661# An explicit conversion to a Latency or Frequency must be made first.
662class Clock(ParamValue):
663 cxx_type = 'Tick'
664 cxx_predecls = ['#include "sim/host.hh"']
665 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
666 '%import "sim/host.hh"']
667 def __init__(self, value):
668 self.value = getLatency(value)
669
670 def __getattr__(self, attr):
671 if attr == 'frequency':
672 return Frequency(self)
673 if attr in ('latency', 'period'):
674 return Latency(self)
675 raise AttributeError, "Frequency object has no attribute '%s'" % attr
676
677 def ini_str(self):
678 return self.period.ini_str()
679
680class NetworkBandwidth(float,ParamValue):
681 cxx_type = 'float'
682 def __new__(cls, value):
683 val = convert.toNetworkBandwidth(value) / 8.0
684 return super(cls, NetworkBandwidth).__new__(cls, val)
685
686 def __str__(self):
687 return str(self.val)
688
689 def ini_str(self):
690 return '%f' % (ticks_per_sec / float(self))
691
692class MemoryBandwidth(float,ParamValue):
693 cxx_type = 'float'
694 def __new__(self, value):
695 val = convert.toMemoryBandwidth(value)
696 return super(cls, MemoryBandwidth).__new__(cls, val)
697
698 def __str__(self):
699 return str(self.val)
700
701 def ini_str(self):
702 return '%f' % (ticks_per_sec / float(self))
703
704#
705# "Constants"... handy aliases for various values.
706#
707
708# Special class for NULL pointers. Note the special check in
709# make_param_value() above that lets these be assigned where a
710# SimObject is required.
711# only one copy of a particular node
712class NullSimObject(object):
713 __metaclass__ = Singleton
714
715 def __call__(cls):
716 return cls
717
718 def _instantiate(self, parent = None, path = ''):
719 pass
720
721 def ini_str(self):
722 return 'Null'
723
724 def unproxy(self, base):
725 return self
726
727 def set_path(self, parent, name):
728 pass
729 def __str__(self):
730 return 'Null'
731
732# The only instance you'll ever need...
733NULL = NullSimObject()
734
735def isNullPointer(value):
736 return isinstance(value, NullSimObject)
737
738# Some memory range specifications use this as a default upper bound.
739MaxAddr = Addr.max
740MaxTick = Tick.max
741AllMemory = AddrRange(0, MaxAddr)
742
743
744#####################################################################
745#
746# Port objects
747#
748# Ports are used to interconnect objects in the memory system.
749#
750#####################################################################
751
752# Port reference: encapsulates a reference to a particular port on a
753# particular SimObject.
754class PortRef(object):
755 def __init__(self, simobj, name, isVec):
756 assert(isSimObject(simobj))
755 def __init__(self, simobj, name):
756 assert(isSimObject(simobj) or isSimObjectClass(simobj))
757 self.simobj = simobj
758 self.name = name
757 self.simobj = simobj
758 self.name = name
759 self.index = -1
760 self.isVec = isVec # is this a vector port?
761 self.peer = None # not associated with another port yet
762 self.ccConnected = False # C++ port connection done?
759 self.peer = None # not associated with another port yet
760 self.ccConnected = False # C++ port connection done?
761 self.index = -1 # always -1 for non-vector ports
763
764 def __str__(self):
762
763 def __str__(self):
765 ext = ''
766 if self.isVec:
767 ext = '[%d]' % self.index
768 return '%s.%s%s' % (self.simobj.path(), self.name, ext)
764 return '%s.%s' % (self.simobj, self.name)
769
765
770 # Set peer port reference. Called via __setattr__ as a result of
771 # a port assignment, e.g., "obj1.port1 = obj2.port2".
772 def setPeer(self, other):
773 if self.isVec:
774 curMap = self.simobj._port_map.get(self.name, [])
775 self.index = len(curMap)
776 curMap.append(other)
777 else:
778 curMap = self.simobj._port_map.get(self.name)
779 if curMap and not self.isVec:
780 print "warning: overwriting port", self.simobj, self.name
781 curMap = other
782 self.simobj._port_map[self.name] = curMap
766 # for config.ini, print peer's name (not ours)
767 def ini_str(self):
768 return str(self.peer)
769
770 def __getattr__(self, attr):
771 if attr == 'peerObj':
772 # shorthand for proxies
773 return self.peer.simobj
774 raise AttributeError, "'%s' object has no attribute '%s'" % \
775 (self.__class__.__name__, attr)
776
777 # Full connection is symmetric (both ways). Called via
778 # SimObject.__setattr__ as a result of a port assignment, e.g.,
779 # "obj1.portA = obj2.portB", or via VectorPortRef.__setitem__,
780 # e.g., "obj1.portA[3] = obj2.portB".
781 def connect(self, other):
782 if isinstance(other, VectorPortRef):
783 # reference to plain VectorPort is implicit append
784 other = other._get_next()
785 if not (isinstance(other, PortRef) or proxy.isproxy(other)):
786 raise TypeError, \
787 "assigning non-port reference '%s' to port '%s'" \
788 % (other, self)
789 if self.peer and not proxy.isproxy(self.peer):
790 print "warning: overwriting port", self, \
791 "value", self.peer, "with", other
783 self.peer = other
792 self.peer = other
793 assert(not isinstance(self.peer, VectorPortRef))
794 if isinstance(other, PortRef) and other.peer is not self:
795 other.connect(self)
784
796
785 def clone(self, memo):
797 def clone(self, simobj, memo):
798 if memo.has_key(self):
799 return memo[self]
786 newRef = copy.copy(self)
800 newRef = copy.copy(self)
801 memo[self] = newRef
802 newRef.simobj = simobj
787 assert(isSimObject(newRef.simobj))
803 assert(isSimObject(newRef.simobj))
788 newRef.simobj = newRef.simobj(_memo=memo)
789 # Tricky: if I'm the *second* PortRef in the pair to be
790 # cloned, then my peer is still in the middle of its clone
791 # method, and thus hasn't returned to its owner's
792 # SimObject.__init__ to get installed in _port_map. As a
793 # result I have no way of finding the *new* peer object. So I
794 # mark myself as "waiting" for my peer, and I let the *first*
795 # PortRef clone call set up both peer pointers after I return.
796 newPeer = newRef.simobj._port_map.get(self.name)
797 if newPeer:
798 if self.isVec:
799 assert(self.index != -1)
800 newPeer = newPeer[self.index]
801 # other guy is all set up except for his peer pointer
802 assert(newPeer.peer == -1) # peer must be waiting for handshake
803 newPeer.peer = newRef
804 newRef.peer = newPeer
805 else:
806 # other guy is in clone; just wait for him to do the work
807 newRef.peer = -1 # mark as waiting for handshake
804 if self.peer and not proxy.isproxy(self.peer):
805 peerObj = memo[self.peer.simobj]
806 newRef.peer = self.peer.clone(peerObj, memo)
807 assert(not isinstance(newRef.peer, VectorPortRef))
808 return newRef
809
808 return newRef
809
810 def unproxy(self, simobj):
811 assert(simobj is self.simobj)
812 if proxy.isproxy(self.peer):
813 try:
814 realPeer = self.peer.unproxy(self.simobj)
815 except:
816 print "Error in unproxying port '%s' of %s" % \
817 (self.name, self.simobj.path())
818 raise
819 self.connect(realPeer)
820
810 # Call C++ to create corresponding port connection between C++ objects
811 def ccConnect(self):
812 if self.ccConnected: # already done this
813 return
814 peer = self.peer
815 cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
816 peer.simobj.getCCObject(), peer.name, peer.index)
817 self.ccConnected = True
818 peer.ccConnected = True
819
821 # Call C++ to create corresponding port connection between C++ objects
822 def ccConnect(self):
823 if self.ccConnected: # already done this
824 return
825 peer = self.peer
826 cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
827 peer.simobj.getCCObject(), peer.name, peer.index)
828 self.ccConnected = True
829 peer.ccConnected = True
830
831# A reference to an individual element of a VectorPort... much like a
832# PortRef, but has an index.
833class VectorPortElementRef(PortRef):
834 def __init__(self, simobj, name, index):
835 PortRef.__init__(self, simobj, name)
836 self.index = index
837
838 def __str__(self):
839 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
840
841# A reference to a complete vector-valued port (not just a single element).
842# Can be indexed to retrieve individual VectorPortElementRef instances.
843class VectorPortRef(object):
844 def __init__(self, simobj, name):
845 assert(isSimObject(simobj) or isSimObjectClass(simobj))
846 self.simobj = simobj
847 self.name = name
848 self.elements = []
849
850 # for config.ini, print peer's name (not ours)
851 def ini_str(self):
852 return ' '.join([el.ini_str() for el in self.elements])
853
854 def __getitem__(self, key):
855 if not isinstance(key, int):
856 raise TypeError, "VectorPort index must be integer"
857 if key >= len(self.elements):
858 # need to extend list
859 ext = [VectorPortElementRef(self.simobj, self.name, i)
860 for i in range(len(self.elements), key+1)]
861 self.elements.extend(ext)
862 return self.elements[key]
863
864 def _get_next(self):
865 return self[len(self.elements)]
866
867 def __setitem__(self, key, value):
868 if not isinstance(key, int):
869 raise TypeError, "VectorPort index must be integer"
870 self[key].connect(value)
871
872 def connect(self, other):
873 # reference to plain VectorPort is implicit append
874 self._get_next().connect(other)
875
876 def unproxy(self, simobj):
877 [el.unproxy(simobj) for el in self.elements]
878
879 def ccConnect(self):
880 [el.ccConnect() for el in self.elements]
881
820# Port description object. Like a ParamDesc object, this represents a
821# logical port in the SimObject class, not a particular port on a
822# SimObject instance. The latter are represented by PortRef objects.
823class Port(object):
882# Port description object. Like a ParamDesc object, this represents a
883# logical port in the SimObject class, not a particular port on a
884# SimObject instance. The latter are represented by PortRef objects.
885class Port(object):
824 def __init__(self, desc):
825 self.desc = desc
826 self.isVec = False
886 # Port("description") or Port(default, "description")
887 def __init__(self, *args):
888 if len(args) == 1:
889 self.desc = args[0]
890 elif len(args) == 2:
891 self.default = args[0]
892 self.desc = args[1]
893 else:
894 raise TypeError, 'wrong number of arguments'
895 # self.name is set by SimObject class on assignment
896 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
827
828 # Generate a PortRef for this port on the given SimObject with the
829 # given name
897
898 # Generate a PortRef for this port on the given SimObject with the
899 # given name
830 def makeRef(self, simobj, name):
831 return PortRef(simobj, name, self.isVec)
900 def makeRef(self, simobj):
901 return PortRef(simobj, self.name)
832
833 # Connect an instance of this port (on the given SimObject with
834 # the given name) with the port described by the supplied PortRef
902
903 # Connect an instance of this port (on the given SimObject with
904 # the given name) with the port described by the supplied PortRef
835 def connect(self, simobj, name, ref):
836 if not isinstance(ref, PortRef):
837 raise TypeError, \
838 "assigning non-port reference port '%s'" % name
839 myRef = self.makeRef(simobj, name)
840 myRef.setPeer(ref)
841 ref.setPeer(myRef)
905 def connect(self, simobj, ref):
906 self.makeRef(simobj).connect(ref)
842
843# VectorPort description object. Like Port, but represents a vector
844# of connections (e.g., as on a Bus).
845class VectorPort(Port):
907
908# VectorPort description object. Like Port, but represents a vector
909# of connections (e.g., as on a Bus).
910class VectorPort(Port):
846 def __init__(self, desc):
847 Port.__init__(self, desc)
911 def __init__(self, *args):
912 Port.__init__(self, *args)
848 self.isVec = True
849
913 self.isVec = True
914
915 def makeRef(self, simobj):
916 return VectorPortRef(simobj, self.name)
850
917
918
919
851__all__ = ['Param', 'VectorParam',
852 'Enum', 'Bool', 'String', 'Float',
853 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
854 'Int32', 'UInt32', 'Int64', 'UInt64',
855 'Counter', 'Addr', 'Tick', 'Percent',
856 'TcpPort', 'UdpPort', 'EthernetAddr',
857 'MemorySize', 'MemorySize32',
858 'Latency', 'Frequency', 'RootClock', 'Clock',
859 'NetworkBandwidth', 'MemoryBandwidth',
860 'Range', 'AddrRange', 'TickRange',
861 'MaxAddr', 'MaxTick', 'AllMemory',
862 'NextEthernetAddr', 'NULL',
863 'Port', 'VectorPort']
864
865# see comment on imports at end of __init__.py.
866from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
867import proxy
868import objects
869import cc_main
920__all__ = ['Param', 'VectorParam',
921 'Enum', 'Bool', 'String', 'Float',
922 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
923 'Int32', 'UInt32', 'Int64', 'UInt64',
924 'Counter', 'Addr', 'Tick', 'Percent',
925 'TcpPort', 'UdpPort', 'EthernetAddr',
926 'MemorySize', 'MemorySize32',
927 'Latency', 'Frequency', 'RootClock', 'Clock',
928 'NetworkBandwidth', 'MemoryBandwidth',
929 'Range', 'AddrRange', 'TickRange',
930 'MaxAddr', 'MaxTick', 'AllMemory',
931 'NextEthernetAddr', 'NULL',
932 'Port', 'VectorPort']
933
934# see comment on imports at end of __init__.py.
935from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
936import proxy
937import objects
938import cc_main