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 #####################################################################
47 import sys
, inspect
, copy
51 # Dummy base class to identify types that are legitimate for SimObject
53 class ParamValue(object):
58 # default for printing to .ini file is regular string conversion.
59 # will be overridden in some cases
63 # allows us to blithely call unproxy() on things without checking
64 # if they're really proxies or not
65 def unproxy(self
, base
):
68 # Regular parameter description.
69 class 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
80 self
.default
= args
[0]
83 raise TypeError, 'too many arguments'
85 if kwargs
.has_key('desc'):
86 assert(not hasattr(self
, 'desc'))
87 self
.desc
= kwargs
['desc']
90 if kwargs
.has_key('default'):
91 assert(not hasattr(self
, 'default'))
92 self
.default
= kwargs
['default']
96 raise TypeError, 'extra unknown kwargs %s' % kwargs
98 if not hasattr(self
, 'desc'):
99 raise TypeError, 'desc attribute missing'
101 def __getattr__(self
, attr
):
104 ptype
= eval(self
.ptype_str
, objects
.__dict
__)
105 if not isinstance(ptype
, type):
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
)
115 def convert(self
, value
):
116 if isinstance(value
, proxy
.BaseProxy
):
117 value
.set_param_desc(self
)
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
123 if isinstance(value
, self
.ptype
):
125 if isNullPointer(value
) and isSimObjectClass(self
.ptype
):
127 return self
.ptype(value
)
129 def cxx_predecls(self
):
130 return self
.ptype
.cxx_predecls
132 def swig_predecls(self
):
133 return self
.ptype
.swig_predecls
136 return '%s %s;' % (self
.ptype
.cxx_type
, self
.name
)
138 # Vector-valued parameter description. Just like ParamDesc, except
139 # that the value is a vector (list) of the specified type instead of a
142 class VectorParamValue(list):
144 return ' '.join([v
.ini_str() for v
in self
])
146 def unproxy(self
, base
):
147 return [v
.unproxy(base
) for v
in self
]
149 class SimObjVector(VectorParamValue
):
154 class 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
)
164 return VectorParamValue(tmp_list
)
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
)
170 def cxx_predecls(self
):
171 return ['#include <vector>'] + self
.ptype
.cxx_predecls
173 def swig_predecls(self
):
174 return ['%include "std_vector.i"'] + self
.ptype
.swig_predecls
177 return 'std::vector< %s > %s;' % (self
.ptype
.cxx_type
, self
.name
)
179 class 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
184 def __getattr__(self
, attr
):
186 attr
= self
.ptype_str
+ '.' + attr
187 return ParamFactory(self
.param_desc_class
, attr
)
189 # E.g., Param.Int(5, "number of widgets")
190 def __call__(self
, *args
, **kwargs
):
191 caller_frame
= inspect
.currentframe().f_back
194 ptype
= eval(self
.ptype_str
,
195 caller_frame
.f_globals
, caller_frame
.f_locals
)
196 if not isinstance(ptype
, type):
198 "Param qualifier is not a type: %s" % ptype
200 # if name isn't defined yet, assume it's a SimObject, and
201 # try to resolve it later
203 return self
.param_desc_class(self
.ptype_str
, ptype
, *args
, **kwargs
)
205 Param
= ParamFactory(ParamDesc
)
206 VectorParam
= ParamFactory(VectorParamDesc
)
208 #####################################################################
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).
219 #####################################################################
221 # String-valued parameter. Just mixin the ParamValue class with the
222 # built-in str class.
223 class 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 *};']
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.
233 class NumericParamValue(ParamValue
):
235 return str(self
.value
)
238 return float(self
.value
)
241 return long(self
.value
)
244 return int(self
.value
)
246 # hook for bounds checking
250 def __mul__(self
, other
):
251 newobj
= self
.__class
__(self
)
252 newobj
.value
*= other
258 def __div__(self
, other
):
259 newobj
= self
.__class
__(self
)
260 newobj
.value
/= other
264 def __sub__(self
, other
):
265 newobj
= self
.__class
__(self
)
266 newobj
.value
-= other
270 # Metaclass for bounds-checked integer parameters. See CheckedInt.
271 class CheckedIntType(type):
272 def __init__(cls
, name
, bases
, dict):
273 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
275 # CheckedInt is an abstract base class, so we actually don't
276 # want to do any processing on it... the rest of this code is
277 # just for classes that derive from CheckedInt.
278 if name
== 'CheckedInt':
281 if not cls
.cxx_predecls
:
282 # most derived types require this, so we just do it here once
283 cls
.cxx_predecls
= ['#include "sim/host.hh"']
285 if not cls
.swig_predecls
:
286 # most derived types require this, so we just do it here once
287 cls
.swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
288 '%import "sim/host.hh"']
290 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
291 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
292 panic("CheckedInt subclass %s must define either\n" \
293 " 'min' and 'max' or 'size' and 'unsigned'\n" \
297 cls
.max = 2 ** cls
.size
- 1
299 cls
.min = -(2 ** (cls
.size
- 1))
300 cls
.max = (2 ** (cls
.size
- 1)) - 1
302 # Abstract superclass for bounds-checked integer parameters. This
303 # class is subclassed to generate parameter classes with specific
304 # bounds. Initialization of the min and max bounds is done in the
305 # metaclass CheckedIntType.__init__.
306 class CheckedInt(NumericParamValue
):
307 __metaclass__
= CheckedIntType
310 if not self
.min <= self
.value
<= self
.max:
311 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
312 (self
.min, self
.value
, self
.max)
314 def __init__(self
, value
):
315 if isinstance(value
, str):
316 self
.value
= convert
.toInteger(value
)
317 elif isinstance(value
, (int, long, float, NumericParamValue
)):
318 self
.value
= long(value
)
320 raise TypeError, "Can't convert object of type %s to CheckedInt" \
321 % type(value
).__name
__
324 class Int(CheckedInt
): cxx_type
= 'int'; size
= 32; unsigned
= False
325 class Unsigned(CheckedInt
): cxx_type
= 'unsigned'; size
= 32; unsigned
= True
327 class Int8(CheckedInt
): cxx_type
= 'int8_t'; size
= 8; unsigned
= False
328 class UInt8(CheckedInt
): cxx_type
= 'uint8_t'; size
= 8; unsigned
= True
329 class Int16(CheckedInt
): cxx_type
= 'int16_t'; size
= 16; unsigned
= False
330 class UInt16(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
331 class Int32(CheckedInt
): cxx_type
= 'int32_t'; size
= 32; unsigned
= False
332 class UInt32(CheckedInt
): cxx_type
= 'uint32_t'; size
= 32; unsigned
= True
333 class Int64(CheckedInt
): cxx_type
= 'int64_t'; size
= 64; unsigned
= False
334 class UInt64(CheckedInt
): cxx_type
= 'uint64_t'; size
= 64; unsigned
= True
336 class Counter(CheckedInt
): cxx_type
= 'Counter'; size
= 64; unsigned
= True
337 class Tick(CheckedInt
): cxx_type
= 'Tick'; size
= 64; unsigned
= True
338 class TcpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
339 class UdpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
341 class Percent(CheckedInt
): cxx_type
= 'int'; min = 0; max = 100
343 class Float(ParamValue
, float):
346 class MemorySize(CheckedInt
):
347 cxx_type
= 'uint64_t'
350 def __init__(self
, value
):
351 if isinstance(value
, MemorySize
):
352 self
.value
= value
.value
354 self
.value
= convert
.toMemorySize(value
)
357 class MemorySize32(CheckedInt
):
360 def __init__(self
, value
):
361 if isinstance(value
, MemorySize
):
362 self
.value
= value
.value
364 self
.value
= convert
.toMemorySize(value
)
367 class Addr(CheckedInt
):
369 cxx_predecls
= ['#include "targetarch/isa_traits.hh"']
372 def __init__(self
, value
):
373 if isinstance(value
, Addr
):
374 self
.value
= value
.value
377 self
.value
= convert
.toMemorySize(value
)
379 self
.value
= long(value
)
381 def __add__(self
, other
):
382 if isinstance(other
, Addr
):
383 return self
.value
+ other
.value
385 return self
.value
+ other
388 class MetaRange(type):
389 def __init__(cls
, name
, bases
, dict):
390 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
393 cls
.cxx_type
= 'Range< %s >' % cls
.type.cxx_type
395 ['#include "base/range.hh"'] + cls
.type.cxx_predecls
397 class Range(ParamValue
):
398 __metaclass__
= MetaRange
399 type = Int
# default; can be overridden in subclasses
400 def __init__(self
, *args
, **kwargs
):
401 def handle_kwargs(self
, kwargs
):
403 self
.second
= self
.type(kwargs
.pop('end'))
404 elif 'size' in kwargs
:
405 self
.second
= self
.first
+ self
.type(kwargs
.pop('size')) - 1
407 raise TypeError, "Either end or size must be specified"
410 self
.first
= self
.type(kwargs
.pop('start'))
411 handle_kwargs(self
, kwargs
)
415 self
.first
= self
.type(args
[0])
416 handle_kwargs(self
, kwargs
)
417 elif isinstance(args
[0], Range
):
418 self
.first
= self
.type(args
[0].first
)
419 self
.second
= self
.type(args
[0].second
)
421 self
.first
= self
.type(0)
422 self
.second
= self
.type(args
[0]) - 1
425 self
.first
= self
.type(args
[0])
426 self
.second
= self
.type(args
[1])
428 raise TypeError, "Too many arguments specified"
431 raise TypeError, "too many keywords: %s" % kwargs
.keys()
434 return '%s:%s' % (self
.first
, self
.second
)
436 class AddrRange(Range
):
439 class TickRange(Range
):
442 # Boolean parameter type. Python doesn't let you subclass bool, since
443 # it doesn't want to let you create multiple instances of True and
444 # False. Thus this is a little more complicated than String.
445 class Bool(ParamValue
):
447 def __init__(self
, value
):
449 self
.value
= convert
.toBool(value
)
451 self
.value
= bool(value
)
454 return str(self
.value
)
461 def IncEthernetAddr(addr
, val
= 1):
462 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
464 for i
in (5, 4, 3, 2, 1):
465 val
,rem
= divmod(bytes
[i
], 256)
470 assert(bytes
[0] <= 255)
471 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
473 class NextEthernetAddr(object):
474 addr
= "00:90:00:00:00:01"
476 def __init__(self
, inc
= 1):
477 self
.value
= NextEthernetAddr
.addr
478 NextEthernetAddr
.addr
= IncEthernetAddr(NextEthernetAddr
.addr
, inc
)
480 class EthernetAddr(ParamValue
):
481 cxx_type
= 'Net::EthAddr'
482 cxx_predecls
= ['#include "base/inet.hh"']
483 swig_predecls
= ['class Net::EthAddr;']
484 def __init__(self
, value
):
485 if value
== NextEthernetAddr
:
489 if not isinstance(value
, str):
490 raise TypeError, "expected an ethernet address and didn't get one"
492 bytes
= value
.split(':')
494 raise TypeError, 'invalid ethernet address %s' % value
497 if not 0 <= int(byte
) <= 256:
498 raise TypeError, 'invalid ethernet address %s' % value
502 def unproxy(self
, base
):
503 if self
.value
== NextEthernetAddr
:
504 self
.addr
= self
.value().value
508 if self
.value
== NextEthernetAddr
:
509 if hasattr(self
, 'addr'):
512 return "NextEthernetAddr (unresolved)"
516 # Enumerated types are a little more complex. The user specifies the
517 # type as Enum(foo) where foo is either a list or dictionary of
518 # alternatives (typically strings, but not necessarily so). (In the
519 # long run, the integer value of the parameter will be the list index
520 # or the corresponding dictionary value. For now, since we only check
521 # that the alternative is valid and then spit it into a .ini file,
522 # there's not much point in using the dictionary.)
524 # What Enum() must do is generate a new type encapsulating the
525 # provided list/dictionary so that specific values of the parameter
526 # can be instances of that type. We define two hidden internal
527 # classes (_ListEnum and _DictEnum) to serve as base classes, then
528 # derive the new type from the appropriate base class on the fly.
531 # Metaclass for Enum types
532 class MetaEnum(type):
533 def __init__(cls
, name
, bases
, init_dict
):
534 if init_dict
.has_key('map'):
535 if not isinstance(cls
.map, dict):
536 raise TypeError, "Enum-derived class attribute 'map' " \
537 "must be of type dict"
538 # build list of value strings from map
539 cls
.vals
= cls
.map.keys()
541 elif init_dict
.has_key('vals'):
542 if not isinstance(cls
.vals
, list):
543 raise TypeError, "Enum-derived class attribute 'vals' " \
544 "must be of type list"
545 # build string->value map from vals sequence
547 for idx
,val
in enumerate(cls
.vals
):
550 raise TypeError, "Enum-derived class must define "\
551 "attribute 'map' or 'vals'"
553 cls
.cxx_type
= name
+ '::Enum'
555 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
557 # Generate C++ class declaration for this enum type.
558 # Note that we wrap the enum in a class/struct to act as a namespace,
559 # so that the enum strings can be brief w/o worrying about collisions.
561 s
= 'struct %s {\n enum Enum {\n ' % cls
.__name
__
562 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
566 # Base class for enum types.
567 class Enum(ParamValue
):
568 __metaclass__
= MetaEnum
571 def __init__(self
, value
):
572 if value
not in self
.map:
573 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
582 # how big does a rounding error need to be before we warn about it?
583 frequency_tolerance
= 0.001 # 0.1%
585 # convert a floting-point # of ticks to integer, and warn if rounding
586 # discards too much precision
587 def tick_check(float_ticks
):
590 int_ticks
= int(round(float_ticks
))
591 err
= (float_ticks
- int_ticks
) / float_ticks
592 if err
> frequency_tolerance
:
593 print >> sys
.stderr
, "Warning: rounding error > tolerance"
594 print >> sys
.stderr
, " %f rounded to %d" % (float_ticks
, int_ticks
)
598 def getLatency(value
):
599 if isinstance(value
, Latency
) or isinstance(value
, Clock
):
601 elif isinstance(value
, Frequency
) or isinstance(value
, RootClock
):
602 return 1 / value
.value
603 elif isinstance(value
, str):
605 return convert
.toLatency(value
)
608 return 1 / convert
.toFrequency(value
)
611 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
614 class Latency(NumericParamValue
):
616 cxx_predecls
= ['#include "sim/host.hh"']
617 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
618 '%import "sim/host.hh"']
619 def __init__(self
, value
):
620 self
.value
= getLatency(value
)
622 def __getattr__(self
, attr
):
623 if attr
in ('latency', 'period'):
625 if attr
== 'frequency':
626 return Frequency(self
)
627 raise AttributeError, "Latency object has no attribute '%s'" % attr
629 # convert latency to ticks
631 return str(tick_check(self
.value
* ticks_per_sec
))
633 class Frequency(NumericParamValue
):
635 cxx_predecls
= ['#include "sim/host.hh"']
636 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
637 '%import "sim/host.hh"']
638 def __init__(self
, value
):
639 self
.value
= 1 / getLatency(value
)
641 def __getattr__(self
, attr
):
642 if attr
== 'frequency':
644 if attr
in ('latency', 'period'):
646 raise AttributeError, "Frequency object has no attribute '%s'" % attr
648 # convert frequency to ticks per period
650 return self
.period
.ini_str()
652 # Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
653 # We can't inherit from Frequency because we don't want it to be directly
654 # assignable to a regular Frequency parameter.
655 class RootClock(ParamValue
):
657 cxx_predecls
= ['#include "sim/host.hh"']
658 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
659 '%import "sim/host.hh"']
660 def __init__(self
, value
):
661 self
.value
= 1 / getLatency(value
)
663 def __getattr__(self
, attr
):
664 if attr
== 'frequency':
665 return Frequency(self
)
666 if attr
in ('latency', 'period'):
668 raise AttributeError, "Frequency object has no attribute '%s'" % attr
671 return str(tick_check(self
.value
))
673 # A generic frequency and/or Latency value. Value is stored as a latency,
674 # but to avoid ambiguity this object does not support numeric ops (* or /).
675 # An explicit conversion to a Latency or Frequency must be made first.
676 class Clock(ParamValue
):
678 cxx_predecls
= ['#include "sim/host.hh"']
679 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
680 '%import "sim/host.hh"']
681 def __init__(self
, value
):
682 self
.value
= getLatency(value
)
684 def __getattr__(self
, attr
):
685 if attr
== 'frequency':
686 return Frequency(self
)
687 if attr
in ('latency', 'period'):
689 raise AttributeError, "Frequency object has no attribute '%s'" % attr
692 return self
.period
.ini_str()
694 class NetworkBandwidth(float,ParamValue
):
696 def __new__(cls
, value
):
697 val
= convert
.toNetworkBandwidth(value
) / 8.0
698 return super(cls
, NetworkBandwidth
).__new
__(cls
, val
)
704 return '%f' % (ticks_per_sec
/ float(self
))
706 class MemoryBandwidth(float,ParamValue
):
708 def __new__(self
, value
):
709 val
= convert
.toMemoryBandwidth(value
)
710 return super(cls
, MemoryBandwidth
).__new
__(cls
, val
)
716 return '%f' % (ticks_per_sec
/ float(self
))
719 # "Constants"... handy aliases for various values.
722 # Special class for NULL pointers. Note the special check in
723 # make_param_value() above that lets these be assigned where a
724 # SimObject is required.
725 # only one copy of a particular node
726 class NullSimObject(object):
727 __metaclass__
= Singleton
732 def _instantiate(self
, parent
= None, path
= ''):
738 def unproxy(self
, base
):
741 def set_path(self
, parent
, name
):
746 # The only instance you'll ever need...
747 NULL
= NullSimObject()
749 def isNullPointer(value
):
750 return isinstance(value
, NullSimObject
)
752 # Some memory range specifications use this as a default upper bound.
755 AllMemory
= AddrRange(0, MaxAddr
)
758 #####################################################################
762 # Ports are used to interconnect objects in the memory system.
764 #####################################################################
766 # Port reference: encapsulates a reference to a particular port on a
767 # particular SimObject.
768 class PortRef(object):
769 def __init__(self
, simobj
, name
):
770 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
773 self
.peer
= None # not associated with another port yet
774 self
.ccConnected
= False # C++ port connection done?
775 self
.index
= -1 # always -1 for non-vector ports
778 return '%s.%s' % (self
.simobj
, self
.name
)
780 # for config.ini, print peer's name (not ours)
782 return str(self
.peer
)
784 def __getattr__(self
, attr
):
785 if attr
== 'peerObj':
786 # shorthand for proxies
787 return self
.peer
.simobj
788 raise AttributeError, "'%s' object has no attribute '%s'" % \
789 (self
.__class
__.__name
__, attr
)
791 # Full connection is symmetric (both ways). Called via
792 # SimObject.__setattr__ as a result of a port assignment, e.g.,
793 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
794 # e.g., "obj1.portA[3] = obj2.portB".
795 def connect(self
, other
):
796 if isinstance(other
, VectorPortRef
):
797 # reference to plain VectorPort is implicit append
798 other
= other
._get
_next
()
799 if self
.peer
and not proxy
.isproxy(self
.peer
):
800 print "warning: overwriting port", self
, \
801 "value", self
.peer
, "with", other
803 if proxy
.isproxy(other
):
804 other
.set_param_desc(PortParamDesc())
805 elif isinstance(other
, PortRef
):
806 if other
.peer
is not self
:
810 "assigning non-port reference '%s' to port '%s'" \
813 def clone(self
, simobj
, memo
):
814 if memo
.has_key(self
):
816 newRef
= copy
.copy(self
)
818 newRef
.simobj
= simobj
819 assert(isSimObject(newRef
.simobj
))
820 if self
.peer
and not proxy
.isproxy(self
.peer
):
821 peerObj
= self
.peer
.simobj(_memo
=memo
)
822 newRef
.peer
= self
.peer
.clone(peerObj
, memo
)
823 assert(not isinstance(newRef
.peer
, VectorPortRef
))
826 def unproxy(self
, simobj
):
827 assert(simobj
is self
.simobj
)
828 if proxy
.isproxy(self
.peer
):
830 realPeer
= self
.peer
.unproxy(self
.simobj
)
832 print "Error in unproxying port '%s' of %s" % \
833 (self
.name
, self
.simobj
.path())
835 self
.connect(realPeer
)
837 # Call C++ to create corresponding port connection between C++ objects
839 if self
.ccConnected
: # already done this
842 internal
.main
.connectPorts(self
.simobj
.getCCObject(), self
.name
,
843 self
.index
, peer
.simobj
.getCCObject(),
844 peer
.name
, peer
.index
)
845 self
.ccConnected
= True
846 peer
.ccConnected
= True
848 # A reference to an individual element of a VectorPort... much like a
849 # PortRef, but has an index.
850 class VectorPortElementRef(PortRef
):
851 def __init__(self
, simobj
, name
, index
):
852 PortRef
.__init
__(self
, simobj
, name
)
856 return '%s.%s[%d]' % (self
.simobj
, self
.name
, self
.index
)
858 # A reference to a complete vector-valued port (not just a single element).
859 # Can be indexed to retrieve individual VectorPortElementRef instances.
860 class VectorPortRef(object):
861 def __init__(self
, simobj
, name
):
862 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
868 return '%s.%s[:]' % (self
.simobj
, self
.name
)
870 # for config.ini, print peer's name (not ours)
872 return ' '.join([el
.ini_str() for el
in self
.elements
])
874 def __getitem__(self
, key
):
875 if not isinstance(key
, int):
876 raise TypeError, "VectorPort index must be integer"
877 if key
>= len(self
.elements
):
878 # need to extend list
879 ext
= [VectorPortElementRef(self
.simobj
, self
.name
, i
)
880 for i
in range(len(self
.elements
), key
+1)]
881 self
.elements
.extend(ext
)
882 return self
.elements
[key
]
885 return self
[len(self
.elements
)]
887 def __setitem__(self
, key
, value
):
888 if not isinstance(key
, int):
889 raise TypeError, "VectorPort index must be integer"
890 self
[key
].connect(value
)
892 def connect(self
, other
):
893 if isinstance(other
, (list, tuple)):
894 # Assign list of port refs to vector port.
895 # For now, append them... not sure if that's the right semantics
896 # or if it should replace the current vector.
898 self
._get
_next
().connect(ref
)
900 # scalar assignment to plain VectorPort is implicit append
901 self
._get
_next
().connect(other
)
903 def clone(self
, simobj
, memo
):
904 if memo
.has_key(self
):
906 newRef
= copy
.copy(self
)
908 newRef
.simobj
= simobj
909 assert(isSimObject(newRef
.simobj
))
910 newRef
.elements
= [el
.clone(simobj
, memo
) for el
in self
.elements
]
913 def unproxy(self
, simobj
):
914 [el
.unproxy(simobj
) for el
in self
.elements
]
917 [el
.ccConnect() for el
in self
.elements
]
919 # Port description object. Like a ParamDesc object, this represents a
920 # logical port in the SimObject class, not a particular port on a
921 # SimObject instance. The latter are represented by PortRef objects.
923 # Port("description") or Port(default, "description")
924 def __init__(self
, *args
):
928 self
.default
= args
[0]
931 raise TypeError, 'wrong number of arguments'
932 # self.name is set by SimObject class on assignment
933 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
935 # Generate a PortRef for this port on the given SimObject with the
937 def makeRef(self
, simobj
):
938 return PortRef(simobj
, self
.name
)
940 # Connect an instance of this port (on the given SimObject with
941 # the given name) with the port described by the supplied PortRef
942 def connect(self
, simobj
, ref
):
943 self
.makeRef(simobj
).connect(ref
)
945 # VectorPort description object. Like Port, but represents a vector
946 # of connections (e.g., as on a Bus).
947 class VectorPort(Port
):
948 def __init__(self
, *args
):
949 Port
.__init
__(self
, *args
)
952 def makeRef(self
, simobj
):
953 return VectorPortRef(simobj
, self
.name
)
955 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
956 # proxy objects (via set_param_desc()) so that proxy error messages
958 class PortParamDesc(object):
959 __metaclass__
= Singleton
965 __all__
= ['Param', 'VectorParam',
966 'Enum', 'Bool', 'String', 'Float',
967 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
968 'Int32', 'UInt32', 'Int64', 'UInt64',
969 'Counter', 'Addr', 'Tick', 'Percent',
970 'TcpPort', 'UdpPort', 'EthernetAddr',
971 'MemorySize', 'MemorySize32',
972 'Latency', 'Frequency', 'RootClock', 'Clock',
973 'NetworkBandwidth', 'MemoryBandwidth',
974 'Range', 'AddrRange', 'TickRange',
975 'MaxAddr', 'MaxTick', 'AllMemory',
976 'NextEthernetAddr', 'NULL',
977 'Port', 'VectorPort']
979 # see comment on imports at end of __init__.py.
980 from SimObject
import isSimObject
, isSimObjectSequence
, isSimObjectClass