1 # Copyright (c) 2004-2006 The Regents of The University of Michigan
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.
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.
27 # Authors: Steve Reinhardt
30 #####################################################################
32 # Parameter description classes
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
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.
45 #####################################################################
57 # Dummy base class to identify types that are legitimate for SimObject
59 class ParamValue(object):
64 # default for printing to .ini file is regular string conversion.
65 # will be overridden in some cases
69 # allows us to blithely call unproxy() on things without checking
70 # if they're really proxies or not
71 def unproxy(self
, base
):
74 # Regular parameter description.
75 class ParamDesc(object):
76 def __init__(self
, ptype_str
, ptype
, *args
, **kwargs
):
77 self
.ptype_str
= ptype_str
78 # remember ptype only if it is provided
86 self
.default
= args
[0]
89 raise TypeError, 'too many arguments'
91 if kwargs
.has_key('desc'):
92 assert(not hasattr(self
, 'desc'))
93 self
.desc
= kwargs
['desc']
96 if kwargs
.has_key('default'):
97 assert(not hasattr(self
, 'default'))
98 self
.default
= kwargs
['default']
102 raise TypeError, 'extra unknown kwargs %s' % kwargs
104 if not hasattr(self
, 'desc'):
105 raise TypeError, 'desc attribute missing'
107 def __getattr__(self
, attr
):
110 ptype
= eval(self
.ptype_str
, objects
.__dict
__)
111 if not isinstance(ptype
, type):
117 "Param qualifier '%s' is not a type" % self
.ptype_str
118 raise AttributeError, "'%s' object has no attribute '%s'" % \
119 (type(self
).__name
__, attr
)
121 def convert(self
, value
):
122 if isinstance(value
, proxy
.BaseProxy
):
123 value
.set_param_desc(self
)
125 if not hasattr(self
, 'ptype') and isNullPointer(value
):
126 # deferred evaluation of SimObject; continue to defer if
127 # we're just assigning a null pointer
129 if isinstance(value
, self
.ptype
):
131 if isNullPointer(value
) and isSimObjectClass(self
.ptype
):
133 return self
.ptype(value
)
135 def cxx_predecls(self
):
136 return self
.ptype
.cxx_predecls
138 def swig_predecls(self
):
139 return self
.ptype
.swig_predecls
142 return '%s %s;' % (self
.ptype
.cxx_type
, self
.name
)
144 # Vector-valued parameter description. Just like ParamDesc, except
145 # that the value is a vector (list) of the specified type instead of a
148 class VectorParamValue(list):
150 return ' '.join([v
.ini_str() for v
in self
])
152 def unproxy(self
, base
):
153 return [v
.unproxy(base
) for v
in self
]
155 class SimObjVector(VectorParamValue
):
160 class VectorParamDesc(ParamDesc
):
161 # Convert assigned value to appropriate type. If the RHS is not a
162 # list or tuple, it generates a single-element list.
163 def convert(self
, value
):
164 if isinstance(value
, (list, tuple)):
165 # list: coerce each element into new list
166 tmp_list
= [ ParamDesc
.convert(self
, v
) for v
in value
]
167 if isSimObjectSequence(tmp_list
):
168 return SimObjVector(tmp_list
)
170 return VectorParamValue(tmp_list
)
172 # singleton: leave it be (could coerce to a single-element
173 # list here, but for some historical reason we don't...
174 return ParamDesc
.convert(self
, value
)
176 def cxx_predecls(self
):
177 return ['#include <vector>'] + self
.ptype
.cxx_predecls
179 def swig_predecls(self
):
180 return ['%include "std_vector.i"'] + self
.ptype
.swig_predecls
183 return 'std::vector< %s > %s;' % (self
.ptype
.cxx_type
, self
.name
)
185 class ParamFactory(object):
186 def __init__(self
, param_desc_class
, ptype_str
= None):
187 self
.param_desc_class
= param_desc_class
188 self
.ptype_str
= ptype_str
190 def __getattr__(self
, attr
):
192 attr
= self
.ptype_str
+ '.' + attr
193 return ParamFactory(self
.param_desc_class
, attr
)
195 # E.g., Param.Int(5, "number of widgets")
196 def __call__(self
, *args
, **kwargs
):
197 caller_frame
= inspect
.currentframe().f_back
200 ptype
= eval(self
.ptype_str
,
201 caller_frame
.f_globals
, caller_frame
.f_locals
)
202 if not isinstance(ptype
, type):
204 "Param qualifier is not a type: %s" % ptype
206 # if name isn't defined yet, assume it's a SimObject, and
207 # try to resolve it later
209 return self
.param_desc_class(self
.ptype_str
, ptype
, *args
, **kwargs
)
211 Param
= ParamFactory(ParamDesc
)
212 VectorParam
= ParamFactory(VectorParamDesc
)
214 #####################################################################
218 # Though native Python types could be used to specify parameter types
219 # (the 'ptype' field of the Param and VectorParam classes), it's more
220 # flexible to define our own set of types. This gives us more control
221 # over how Python expressions are converted to values (via the
222 # __init__() constructor) and how these values are printed out (via
223 # the __str__() conversion method).
225 #####################################################################
227 # String-valued parameter. Just mixin the ParamValue class with the
228 # built-in str class.
229 class String(ParamValue
,str):
230 cxx_type
= 'std::string'
231 cxx_predecls
= ['#include <string>']
232 swig_predecls
= ['%include "std_string.i"\n' +
233 '%apply const std::string& {std::string *};']
236 # superclass for "numeric" parameter values, to emulate math
237 # operations in a type-safe way. e.g., a Latency times an int returns
238 # a new Latency object.
239 class NumericParamValue(ParamValue
):
241 return str(self
.value
)
244 return float(self
.value
)
247 return long(self
.value
)
250 return int(self
.value
)
252 # hook for bounds checking
256 def __mul__(self
, other
):
257 newobj
= self
.__class
__(self
)
258 newobj
.value
*= other
264 def __div__(self
, other
):
265 newobj
= self
.__class
__(self
)
266 newobj
.value
/= other
270 def __sub__(self
, other
):
271 newobj
= self
.__class
__(self
)
272 newobj
.value
-= other
276 # Metaclass for bounds-checked integer parameters. See CheckedInt.
277 class CheckedIntType(type):
278 def __init__(cls
, name
, bases
, dict):
279 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
281 # CheckedInt is an abstract base class, so we actually don't
282 # want to do any processing on it... the rest of this code is
283 # just for classes that derive from CheckedInt.
284 if name
== 'CheckedInt':
287 if not cls
.cxx_predecls
:
288 # most derived types require this, so we just do it here once
289 cls
.cxx_predecls
= ['#include "sim/host.hh"']
291 if not cls
.swig_predecls
:
292 # most derived types require this, so we just do it here once
293 cls
.swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
294 '%import "sim/host.hh"']
296 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
297 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
298 panic("CheckedInt subclass %s must define either\n" \
299 " 'min' and 'max' or 'size' and 'unsigned'\n" \
303 cls
.max = 2 ** cls
.size
- 1
305 cls
.min = -(2 ** (cls
.size
- 1))
306 cls
.max = (2 ** (cls
.size
- 1)) - 1
308 # Abstract superclass for bounds-checked integer parameters. This
309 # class is subclassed to generate parameter classes with specific
310 # bounds. Initialization of the min and max bounds is done in the
311 # metaclass CheckedIntType.__init__.
312 class CheckedInt(NumericParamValue
):
313 __metaclass__
= CheckedIntType
316 if not self
.min <= self
.value
<= self
.max:
317 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
318 (self
.min, self
.value
, self
.max)
320 def __init__(self
, value
):
321 if isinstance(value
, str):
322 self
.value
= convert
.toInteger(value
)
323 elif isinstance(value
, (int, long, float, NumericParamValue
)):
324 self
.value
= long(value
)
326 raise TypeError, "Can't convert object of type %s to CheckedInt" \
327 % type(value
).__name
__
330 class Int(CheckedInt
): cxx_type
= 'int'; size
= 32; unsigned
= False
331 class Unsigned(CheckedInt
): cxx_type
= 'unsigned'; size
= 32; unsigned
= True
333 class Int8(CheckedInt
): cxx_type
= 'int8_t'; size
= 8; unsigned
= False
334 class UInt8(CheckedInt
): cxx_type
= 'uint8_t'; size
= 8; unsigned
= True
335 class Int16(CheckedInt
): cxx_type
= 'int16_t'; size
= 16; unsigned
= False
336 class UInt16(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
337 class Int32(CheckedInt
): cxx_type
= 'int32_t'; size
= 32; unsigned
= False
338 class UInt32(CheckedInt
): cxx_type
= 'uint32_t'; size
= 32; unsigned
= True
339 class Int64(CheckedInt
): cxx_type
= 'int64_t'; size
= 64; unsigned
= False
340 class UInt64(CheckedInt
): cxx_type
= 'uint64_t'; size
= 64; unsigned
= True
342 class Counter(CheckedInt
): cxx_type
= 'Counter'; size
= 64; unsigned
= True
343 class Tick(CheckedInt
): cxx_type
= 'Tick'; size
= 64; unsigned
= True
344 class TcpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
345 class UdpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
347 class Percent(CheckedInt
): cxx_type
= 'int'; min = 0; max = 100
349 class Float(ParamValue
, float):
352 class MemorySize(CheckedInt
):
353 cxx_type
= 'uint64_t'
356 def __init__(self
, value
):
357 if isinstance(value
, MemorySize
):
358 self
.value
= value
.value
360 self
.value
= convert
.toMemorySize(value
)
363 class MemorySize32(CheckedInt
):
364 cxx_type
= 'uint32_t'
367 def __init__(self
, value
):
368 if isinstance(value
, MemorySize
):
369 self
.value
= value
.value
371 self
.value
= convert
.toMemorySize(value
)
374 class Addr(CheckedInt
):
376 cxx_predecls
= ['#include "targetarch/isa_traits.hh"']
379 def __init__(self
, value
):
380 if isinstance(value
, Addr
):
381 self
.value
= value
.value
384 self
.value
= convert
.toMemorySize(value
)
386 self
.value
= long(value
)
388 def __add__(self
, other
):
389 if isinstance(other
, Addr
):
390 return self
.value
+ other
.value
392 return self
.value
+ other
395 class MetaRange(type):
396 def __init__(cls
, name
, bases
, dict):
397 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
400 cls
.cxx_type
= 'Range< %s >' % cls
.type.cxx_type
402 ['#include "base/range.hh"'] + cls
.type.cxx_predecls
404 class Range(ParamValue
):
405 __metaclass__
= MetaRange
406 type = Int
# default; can be overridden in subclasses
407 def __init__(self
, *args
, **kwargs
):
408 def handle_kwargs(self
, kwargs
):
410 self
.second
= self
.type(kwargs
.pop('end'))
411 elif 'size' in kwargs
:
412 self
.second
= self
.first
+ self
.type(kwargs
.pop('size')) - 1
414 raise TypeError, "Either end or size must be specified"
417 self
.first
= self
.type(kwargs
.pop('start'))
418 handle_kwargs(self
, kwargs
)
422 self
.first
= self
.type(args
[0])
423 handle_kwargs(self
, kwargs
)
424 elif isinstance(args
[0], Range
):
425 self
.first
= self
.type(args
[0].first
)
426 self
.second
= self
.type(args
[0].second
)
428 self
.first
= self
.type(0)
429 self
.second
= self
.type(args
[0]) - 1
432 self
.first
= self
.type(args
[0])
433 self
.second
= self
.type(args
[1])
435 raise TypeError, "Too many arguments specified"
438 raise TypeError, "too many keywords: %s" % kwargs
.keys()
441 return '%s:%s' % (self
.first
, self
.second
)
443 class AddrRange(Range
):
446 class TickRange(Range
):
449 # Boolean parameter type. Python doesn't let you subclass bool, since
450 # it doesn't want to let you create multiple instances of True and
451 # False. Thus this is a little more complicated than String.
452 class Bool(ParamValue
):
454 def __init__(self
, value
):
456 self
.value
= convert
.toBool(value
)
458 self
.value
= bool(value
)
461 return str(self
.value
)
468 def IncEthernetAddr(addr
, val
= 1):
469 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
471 for i
in (5, 4, 3, 2, 1):
472 val
,rem
= divmod(bytes
[i
], 256)
477 assert(bytes
[0] <= 255)
478 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
480 class NextEthernetAddr(object):
481 addr
= "00:90:00:00:00:01"
483 def __init__(self
, inc
= 1):
484 self
.value
= NextEthernetAddr
.addr
485 NextEthernetAddr
.addr
= IncEthernetAddr(NextEthernetAddr
.addr
, inc
)
487 class EthernetAddr(ParamValue
):
488 cxx_type
= 'Net::EthAddr'
489 cxx_predecls
= ['#include "base/inet.hh"']
490 swig_predecls
= ['class Net::EthAddr;']
491 def __init__(self
, value
):
492 if value
== NextEthernetAddr
:
496 if not isinstance(value
, str):
497 raise TypeError, "expected an ethernet address and didn't get one"
499 bytes
= value
.split(':')
501 raise TypeError, 'invalid ethernet address %s' % value
504 if not 0 <= int(byte
) <= 256:
505 raise TypeError, 'invalid ethernet address %s' % value
509 def unproxy(self
, base
):
510 if self
.value
== NextEthernetAddr
:
511 self
.addr
= self
.value().value
515 if self
.value
== NextEthernetAddr
:
516 if hasattr(self
, 'addr'):
519 return "NextEthernetAddr (unresolved)"
523 time_formats
= [ "%a %b %d %H:%M:%S %Z %Y",
524 "%a %b %d %H:%M:%S %Z %Y",
536 def parse_time(value
):
537 from time
import gmtime
, strptime
, struct_time
, time
538 from datetime
import datetime
, date
540 if isinstance(value
, struct_time
):
543 if isinstance(value
, (int, long)):
546 if isinstance(value
, (datetime
, date
)):
547 return value
.timetuple()
549 if isinstance(value
, str):
550 if value
in ('Now', 'Today'):
551 return time
.gmtime(time
.time())
553 for format
in time_formats
:
555 return strptime(value
, format
)
559 raise ValueError, "Could not parse '%s' as a time" % value
561 class Time(ParamValue
):
563 def __init__(self
, value
):
564 self
.value
= parse_time(value
)
568 return ' '.join([ str(tm
[i
]) for i
in xrange(8)])
573 # Enumerated types are a little more complex. The user specifies the
574 # type as Enum(foo) where foo is either a list or dictionary of
575 # alternatives (typically strings, but not necessarily so). (In the
576 # long run, the integer value of the parameter will be the list index
577 # or the corresponding dictionary value. For now, since we only check
578 # that the alternative is valid and then spit it into a .ini file,
579 # there's not much point in using the dictionary.)
581 # What Enum() must do is generate a new type encapsulating the
582 # provided list/dictionary so that specific values of the parameter
583 # can be instances of that type. We define two hidden internal
584 # classes (_ListEnum and _DictEnum) to serve as base classes, then
585 # derive the new type from the appropriate base class on the fly.
588 # Metaclass for Enum types
589 class MetaEnum(type):
590 def __init__(cls
, name
, bases
, init_dict
):
591 if init_dict
.has_key('map'):
592 if not isinstance(cls
.map, dict):
593 raise TypeError, "Enum-derived class attribute 'map' " \
594 "must be of type dict"
595 # build list of value strings from map
596 cls
.vals
= cls
.map.keys()
598 elif init_dict
.has_key('vals'):
599 if not isinstance(cls
.vals
, list):
600 raise TypeError, "Enum-derived class attribute 'vals' " \
601 "must be of type list"
602 # build string->value map from vals sequence
604 for idx
,val
in enumerate(cls
.vals
):
607 raise TypeError, "Enum-derived class must define "\
608 "attribute 'map' or 'vals'"
610 cls
.cxx_type
= name
+ '::Enum'
612 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
614 # Generate C++ class declaration for this enum type.
615 # Note that we wrap the enum in a class/struct to act as a namespace,
616 # so that the enum strings can be brief w/o worrying about collisions.
618 s
= 'struct %s {\n enum Enum {\n ' % cls
.__name
__
619 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
623 # Base class for enum types.
624 class Enum(ParamValue
):
625 __metaclass__
= MetaEnum
628 def __init__(self
, value
):
629 if value
not in self
.map:
630 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
637 # how big does a rounding error need to be before we warn about it?
638 frequency_tolerance
= 0.001 # 0.1%
640 class TickParamValue(NumericParamValue
):
642 cxx_predecls
= ['#include "sim/host.hh"']
643 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
644 '%import "sim/host.hh"']
646 class Latency(TickParamValue
):
647 def __init__(self
, value
):
648 if isinstance(value
, (Latency
, Clock
)):
649 self
.ticks
= value
.ticks
650 self
.value
= value
.value
651 elif isinstance(value
, Frequency
):
652 self
.ticks
= value
.ticks
653 self
.value
= 1.0 / value
.value
654 elif value
.endswith('t'):
656 self
.value
= int(value
[:-1])
659 self
.value
= convert
.toLatency(value
)
661 def __getattr__(self
, attr
):
662 if attr
in ('latency', 'period'):
664 if attr
== 'frequency':
665 return Frequency(self
)
666 raise AttributeError, "Latency object has no attribute '%s'" % attr
668 # convert latency to ticks
670 if self
.ticks
or self
.value
== 0:
671 return '%d' % self
.value
673 return '%d' % (ticks
.fromSeconds(self
.value
))
675 class Frequency(TickParamValue
):
676 def __init__(self
, value
):
677 if isinstance(value
, (Latency
, Clock
)):
681 self
.value
= 1.0 / value
.value
682 self
.ticks
= value
.ticks
683 elif isinstance(value
, Frequency
):
684 self
.value
= value
.value
685 self
.ticks
= value
.ticks
688 self
.value
= convert
.toFrequency(value
)
690 def __getattr__(self
, attr
):
691 if attr
== 'frequency':
693 if attr
in ('latency', 'period'):
695 raise AttributeError, "Frequency object has no attribute '%s'" % attr
697 # convert latency to ticks
699 if self
.ticks
or self
.value
== 0:
700 return '%d' % self
.value
702 return '%d' % (ticks
.fromSeconds(1.0 / self
.value
))
704 # A generic frequency and/or Latency value. Value is stored as a latency,
705 # but to avoid ambiguity this object does not support numeric ops (* or /).
706 # An explicit conversion to a Latency or Frequency must be made first.
707 class Clock(ParamValue
):
709 cxx_predecls
= ['#include "sim/host.hh"']
710 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
711 '%import "sim/host.hh"']
712 def __init__(self
, value
):
713 if isinstance(value
, (Latency
, Clock
)):
714 self
.ticks
= value
.ticks
715 self
.value
= value
.value
716 elif isinstance(value
, Frequency
):
717 self
.ticks
= value
.ticks
718 self
.value
= 1.0 / value
.value
719 elif value
.endswith('t'):
721 self
.value
= int(value
[:-1])
724 self
.value
= convert
.anyToLatency(value
)
726 def __getattr__(self
, attr
):
727 if attr
== 'frequency':
728 return Frequency(self
)
729 if attr
in ('latency', 'period'):
731 raise AttributeError, "Frequency object has no attribute '%s'" % attr
734 return self
.period
.ini_str()
736 class NetworkBandwidth(float,ParamValue
):
738 def __new__(cls
, value
):
739 # convert to bits per second
740 val
= convert
.toNetworkBandwidth(value
)
741 return super(cls
, NetworkBandwidth
).__new
__(cls
, val
)
747 # convert to seconds per byte
748 value
= 8.0 / float(self
)
749 # convert to ticks per byte
750 return '%f' % (ticks
.fromSeconds(value
))
752 class MemoryBandwidth(float,ParamValue
):
754 def __new__(self
, value
):
755 # we want the number of ticks per byte of data
756 val
= convert
.toMemoryBandwidth(value
)
757 return super(cls
, MemoryBandwidth
).__new
__(cls
, val
)
763 # convert to seconds per byte
764 value
= 1.0 / float(self
)
765 # convert to ticks per byte
766 return '%f' % (ticks
.fromSeconds(value
))
769 # "Constants"... handy aliases for various values.
772 # Special class for NULL pointers. Note the special check in
773 # make_param_value() above that lets these be assigned where a
774 # SimObject is required.
775 # only one copy of a particular node
776 class NullSimObject(object):
777 __metaclass__
= Singleton
782 def _instantiate(self
, parent
= None, path
= ''):
788 def unproxy(self
, base
):
791 def set_path(self
, parent
, name
):
796 # The only instance you'll ever need...
797 NULL
= NullSimObject()
799 def isNullPointer(value
):
800 return isinstance(value
, NullSimObject
)
802 # Some memory range specifications use this as a default upper bound.
805 AllMemory
= AddrRange(0, MaxAddr
)
808 #####################################################################
812 # Ports are used to interconnect objects in the memory system.
814 #####################################################################
816 # Port reference: encapsulates a reference to a particular port on a
817 # particular SimObject.
818 class PortRef(object):
819 def __init__(self
, simobj
, name
):
820 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
823 self
.peer
= None # not associated with another port yet
824 self
.ccConnected
= False # C++ port connection done?
825 self
.index
= -1 # always -1 for non-vector ports
828 return '%s.%s' % (self
.simobj
, self
.name
)
830 # for config.ini, print peer's name (not ours)
832 return str(self
.peer
)
834 def __getattr__(self
, attr
):
835 if attr
== 'peerObj':
836 # shorthand for proxies
837 return self
.peer
.simobj
838 raise AttributeError, "'%s' object has no attribute '%s'" % \
839 (self
.__class
__.__name
__, attr
)
841 # Full connection is symmetric (both ways). Called via
842 # SimObject.__setattr__ as a result of a port assignment, e.g.,
843 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
844 # e.g., "obj1.portA[3] = obj2.portB".
845 def connect(self
, other
):
846 if isinstance(other
, VectorPortRef
):
847 # reference to plain VectorPort is implicit append
848 other
= other
._get
_next
()
849 if self
.peer
and not proxy
.isproxy(self
.peer
):
850 print "warning: overwriting port", self
, \
851 "value", self
.peer
, "with", other
853 if proxy
.isproxy(other
):
854 other
.set_param_desc(PortParamDesc())
855 elif isinstance(other
, PortRef
):
856 if other
.peer
is not self
:
860 "assigning non-port reference '%s' to port '%s'" \
863 def clone(self
, simobj
, memo
):
864 if memo
.has_key(self
):
866 newRef
= copy
.copy(self
)
868 newRef
.simobj
= simobj
869 assert(isSimObject(newRef
.simobj
))
870 if self
.peer
and not proxy
.isproxy(self
.peer
):
871 peerObj
= self
.peer
.simobj(_memo
=memo
)
872 newRef
.peer
= self
.peer
.clone(peerObj
, memo
)
873 assert(not isinstance(newRef
.peer
, VectorPortRef
))
876 def unproxy(self
, simobj
):
877 assert(simobj
is self
.simobj
)
878 if proxy
.isproxy(self
.peer
):
880 realPeer
= self
.peer
.unproxy(self
.simobj
)
882 print "Error in unproxying port '%s' of %s" % \
883 (self
.name
, self
.simobj
.path())
885 self
.connect(realPeer
)
887 # Call C++ to create corresponding port connection between C++ objects
889 if self
.ccConnected
: # already done this
892 internal
.sim_object
.connectPorts(self
.simobj
.getCCObject(), self
.name
,
893 self
.index
, peer
.simobj
.getCCObject(), peer
.name
, peer
.index
)
894 self
.ccConnected
= True
895 peer
.ccConnected
= True
897 # A reference to an individual element of a VectorPort... much like a
898 # PortRef, but has an index.
899 class VectorPortElementRef(PortRef
):
900 def __init__(self
, simobj
, name
, index
):
901 PortRef
.__init
__(self
, simobj
, name
)
905 return '%s.%s[%d]' % (self
.simobj
, self
.name
, self
.index
)
907 # A reference to a complete vector-valued port (not just a single element).
908 # Can be indexed to retrieve individual VectorPortElementRef instances.
909 class VectorPortRef(object):
910 def __init__(self
, simobj
, name
):
911 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
917 return '%s.%s[:]' % (self
.simobj
, self
.name
)
919 # for config.ini, print peer's name (not ours)
921 return ' '.join([el
.ini_str() for el
in self
.elements
])
923 def __getitem__(self
, key
):
924 if not isinstance(key
, int):
925 raise TypeError, "VectorPort index must be integer"
926 if key
>= len(self
.elements
):
927 # need to extend list
928 ext
= [VectorPortElementRef(self
.simobj
, self
.name
, i
)
929 for i
in range(len(self
.elements
), key
+1)]
930 self
.elements
.extend(ext
)
931 return self
.elements
[key
]
934 return self
[len(self
.elements
)]
936 def __setitem__(self
, key
, value
):
937 if not isinstance(key
, int):
938 raise TypeError, "VectorPort index must be integer"
939 self
[key
].connect(value
)
941 def connect(self
, other
):
942 if isinstance(other
, (list, tuple)):
943 # Assign list of port refs to vector port.
944 # For now, append them... not sure if that's the right semantics
945 # or if it should replace the current vector.
947 self
._get
_next
().connect(ref
)
949 # scalar assignment to plain VectorPort is implicit append
950 self
._get
_next
().connect(other
)
952 def clone(self
, simobj
, memo
):
953 if memo
.has_key(self
):
955 newRef
= copy
.copy(self
)
957 newRef
.simobj
= simobj
958 assert(isSimObject(newRef
.simobj
))
959 newRef
.elements
= [el
.clone(simobj
, memo
) for el
in self
.elements
]
962 def unproxy(self
, simobj
):
963 [el
.unproxy(simobj
) for el
in self
.elements
]
966 [el
.ccConnect() for el
in self
.elements
]
968 # Port description object. Like a ParamDesc object, this represents a
969 # logical port in the SimObject class, not a particular port on a
970 # SimObject instance. The latter are represented by PortRef objects.
972 # Port("description") or Port(default, "description")
973 def __init__(self
, *args
):
977 self
.default
= args
[0]
980 raise TypeError, 'wrong number of arguments'
981 # self.name is set by SimObject class on assignment
982 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
984 # Generate a PortRef for this port on the given SimObject with the
986 def makeRef(self
, simobj
):
987 return PortRef(simobj
, self
.name
)
989 # Connect an instance of this port (on the given SimObject with
990 # the given name) with the port described by the supplied PortRef
991 def connect(self
, simobj
, ref
):
992 self
.makeRef(simobj
).connect(ref
)
994 # VectorPort description object. Like Port, but represents a vector
995 # of connections (e.g., as on a Bus).
996 class VectorPort(Port
):
997 def __init__(self
, *args
):
998 Port
.__init
__(self
, *args
)
1001 def makeRef(self
, simobj
):
1002 return VectorPortRef(simobj
, self
.name
)
1004 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1005 # proxy objects (via set_param_desc()) so that proxy error messages
1007 class PortParamDesc(object):
1008 __metaclass__
= Singleton
1014 __all__
= ['Param', 'VectorParam',
1015 'Enum', 'Bool', 'String', 'Float',
1016 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1017 'Int32', 'UInt32', 'Int64', 'UInt64',
1018 'Counter', 'Addr', 'Tick', 'Percent',
1019 'TcpPort', 'UdpPort', 'EthernetAddr',
1020 'MemorySize', 'MemorySize32',
1021 'Latency', 'Frequency', 'Clock',
1022 'NetworkBandwidth', 'MemoryBandwidth',
1023 'Range', 'AddrRange', 'TickRange',
1024 'MaxAddr', 'MaxTick', 'AllMemory',
1026 'NextEthernetAddr', 'NULL',
1027 'Port', 'VectorPort']
1029 # see comment on imports at end of __init__.py.
1030 from SimObject
import isSimObject
, isSimObjectSequence
, isSimObjectClass