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 #####################################################################
58 # Dummy base class to identify types that are legitimate for SimObject
60 class ParamValue(object):
65 # default for printing to .ini file is regular string conversion.
66 # will be overridden in some cases
70 # allows us to blithely call unproxy() on things without checking
71 # if they're really proxies or not
72 def unproxy(self
, base
):
75 # Regular parameter description.
76 class ParamDesc(object):
77 def __init__(self
, ptype_str
, ptype
, *args
, **kwargs
):
78 self
.ptype_str
= ptype_str
79 # remember ptype only if it is provided
87 self
.default
= args
[0]
90 raise TypeError, 'too many arguments'
92 if kwargs
.has_key('desc'):
93 assert(not hasattr(self
, 'desc'))
94 self
.desc
= kwargs
['desc']
97 if kwargs
.has_key('default'):
98 assert(not hasattr(self
, 'default'))
99 self
.default
= kwargs
['default']
100 del kwargs
['default']
103 raise TypeError, 'extra unknown kwargs %s' % kwargs
105 if not hasattr(self
, 'desc'):
106 raise TypeError, 'desc attribute missing'
108 def __getattr__(self
, attr
):
111 ptype
= eval(self
.ptype_str
, objects
.__dict
__)
112 if not isinstance(ptype
, type):
118 "Param qualifier '%s' is not a type" % self
.ptype_str
119 raise AttributeError, "'%s' object has no attribute '%s'" % \
120 (type(self
).__name
__, attr
)
122 def convert(self
, value
):
123 if isinstance(value
, proxy
.BaseProxy
):
124 value
.set_param_desc(self
)
126 if not hasattr(self
, 'ptype') and isNullPointer(value
):
127 # deferred evaluation of SimObject; continue to defer if
128 # we're just assigning a null pointer
130 if isinstance(value
, self
.ptype
):
132 if isNullPointer(value
) and isSimObjectClass(self
.ptype
):
134 return self
.ptype(value
)
136 def cxx_predecls(self
):
137 return self
.ptype
.cxx_predecls
139 def swig_predecls(self
):
140 return self
.ptype
.swig_predecls
143 return '%s %s;' % (self
.ptype
.cxx_type
, self
.name
)
145 # Vector-valued parameter description. Just like ParamDesc, except
146 # that the value is a vector (list) of the specified type instead of a
149 class VectorParamValue(list):
151 return ' '.join([v
.ini_str() for v
in self
])
153 def unproxy(self
, base
):
154 return [v
.unproxy(base
) for v
in self
]
156 class SimObjVector(VectorParamValue
):
161 class VectorParamDesc(ParamDesc
):
162 # Convert assigned value to appropriate type. If the RHS is not a
163 # list or tuple, it generates a single-element list.
164 def convert(self
, value
):
165 if isinstance(value
, (list, tuple)):
166 # list: coerce each element into new list
167 tmp_list
= [ ParamDesc
.convert(self
, v
) for v
in value
]
168 if isSimObjectSequence(tmp_list
):
169 return SimObjVector(tmp_list
)
171 return VectorParamValue(tmp_list
)
173 # singleton: leave it be (could coerce to a single-element
174 # list here, but for some historical reason we don't...
175 return ParamDesc
.convert(self
, value
)
177 def cxx_predecls(self
):
178 return ['#include <vector>'] + self
.ptype
.cxx_predecls
180 def swig_predecls(self
):
181 return ['%include "std_vector.i"'] + self
.ptype
.swig_predecls
184 return 'std::vector< %s > %s;' % (self
.ptype
.cxx_type
, self
.name
)
186 class ParamFactory(object):
187 def __init__(self
, param_desc_class
, ptype_str
= None):
188 self
.param_desc_class
= param_desc_class
189 self
.ptype_str
= ptype_str
191 def __getattr__(self
, attr
):
193 attr
= self
.ptype_str
+ '.' + attr
194 return ParamFactory(self
.param_desc_class
, attr
)
196 # E.g., Param.Int(5, "number of widgets")
197 def __call__(self
, *args
, **kwargs
):
198 caller_frame
= inspect
.currentframe().f_back
201 ptype
= eval(self
.ptype_str
,
202 caller_frame
.f_globals
, caller_frame
.f_locals
)
203 if not isinstance(ptype
, type):
205 "Param qualifier is not a type: %s" % ptype
207 # if name isn't defined yet, assume it's a SimObject, and
208 # try to resolve it later
210 return self
.param_desc_class(self
.ptype_str
, ptype
, *args
, **kwargs
)
212 Param
= ParamFactory(ParamDesc
)
213 VectorParam
= ParamFactory(VectorParamDesc
)
215 #####################################################################
219 # Though native Python types could be used to specify parameter types
220 # (the 'ptype' field of the Param and VectorParam classes), it's more
221 # flexible to define our own set of types. This gives us more control
222 # over how Python expressions are converted to values (via the
223 # __init__() constructor) and how these values are printed out (via
224 # the __str__() conversion method).
226 #####################################################################
228 # String-valued parameter. Just mixin the ParamValue class with the
229 # built-in str class.
230 class String(ParamValue
,str):
231 cxx_type
= 'std::string'
232 cxx_predecls
= ['#include <string>']
233 swig_predecls
= ['%include "std_string.i"\n' +
234 '%apply const std::string& {std::string *};']
237 # superclass for "numeric" parameter values, to emulate math
238 # operations in a type-safe way. e.g., a Latency times an int returns
239 # a new Latency object.
240 class NumericParamValue(ParamValue
):
242 return str(self
.value
)
245 return float(self
.value
)
248 return long(self
.value
)
251 return int(self
.value
)
253 # hook for bounds checking
257 def __mul__(self
, other
):
258 newobj
= self
.__class
__(self
)
259 newobj
.value
*= other
265 def __div__(self
, other
):
266 newobj
= self
.__class
__(self
)
267 newobj
.value
/= other
271 def __sub__(self
, other
):
272 newobj
= self
.__class
__(self
)
273 newobj
.value
-= other
277 # Metaclass for bounds-checked integer parameters. See CheckedInt.
278 class CheckedIntType(type):
279 def __init__(cls
, name
, bases
, dict):
280 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
282 # CheckedInt is an abstract base class, so we actually don't
283 # want to do any processing on it... the rest of this code is
284 # just for classes that derive from CheckedInt.
285 if name
== 'CheckedInt':
288 if not cls
.cxx_predecls
:
289 # most derived types require this, so we just do it here once
290 cls
.cxx_predecls
= ['#include "sim/host.hh"']
292 if not cls
.swig_predecls
:
293 # most derived types require this, so we just do it here once
294 cls
.swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
295 '%import "sim/host.hh"']
297 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
298 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
299 panic("CheckedInt subclass %s must define either\n" \
300 " 'min' and 'max' or 'size' and 'unsigned'\n" \
304 cls
.max = 2 ** cls
.size
- 1
306 cls
.min = -(2 ** (cls
.size
- 1))
307 cls
.max = (2 ** (cls
.size
- 1)) - 1
309 # Abstract superclass for bounds-checked integer parameters. This
310 # class is subclassed to generate parameter classes with specific
311 # bounds. Initialization of the min and max bounds is done in the
312 # metaclass CheckedIntType.__init__.
313 class CheckedInt(NumericParamValue
):
314 __metaclass__
= CheckedIntType
317 if not self
.min <= self
.value
<= self
.max:
318 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
319 (self
.min, self
.value
, self
.max)
321 def __init__(self
, value
):
322 if isinstance(value
, str):
323 self
.value
= convert
.toInteger(value
)
324 elif isinstance(value
, (int, long, float, NumericParamValue
)):
325 self
.value
= long(value
)
327 raise TypeError, "Can't convert object of type %s to CheckedInt" \
328 % type(value
).__name
__
331 class Int(CheckedInt
): cxx_type
= 'int'; size
= 32; unsigned
= False
332 class Unsigned(CheckedInt
): cxx_type
= 'unsigned'; size
= 32; unsigned
= True
334 class Int8(CheckedInt
): cxx_type
= 'int8_t'; size
= 8; unsigned
= False
335 class UInt8(CheckedInt
): cxx_type
= 'uint8_t'; size
= 8; unsigned
= True
336 class Int16(CheckedInt
): cxx_type
= 'int16_t'; size
= 16; unsigned
= False
337 class UInt16(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
338 class Int32(CheckedInt
): cxx_type
= 'int32_t'; size
= 32; unsigned
= False
339 class UInt32(CheckedInt
): cxx_type
= 'uint32_t'; size
= 32; unsigned
= True
340 class Int64(CheckedInt
): cxx_type
= 'int64_t'; size
= 64; unsigned
= False
341 class UInt64(CheckedInt
): cxx_type
= 'uint64_t'; size
= 64; unsigned
= True
343 class Counter(CheckedInt
): cxx_type
= 'Counter'; size
= 64; unsigned
= True
344 class Tick(CheckedInt
): cxx_type
= 'Tick'; size
= 64; unsigned
= True
345 class TcpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
346 class UdpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
348 class Percent(CheckedInt
): cxx_type
= 'int'; min = 0; max = 100
350 class Float(ParamValue
, float):
353 class MemorySize(CheckedInt
):
354 cxx_type
= 'uint64_t'
357 def __init__(self
, value
):
358 if isinstance(value
, MemorySize
):
359 self
.value
= value
.value
361 self
.value
= convert
.toMemorySize(value
)
364 class MemorySize32(CheckedInt
):
365 cxx_type
= 'uint32_t'
368 def __init__(self
, value
):
369 if isinstance(value
, MemorySize
):
370 self
.value
= value
.value
372 self
.value
= convert
.toMemorySize(value
)
375 class Addr(CheckedInt
):
377 cxx_predecls
= ['#include "targetarch/isa_traits.hh"']
380 def __init__(self
, value
):
381 if isinstance(value
, Addr
):
382 self
.value
= value
.value
385 self
.value
= convert
.toMemorySize(value
)
387 self
.value
= long(value
)
389 def __add__(self
, other
):
390 if isinstance(other
, Addr
):
391 return self
.value
+ other
.value
393 return self
.value
+ other
396 class MetaRange(type):
397 def __init__(cls
, name
, bases
, dict):
398 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
401 cls
.cxx_type
= 'Range< %s >' % cls
.type.cxx_type
403 ['#include "base/range.hh"'] + cls
.type.cxx_predecls
405 class Range(ParamValue
):
406 __metaclass__
= MetaRange
407 type = Int
# default; can be overridden in subclasses
408 def __init__(self
, *args
, **kwargs
):
409 def handle_kwargs(self
, kwargs
):
411 self
.second
= self
.type(kwargs
.pop('end'))
412 elif 'size' in kwargs
:
413 self
.second
= self
.first
+ self
.type(kwargs
.pop('size')) - 1
415 raise TypeError, "Either end or size must be specified"
418 self
.first
= self
.type(kwargs
.pop('start'))
419 handle_kwargs(self
, kwargs
)
423 self
.first
= self
.type(args
[0])
424 handle_kwargs(self
, kwargs
)
425 elif isinstance(args
[0], Range
):
426 self
.first
= self
.type(args
[0].first
)
427 self
.second
= self
.type(args
[0].second
)
429 self
.first
= self
.type(0)
430 self
.second
= self
.type(args
[0]) - 1
433 self
.first
= self
.type(args
[0])
434 self
.second
= self
.type(args
[1])
436 raise TypeError, "Too many arguments specified"
439 raise TypeError, "too many keywords: %s" % kwargs
.keys()
442 return '%s:%s' % (self
.first
, self
.second
)
444 class AddrRange(Range
):
447 class TickRange(Range
):
450 # Boolean parameter type. Python doesn't let you subclass bool, since
451 # it doesn't want to let you create multiple instances of True and
452 # False. Thus this is a little more complicated than String.
453 class Bool(ParamValue
):
455 def __init__(self
, value
):
457 self
.value
= convert
.toBool(value
)
459 self
.value
= bool(value
)
462 return str(self
.value
)
469 def IncEthernetAddr(addr
, val
= 1):
470 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
472 for i
in (5, 4, 3, 2, 1):
473 val
,rem
= divmod(bytes
[i
], 256)
478 assert(bytes
[0] <= 255)
479 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
481 _NextEthernetAddr
= "00:90:00:00:00:01"
482 def NextEthernetAddr():
483 global _NextEthernetAddr
485 value
= _NextEthernetAddr
486 _NextEthernetAddr
= IncEthernetAddr(_NextEthernetAddr
, 1)
489 class EthernetAddr(ParamValue
):
490 cxx_type
= 'Net::EthAddr'
491 cxx_predecls
= ['#include "base/inet.hh"']
492 swig_predecls
= ['class Net::EthAddr;']
493 def __init__(self
, value
):
494 if value
== NextEthernetAddr
:
498 if not isinstance(value
, str):
499 raise TypeError, "expected an ethernet address and didn't get one"
501 bytes
= value
.split(':')
503 raise TypeError, 'invalid ethernet address %s' % value
506 if not 0 <= int(byte
) <= 256:
507 raise TypeError, 'invalid ethernet address %s' % value
511 def unproxy(self
, base
):
512 if self
.value
== NextEthernetAddr
:
513 return EthernetAddr(self
.value())
519 time_formats
= [ "%a %b %d %H:%M:%S %Z %Y",
520 "%a %b %d %H:%M:%S %Z %Y",
532 def parse_time(value
):
533 from time
import gmtime
, strptime
, struct_time
, time
534 from datetime
import datetime
, date
536 if isinstance(value
, struct_time
):
539 if isinstance(value
, (int, long)):
542 if isinstance(value
, (datetime
, date
)):
543 return value
.timetuple()
545 if isinstance(value
, str):
546 if value
in ('Now', 'Today'):
547 return time
.gmtime(time
.time())
549 for format
in time_formats
:
551 return strptime(value
, format
)
555 raise ValueError, "Could not parse '%s' as a time" % value
557 class Time(ParamValue
):
559 def __init__(self
, value
):
560 self
.value
= parse_time(value
)
564 return ' '.join([ str(tm
[i
]) for i
in xrange(8)])
569 # Enumerated types are a little more complex. The user specifies the
570 # type as Enum(foo) where foo is either a list or dictionary of
571 # alternatives (typically strings, but not necessarily so). (In the
572 # long run, the integer value of the parameter will be the list index
573 # or the corresponding dictionary value. For now, since we only check
574 # that the alternative is valid and then spit it into a .ini file,
575 # there's not much point in using the dictionary.)
577 # What Enum() must do is generate a new type encapsulating the
578 # provided list/dictionary so that specific values of the parameter
579 # can be instances of that type. We define two hidden internal
580 # classes (_ListEnum and _DictEnum) to serve as base classes, then
581 # derive the new type from the appropriate base class on the fly.
584 # Metaclass for Enum types
585 class MetaEnum(type):
586 def __init__(cls
, name
, bases
, init_dict
):
587 if init_dict
.has_key('map'):
588 if not isinstance(cls
.map, dict):
589 raise TypeError, "Enum-derived class attribute 'map' " \
590 "must be of type dict"
591 # build list of value strings from map
592 cls
.vals
= cls
.map.keys()
594 elif init_dict
.has_key('vals'):
595 if not isinstance(cls
.vals
, list):
596 raise TypeError, "Enum-derived class attribute 'vals' " \
597 "must be of type list"
598 # build string->value map from vals sequence
600 for idx
,val
in enumerate(cls
.vals
):
603 raise TypeError, "Enum-derived class must define "\
604 "attribute 'map' or 'vals'"
606 cls
.cxx_type
= name
+ '::Enum'
608 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
610 # Generate C++ class declaration for this enum type.
611 # Note that we wrap the enum in a class/struct to act as a namespace,
612 # so that the enum strings can be brief w/o worrying about collisions.
614 s
= 'struct %s {\n enum Enum {\n ' % cls
.__name
__
615 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
619 # Base class for enum types.
620 class Enum(ParamValue
):
621 __metaclass__
= MetaEnum
624 def __init__(self
, value
):
625 if value
not in self
.map:
626 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
633 # how big does a rounding error need to be before we warn about it?
634 frequency_tolerance
= 0.001 # 0.1%
636 class TickParamValue(NumericParamValue
):
638 cxx_predecls
= ['#include "sim/host.hh"']
639 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
640 '%import "sim/host.hh"']
642 class Latency(TickParamValue
):
643 def __init__(self
, value
):
644 if isinstance(value
, (Latency
, Clock
)):
645 self
.ticks
= value
.ticks
646 self
.value
= value
.value
647 elif isinstance(value
, Frequency
):
648 self
.ticks
= value
.ticks
649 self
.value
= 1.0 / value
.value
650 elif value
.endswith('t'):
652 self
.value
= int(value
[:-1])
655 self
.value
= convert
.toLatency(value
)
657 def __getattr__(self
, attr
):
658 if attr
in ('latency', 'period'):
660 if attr
== 'frequency':
661 return Frequency(self
)
662 raise AttributeError, "Latency object has no attribute '%s'" % attr
664 # convert latency to ticks
666 if self
.ticks
or self
.value
== 0:
667 return '%d' % self
.value
669 return '%d' % (ticks
.fromSeconds(self
.value
))
671 class Frequency(TickParamValue
):
672 def __init__(self
, value
):
673 if isinstance(value
, (Latency
, Clock
)):
677 self
.value
= 1.0 / value
.value
678 self
.ticks
= value
.ticks
679 elif isinstance(value
, Frequency
):
680 self
.value
= value
.value
681 self
.ticks
= value
.ticks
684 self
.value
= convert
.toFrequency(value
)
686 def __getattr__(self
, attr
):
687 if attr
== 'frequency':
689 if attr
in ('latency', 'period'):
691 raise AttributeError, "Frequency object has no attribute '%s'" % attr
693 # convert latency to ticks
695 if self
.ticks
or self
.value
== 0:
696 return '%d' % self
.value
698 return '%d' % (ticks
.fromSeconds(1.0 / self
.value
))
700 # A generic frequency and/or Latency value. Value is stored as a latency,
701 # but to avoid ambiguity this object does not support numeric ops (* or /).
702 # An explicit conversion to a Latency or Frequency must be made first.
703 class Clock(ParamValue
):
705 cxx_predecls
= ['#include "sim/host.hh"']
706 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
707 '%import "sim/host.hh"']
708 def __init__(self
, value
):
709 if isinstance(value
, (Latency
, Clock
)):
710 self
.ticks
= value
.ticks
711 self
.value
= value
.value
712 elif isinstance(value
, Frequency
):
713 self
.ticks
= value
.ticks
714 self
.value
= 1.0 / value
.value
715 elif value
.endswith('t'):
717 self
.value
= int(value
[:-1])
720 self
.value
= convert
.anyToLatency(value
)
722 def __getattr__(self
, attr
):
723 if attr
== 'frequency':
724 return Frequency(self
)
725 if attr
in ('latency', 'period'):
727 raise AttributeError, "Frequency object has no attribute '%s'" % attr
730 return self
.period
.ini_str()
732 class NetworkBandwidth(float,ParamValue
):
734 def __new__(cls
, value
):
735 # convert to bits per second
736 val
= convert
.toNetworkBandwidth(value
)
737 return super(cls
, NetworkBandwidth
).__new
__(cls
, val
)
743 # convert to seconds per byte
744 value
= 8.0 / float(self
)
745 # convert to ticks per byte
746 return '%f' % (ticks
.fromSeconds(value
))
748 class MemoryBandwidth(float,ParamValue
):
750 def __new__(self
, value
):
751 # we want the number of ticks per byte of data
752 val
= convert
.toMemoryBandwidth(value
)
753 return super(cls
, MemoryBandwidth
).__new
__(cls
, val
)
759 # convert to seconds per byte
760 value
= 1.0 / float(self
)
761 # convert to ticks per byte
762 return '%f' % (ticks
.fromSeconds(value
))
765 # "Constants"... handy aliases for various values.
768 # Special class for NULL pointers. Note the special check in
769 # make_param_value() above that lets these be assigned where a
770 # SimObject is required.
771 # only one copy of a particular node
772 class NullSimObject(object):
773 __metaclass__
= Singleton
778 def _instantiate(self
, parent
= None, path
= ''):
784 def unproxy(self
, base
):
787 def set_path(self
, parent
, name
):
792 # The only instance you'll ever need...
793 NULL
= NullSimObject()
795 def isNullPointer(value
):
796 return isinstance(value
, NullSimObject
)
798 # Some memory range specifications use this as a default upper bound.
801 AllMemory
= AddrRange(0, MaxAddr
)
804 #####################################################################
808 # Ports are used to interconnect objects in the memory system.
810 #####################################################################
812 # Port reference: encapsulates a reference to a particular port on a
813 # particular SimObject.
814 class PortRef(object):
815 def __init__(self
, simobj
, name
):
816 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
819 self
.peer
= None # not associated with another port yet
820 self
.ccConnected
= False # C++ port connection done?
821 self
.index
= -1 # always -1 for non-vector ports
824 return '%s.%s' % (self
.simobj
, self
.name
)
826 # for config.ini, print peer's name (not ours)
828 return str(self
.peer
)
830 def __getattr__(self
, attr
):
831 if attr
== 'peerObj':
832 # shorthand for proxies
833 return self
.peer
.simobj
834 raise AttributeError, "'%s' object has no attribute '%s'" % \
835 (self
.__class
__.__name
__, attr
)
837 # Full connection is symmetric (both ways). Called via
838 # SimObject.__setattr__ as a result of a port assignment, e.g.,
839 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
840 # e.g., "obj1.portA[3] = obj2.portB".
841 def connect(self
, other
):
842 if isinstance(other
, VectorPortRef
):
843 # reference to plain VectorPort is implicit append
844 other
= other
._get
_next
()
845 if self
.peer
and not proxy
.isproxy(self
.peer
):
846 print "warning: overwriting port", self
, \
847 "value", self
.peer
, "with", other
849 if proxy
.isproxy(other
):
850 other
.set_param_desc(PortParamDesc())
851 elif isinstance(other
, PortRef
):
852 if other
.peer
is not self
:
856 "assigning non-port reference '%s' to port '%s'" \
859 def clone(self
, simobj
, memo
):
860 if memo
.has_key(self
):
862 newRef
= copy
.copy(self
)
864 newRef
.simobj
= simobj
865 assert(isSimObject(newRef
.simobj
))
866 if self
.peer
and not proxy
.isproxy(self
.peer
):
867 peerObj
= self
.peer
.simobj(_memo
=memo
)
868 newRef
.peer
= self
.peer
.clone(peerObj
, memo
)
869 assert(not isinstance(newRef
.peer
, VectorPortRef
))
872 def unproxy(self
, simobj
):
873 assert(simobj
is self
.simobj
)
874 if proxy
.isproxy(self
.peer
):
876 realPeer
= self
.peer
.unproxy(self
.simobj
)
878 print "Error in unproxying port '%s' of %s" % \
879 (self
.name
, self
.simobj
.path())
881 self
.connect(realPeer
)
883 # Call C++ to create corresponding port connection between C++ objects
885 if self
.ccConnected
: # already done this
888 internal
.sim_object
.connectPorts(self
.simobj
.getCCObject(), self
.name
,
889 self
.index
, peer
.simobj
.getCCObject(), peer
.name
, peer
.index
)
890 self
.ccConnected
= True
891 peer
.ccConnected
= True
893 # A reference to an individual element of a VectorPort... much like a
894 # PortRef, but has an index.
895 class VectorPortElementRef(PortRef
):
896 def __init__(self
, simobj
, name
, index
):
897 PortRef
.__init
__(self
, simobj
, name
)
901 return '%s.%s[%d]' % (self
.simobj
, self
.name
, self
.index
)
903 # A reference to a complete vector-valued port (not just a single element).
904 # Can be indexed to retrieve individual VectorPortElementRef instances.
905 class VectorPortRef(object):
906 def __init__(self
, simobj
, name
):
907 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
913 return '%s.%s[:]' % (self
.simobj
, self
.name
)
915 # for config.ini, print peer's name (not ours)
917 return ' '.join([el
.ini_str() for el
in self
.elements
])
919 def __getitem__(self
, key
):
920 if not isinstance(key
, int):
921 raise TypeError, "VectorPort index must be integer"
922 if key
>= len(self
.elements
):
923 # need to extend list
924 ext
= [VectorPortElementRef(self
.simobj
, self
.name
, i
)
925 for i
in range(len(self
.elements
), key
+1)]
926 self
.elements
.extend(ext
)
927 return self
.elements
[key
]
930 return self
[len(self
.elements
)]
932 def __setitem__(self
, key
, value
):
933 if not isinstance(key
, int):
934 raise TypeError, "VectorPort index must be integer"
935 self
[key
].connect(value
)
937 def connect(self
, other
):
938 if isinstance(other
, (list, tuple)):
939 # Assign list of port refs to vector port.
940 # For now, append them... not sure if that's the right semantics
941 # or if it should replace the current vector.
943 self
._get
_next
().connect(ref
)
945 # scalar assignment to plain VectorPort is implicit append
946 self
._get
_next
().connect(other
)
948 def clone(self
, simobj
, memo
):
949 if memo
.has_key(self
):
951 newRef
= copy
.copy(self
)
953 newRef
.simobj
= simobj
954 assert(isSimObject(newRef
.simobj
))
955 newRef
.elements
= [el
.clone(simobj
, memo
) for el
in self
.elements
]
958 def unproxy(self
, simobj
):
959 [el
.unproxy(simobj
) for el
in self
.elements
]
962 [el
.ccConnect() for el
in self
.elements
]
964 # Port description object. Like a ParamDesc object, this represents a
965 # logical port in the SimObject class, not a particular port on a
966 # SimObject instance. The latter are represented by PortRef objects.
968 # Port("description") or Port(default, "description")
969 def __init__(self
, *args
):
973 self
.default
= args
[0]
976 raise TypeError, 'wrong number of arguments'
977 # self.name is set by SimObject class on assignment
978 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
980 # Generate a PortRef for this port on the given SimObject with the
982 def makeRef(self
, simobj
):
983 return PortRef(simobj
, self
.name
)
985 # Connect an instance of this port (on the given SimObject with
986 # the given name) with the port described by the supplied PortRef
987 def connect(self
, simobj
, ref
):
988 self
.makeRef(simobj
).connect(ref
)
990 # VectorPort description object. Like Port, but represents a vector
991 # of connections (e.g., as on a Bus).
992 class VectorPort(Port
):
993 def __init__(self
, *args
):
994 Port
.__init
__(self
, *args
)
997 def makeRef(self
, simobj
):
998 return VectorPortRef(simobj
, self
.name
)
1000 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1001 # proxy objects (via set_param_desc()) so that proxy error messages
1003 class PortParamDesc(object):
1004 __metaclass__
= Singleton
1010 __all__
= ['Param', 'VectorParam',
1011 'Enum', 'Bool', 'String', 'Float',
1012 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1013 'Int32', 'UInt32', 'Int64', 'UInt64',
1014 'Counter', 'Addr', 'Tick', 'Percent',
1015 'TcpPort', 'UdpPort', 'EthernetAddr',
1016 'MemorySize', 'MemorySize32',
1017 'Latency', 'Frequency', 'Clock',
1018 'NetworkBandwidth', 'MemoryBandwidth',
1019 'Range', 'AddrRange', 'TickRange',
1020 'MaxAddr', 'MaxTick', 'AllMemory',
1022 'NextEthernetAddr', 'NULL',
1023 'Port', 'VectorPort']
1025 # see comment on imports at end of __init__.py.
1026 from SimObject
import isSimObject
, isSimObjectSequence
, isSimObjectClass