f8a9f9ddd1371201adca0019ffb07138d684883c
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 #####################################################################
56 # Dummy base class to identify types that are legitimate for SimObject
58 class ParamValue(object):
63 # default for printing to .ini file is regular string conversion.
64 # will be overridden in some cases
68 # allows us to blithely call unproxy() on things without checking
69 # if they're really proxies or not
70 def unproxy(self
, base
):
73 # Regular parameter description.
74 class ParamDesc(object):
75 def __init__(self
, ptype_str
, ptype
, *args
, **kwargs
):
76 self
.ptype_str
= ptype_str
77 # remember ptype only if it is provided
85 self
.default
= args
[0]
88 raise TypeError, 'too many arguments'
90 if kwargs
.has_key('desc'):
91 assert(not hasattr(self
, 'desc'))
92 self
.desc
= kwargs
['desc']
95 if kwargs
.has_key('default'):
96 assert(not hasattr(self
, 'default'))
97 self
.default
= kwargs
['default']
101 raise TypeError, 'extra unknown kwargs %s' % kwargs
103 if not hasattr(self
, 'desc'):
104 raise TypeError, 'desc attribute missing'
106 def __getattr__(self
, attr
):
109 ptype
= eval(self
.ptype_str
, objects
.__dict
__)
110 if not isinstance(ptype
, type):
116 "Param qualifier '%s' is not a type" % self
.ptype_str
117 raise AttributeError, "'%s' object has no attribute '%s'" % \
118 (type(self
).__name
__, attr
)
120 def convert(self
, value
):
121 if isinstance(value
, proxy
.BaseProxy
):
122 value
.set_param_desc(self
)
124 if not hasattr(self
, 'ptype') and isNullPointer(value
):
125 # deferred evaluation of SimObject; continue to defer if
126 # we're just assigning a null pointer
128 if isinstance(value
, self
.ptype
):
130 if isNullPointer(value
) and isSimObjectClass(self
.ptype
):
132 return self
.ptype(value
)
134 def cxx_predecls(self
):
135 return self
.ptype
.cxx_predecls
137 def swig_predecls(self
):
138 return self
.ptype
.swig_predecls
141 return '%s %s;' % (self
.ptype
.cxx_type
, self
.name
)
143 # Vector-valued parameter description. Just like ParamDesc, except
144 # that the value is a vector (list) of the specified type instead of a
147 class VectorParamValue(list):
149 return ' '.join([v
.ini_str() for v
in self
])
151 def unproxy(self
, base
):
152 return [v
.unproxy(base
) for v
in self
]
154 class SimObjVector(VectorParamValue
):
159 class VectorParamDesc(ParamDesc
):
160 # Convert assigned value to appropriate type. If the RHS is not a
161 # list or tuple, it generates a single-element list.
162 def convert(self
, value
):
163 if isinstance(value
, (list, tuple)):
164 # list: coerce each element into new list
165 tmp_list
= [ ParamDesc
.convert(self
, v
) for v
in value
]
166 if isSimObjectSequence(tmp_list
):
167 return SimObjVector(tmp_list
)
169 return VectorParamValue(tmp_list
)
171 # singleton: leave it be (could coerce to a single-element
172 # list here, but for some historical reason we don't...
173 return ParamDesc
.convert(self
, value
)
175 def cxx_predecls(self
):
176 return ['#include <vector>'] + self
.ptype
.cxx_predecls
178 def swig_predecls(self
):
179 return ['%include "std_vector.i"'] + self
.ptype
.swig_predecls
182 return 'std::vector< %s > %s;' % (self
.ptype
.cxx_type
, self
.name
)
184 class ParamFactory(object):
185 def __init__(self
, param_desc_class
, ptype_str
= None):
186 self
.param_desc_class
= param_desc_class
187 self
.ptype_str
= ptype_str
189 def __getattr__(self
, attr
):
191 attr
= self
.ptype_str
+ '.' + attr
192 return ParamFactory(self
.param_desc_class
, attr
)
194 # E.g., Param.Int(5, "number of widgets")
195 def __call__(self
, *args
, **kwargs
):
196 caller_frame
= inspect
.currentframe().f_back
199 ptype
= eval(self
.ptype_str
,
200 caller_frame
.f_globals
, caller_frame
.f_locals
)
201 if not isinstance(ptype
, type):
203 "Param qualifier is not a type: %s" % ptype
205 # if name isn't defined yet, assume it's a SimObject, and
206 # try to resolve it later
208 return self
.param_desc_class(self
.ptype_str
, ptype
, *args
, **kwargs
)
210 Param
= ParamFactory(ParamDesc
)
211 VectorParam
= ParamFactory(VectorParamDesc
)
213 #####################################################################
217 # Though native Python types could be used to specify parameter types
218 # (the 'ptype' field of the Param and VectorParam classes), it's more
219 # flexible to define our own set of types. This gives us more control
220 # over how Python expressions are converted to values (via the
221 # __init__() constructor) and how these values are printed out (via
222 # the __str__() conversion method).
224 #####################################################################
226 # String-valued parameter. Just mixin the ParamValue class with the
227 # built-in str class.
228 class String(ParamValue
,str):
229 cxx_type
= 'std::string'
230 cxx_predecls
= ['#include <string>']
231 swig_predecls
= ['%include "std_string.i"\n' +
232 '%apply const std::string& {std::string *};']
235 # superclass for "numeric" parameter values, to emulate math
236 # operations in a type-safe way. e.g., a Latency times an int returns
237 # a new Latency object.
238 class NumericParamValue(ParamValue
):
240 return str(self
.value
)
243 return float(self
.value
)
246 return long(self
.value
)
249 return int(self
.value
)
251 # hook for bounds checking
255 def __mul__(self
, other
):
256 newobj
= self
.__class
__(self
)
257 newobj
.value
*= other
263 def __div__(self
, other
):
264 newobj
= self
.__class
__(self
)
265 newobj
.value
/= other
269 def __sub__(self
, other
):
270 newobj
= self
.__class
__(self
)
271 newobj
.value
-= other
275 # Metaclass for bounds-checked integer parameters. See CheckedInt.
276 class CheckedIntType(type):
277 def __init__(cls
, name
, bases
, dict):
278 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
280 # CheckedInt is an abstract base class, so we actually don't
281 # want to do any processing on it... the rest of this code is
282 # just for classes that derive from CheckedInt.
283 if name
== 'CheckedInt':
286 if not cls
.cxx_predecls
:
287 # most derived types require this, so we just do it here once
288 cls
.cxx_predecls
= ['#include "sim/host.hh"']
290 if not cls
.swig_predecls
:
291 # most derived types require this, so we just do it here once
292 cls
.swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
293 '%import "sim/host.hh"']
295 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
296 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
297 panic("CheckedInt subclass %s must define either\n" \
298 " 'min' and 'max' or 'size' and 'unsigned'\n" \
302 cls
.max = 2 ** cls
.size
- 1
304 cls
.min = -(2 ** (cls
.size
- 1))
305 cls
.max = (2 ** (cls
.size
- 1)) - 1
307 # Abstract superclass for bounds-checked integer parameters. This
308 # class is subclassed to generate parameter classes with specific
309 # bounds. Initialization of the min and max bounds is done in the
310 # metaclass CheckedIntType.__init__.
311 class CheckedInt(NumericParamValue
):
312 __metaclass__
= CheckedIntType
315 if not self
.min <= self
.value
<= self
.max:
316 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
317 (self
.min, self
.value
, self
.max)
319 def __init__(self
, value
):
320 if isinstance(value
, str):
321 self
.value
= convert
.toInteger(value
)
322 elif isinstance(value
, (int, long, float, NumericParamValue
)):
323 self
.value
= long(value
)
325 raise TypeError, "Can't convert object of type %s to CheckedInt" \
326 % type(value
).__name
__
329 class Int(CheckedInt
): cxx_type
= 'int'; size
= 32; unsigned
= False
330 class Unsigned(CheckedInt
): cxx_type
= 'unsigned'; size
= 32; unsigned
= True
332 class Int8(CheckedInt
): cxx_type
= 'int8_t'; size
= 8; unsigned
= False
333 class UInt8(CheckedInt
): cxx_type
= 'uint8_t'; size
= 8; unsigned
= True
334 class Int16(CheckedInt
): cxx_type
= 'int16_t'; size
= 16; unsigned
= False
335 class UInt16(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
336 class Int32(CheckedInt
): cxx_type
= 'int32_t'; size
= 32; unsigned
= False
337 class UInt32(CheckedInt
): cxx_type
= 'uint32_t'; size
= 32; unsigned
= True
338 class Int64(CheckedInt
): cxx_type
= 'int64_t'; size
= 64; unsigned
= False
339 class UInt64(CheckedInt
): cxx_type
= 'uint64_t'; size
= 64; unsigned
= True
341 class Counter(CheckedInt
): cxx_type
= 'Counter'; size
= 64; unsigned
= True
342 class Tick(CheckedInt
): cxx_type
= 'Tick'; size
= 64; unsigned
= True
343 class TcpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
344 class UdpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
346 class Percent(CheckedInt
): cxx_type
= 'int'; min = 0; max = 100
348 class Float(ParamValue
, float):
351 class MemorySize(CheckedInt
):
352 cxx_type
= 'uint64_t'
355 def __init__(self
, value
):
356 if isinstance(value
, MemorySize
):
357 self
.value
= value
.value
359 self
.value
= convert
.toMemorySize(value
)
362 class MemorySize32(CheckedInt
):
365 def __init__(self
, value
):
366 if isinstance(value
, MemorySize
):
367 self
.value
= value
.value
369 self
.value
= convert
.toMemorySize(value
)
372 class Addr(CheckedInt
):
374 cxx_predecls
= ['#include "targetarch/isa_traits.hh"']
377 def __init__(self
, value
):
378 if isinstance(value
, Addr
):
379 self
.value
= value
.value
382 self
.value
= convert
.toMemorySize(value
)
384 self
.value
= long(value
)
386 def __add__(self
, other
):
387 if isinstance(other
, Addr
):
388 return self
.value
+ other
.value
390 return self
.value
+ other
393 class MetaRange(type):
394 def __init__(cls
, name
, bases
, dict):
395 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
398 cls
.cxx_type
= 'Range< %s >' % cls
.type.cxx_type
400 ['#include "base/range.hh"'] + cls
.type.cxx_predecls
402 class Range(ParamValue
):
403 __metaclass__
= MetaRange
404 type = Int
# default; can be overridden in subclasses
405 def __init__(self
, *args
, **kwargs
):
406 def handle_kwargs(self
, kwargs
):
408 self
.second
= self
.type(kwargs
.pop('end'))
409 elif 'size' in kwargs
:
410 self
.second
= self
.first
+ self
.type(kwargs
.pop('size')) - 1
412 raise TypeError, "Either end or size must be specified"
415 self
.first
= self
.type(kwargs
.pop('start'))
416 handle_kwargs(self
, kwargs
)
420 self
.first
= self
.type(args
[0])
421 handle_kwargs(self
, kwargs
)
422 elif isinstance(args
[0], Range
):
423 self
.first
= self
.type(args
[0].first
)
424 self
.second
= self
.type(args
[0].second
)
426 self
.first
= self
.type(0)
427 self
.second
= self
.type(args
[0]) - 1
430 self
.first
= self
.type(args
[0])
431 self
.second
= self
.type(args
[1])
433 raise TypeError, "Too many arguments specified"
436 raise TypeError, "too many keywords: %s" % kwargs
.keys()
439 return '%s:%s' % (self
.first
, self
.second
)
441 class AddrRange(Range
):
444 class TickRange(Range
):
447 # Boolean parameter type. Python doesn't let you subclass bool, since
448 # it doesn't want to let you create multiple instances of True and
449 # False. Thus this is a little more complicated than String.
450 class Bool(ParamValue
):
452 def __init__(self
, value
):
454 self
.value
= convert
.toBool(value
)
456 self
.value
= bool(value
)
459 return str(self
.value
)
466 def IncEthernetAddr(addr
, val
= 1):
467 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
469 for i
in (5, 4, 3, 2, 1):
470 val
,rem
= divmod(bytes
[i
], 256)
475 assert(bytes
[0] <= 255)
476 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
478 class NextEthernetAddr(object):
479 addr
= "00:90:00:00:00:01"
481 def __init__(self
, inc
= 1):
482 self
.value
= NextEthernetAddr
.addr
483 NextEthernetAddr
.addr
= IncEthernetAddr(NextEthernetAddr
.addr
, inc
)
485 class EthernetAddr(ParamValue
):
486 cxx_type
= 'Net::EthAddr'
487 cxx_predecls
= ['#include "base/inet.hh"']
488 swig_predecls
= ['class Net::EthAddr;']
489 def __init__(self
, value
):
490 if value
== NextEthernetAddr
:
494 if not isinstance(value
, str):
495 raise TypeError, "expected an ethernet address and didn't get one"
497 bytes
= value
.split(':')
499 raise TypeError, 'invalid ethernet address %s' % value
502 if not 0 <= int(byte
) <= 256:
503 raise TypeError, 'invalid ethernet address %s' % value
507 def unproxy(self
, base
):
508 if self
.value
== NextEthernetAddr
:
509 self
.addr
= self
.value().value
513 if self
.value
== NextEthernetAddr
:
514 if hasattr(self
, 'addr'):
517 return "NextEthernetAddr (unresolved)"
521 time_formats
= [ "%a %b %d %H:%M:%S %Z %Y",
522 "%a %b %d %H:%M:%S %Z %Y",
534 def parse_time(value
):
535 from time
import gmtime
, strptime
, struct_time
, time
536 from datetime
import datetime
, date
538 if isinstance(value
, struct_time
):
541 if isinstance(value
, (int, long)):
544 if isinstance(value
, (datetime
, date
)):
545 return value
.timetuple()
547 if isinstance(value
, str):
548 if value
in ('Now', 'Today'):
549 return time
.gmtime(time
.time())
551 for format
in time_formats
:
553 return strptime(value
, format
)
557 raise ValueError, "Could not parse '%s' as a time" % value
559 class Time(ParamValue
):
561 def __init__(self
, value
):
562 self
.value
= parse_time(value
)
566 return ' '.join([ str(tm
[i
]) for i
in xrange(8)])
571 # Enumerated types are a little more complex. The user specifies the
572 # type as Enum(foo) where foo is either a list or dictionary of
573 # alternatives (typically strings, but not necessarily so). (In the
574 # long run, the integer value of the parameter will be the list index
575 # or the corresponding dictionary value. For now, since we only check
576 # that the alternative is valid and then spit it into a .ini file,
577 # there's not much point in using the dictionary.)
579 # What Enum() must do is generate a new type encapsulating the
580 # provided list/dictionary so that specific values of the parameter
581 # can be instances of that type. We define two hidden internal
582 # classes (_ListEnum and _DictEnum) to serve as base classes, then
583 # derive the new type from the appropriate base class on the fly.
586 # Metaclass for Enum types
587 class MetaEnum(type):
588 def __init__(cls
, name
, bases
, init_dict
):
589 if init_dict
.has_key('map'):
590 if not isinstance(cls
.map, dict):
591 raise TypeError, "Enum-derived class attribute 'map' " \
592 "must be of type dict"
593 # build list of value strings from map
594 cls
.vals
= cls
.map.keys()
596 elif init_dict
.has_key('vals'):
597 if not isinstance(cls
.vals
, list):
598 raise TypeError, "Enum-derived class attribute 'vals' " \
599 "must be of type list"
600 # build string->value map from vals sequence
602 for idx
,val
in enumerate(cls
.vals
):
605 raise TypeError, "Enum-derived class must define "\
606 "attribute 'map' or 'vals'"
608 cls
.cxx_type
= name
+ '::Enum'
610 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
612 # Generate C++ class declaration for this enum type.
613 # Note that we wrap the enum in a class/struct to act as a namespace,
614 # so that the enum strings can be brief w/o worrying about collisions.
616 s
= 'struct %s {\n enum Enum {\n ' % cls
.__name
__
617 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
621 # Base class for enum types.
622 class Enum(ParamValue
):
623 __metaclass__
= MetaEnum
626 def __init__(self
, value
):
627 if value
not in self
.map:
628 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 # convert a floting-point # of ticks to integer, and warn if rounding
641 # discards too much precision
642 def tick_check(float_ticks
):
645 int_ticks
= int(round(float_ticks
))
646 err
= (float_ticks
- int_ticks
) / float_ticks
647 if err
> frequency_tolerance
:
648 print >> sys
.stderr
, "Warning: rounding error > tolerance"
649 print >> sys
.stderr
, " %f rounded to %d" % (float_ticks
, int_ticks
)
653 def getLatency(value
):
654 if isinstance(value
, Latency
) or isinstance(value
, Clock
):
656 elif isinstance(value
, Frequency
) or isinstance(value
, RootClock
):
657 return 1 / value
.value
658 elif isinstance(value
, str):
660 return convert
.toLatency(value
)
663 return 1 / convert
.toFrequency(value
)
666 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
669 class Latency(NumericParamValue
):
671 cxx_predecls
= ['#include "sim/host.hh"']
672 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
673 '%import "sim/host.hh"']
674 def __init__(self
, value
):
675 self
.value
= getLatency(value
)
677 def __getattr__(self
, attr
):
678 if attr
in ('latency', 'period'):
680 if attr
== 'frequency':
681 return Frequency(self
)
682 raise AttributeError, "Latency object has no attribute '%s'" % attr
684 # convert latency to ticks
686 return str(tick_check(self
.value
* ticks_per_sec
))
688 class Frequency(NumericParamValue
):
690 cxx_predecls
= ['#include "sim/host.hh"']
691 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
692 '%import "sim/host.hh"']
693 def __init__(self
, value
):
694 self
.value
= 1 / getLatency(value
)
696 def __getattr__(self
, attr
):
697 if attr
== 'frequency':
699 if attr
in ('latency', 'period'):
701 raise AttributeError, "Frequency object has no attribute '%s'" % attr
703 # convert frequency to ticks per period
705 return self
.period
.ini_str()
707 # Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
708 # We can't inherit from Frequency because we don't want it to be directly
709 # assignable to a regular Frequency parameter.
710 class RootClock(ParamValue
):
712 cxx_predecls
= ['#include "sim/host.hh"']
713 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
714 '%import "sim/host.hh"']
715 def __init__(self
, value
):
716 self
.value
= 1 / getLatency(value
)
718 def __getattr__(self
, attr
):
719 if attr
== 'frequency':
720 return Frequency(self
)
721 if attr
in ('latency', 'period'):
723 raise AttributeError, "Frequency object has no attribute '%s'" % attr
726 return str(tick_check(self
.value
))
728 # A generic frequency and/or Latency value. Value is stored as a latency,
729 # but to avoid ambiguity this object does not support numeric ops (* or /).
730 # An explicit conversion to a Latency or Frequency must be made first.
731 class Clock(ParamValue
):
733 cxx_predecls
= ['#include "sim/host.hh"']
734 swig_predecls
= ['%import "python/m5/swig/stdint.i"\n' +
735 '%import "sim/host.hh"']
736 def __init__(self
, value
):
737 self
.value
= getLatency(value
)
739 def __getattr__(self
, attr
):
740 if attr
== 'frequency':
741 return Frequency(self
)
742 if attr
in ('latency', 'period'):
744 raise AttributeError, "Frequency object has no attribute '%s'" % attr
747 return self
.period
.ini_str()
749 class NetworkBandwidth(float,ParamValue
):
751 def __new__(cls
, value
):
752 val
= convert
.toNetworkBandwidth(value
) / 8.0
753 return super(cls
, NetworkBandwidth
).__new
__(cls
, val
)
759 return '%f' % (ticks_per_sec
/ float(self
))
761 class MemoryBandwidth(float,ParamValue
):
763 def __new__(self
, value
):
764 val
= convert
.toMemoryBandwidth(value
)
765 return super(cls
, MemoryBandwidth
).__new
__(cls
, val
)
771 return '%f' % (ticks_per_sec
/ float(self
))
774 # "Constants"... handy aliases for various values.
777 # Special class for NULL pointers. Note the special check in
778 # make_param_value() above that lets these be assigned where a
779 # SimObject is required.
780 # only one copy of a particular node
781 class NullSimObject(object):
782 __metaclass__
= Singleton
787 def _instantiate(self
, parent
= None, path
= ''):
793 def unproxy(self
, base
):
796 def set_path(self
, parent
, name
):
801 # The only instance you'll ever need...
802 NULL
= NullSimObject()
804 def isNullPointer(value
):
805 return isinstance(value
, NullSimObject
)
807 # Some memory range specifications use this as a default upper bound.
810 AllMemory
= AddrRange(0, MaxAddr
)
813 #####################################################################
817 # Ports are used to interconnect objects in the memory system.
819 #####################################################################
821 # Port reference: encapsulates a reference to a particular port on a
822 # particular SimObject.
823 class PortRef(object):
824 def __init__(self
, simobj
, name
):
825 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
828 self
.peer
= None # not associated with another port yet
829 self
.ccConnected
= False # C++ port connection done?
830 self
.index
= -1 # always -1 for non-vector ports
833 return '%s.%s' % (self
.simobj
, self
.name
)
835 # for config.ini, print peer's name (not ours)
837 return str(self
.peer
)
839 def __getattr__(self
, attr
):
840 if attr
== 'peerObj':
841 # shorthand for proxies
842 return self
.peer
.simobj
843 raise AttributeError, "'%s' object has no attribute '%s'" % \
844 (self
.__class
__.__name
__, attr
)
846 # Full connection is symmetric (both ways). Called via
847 # SimObject.__setattr__ as a result of a port assignment, e.g.,
848 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
849 # e.g., "obj1.portA[3] = obj2.portB".
850 def connect(self
, other
):
851 if isinstance(other
, VectorPortRef
):
852 # reference to plain VectorPort is implicit append
853 other
= other
._get
_next
()
854 if self
.peer
and not proxy
.isproxy(self
.peer
):
855 print "warning: overwriting port", self
, \
856 "value", self
.peer
, "with", other
858 if proxy
.isproxy(other
):
859 other
.set_param_desc(PortParamDesc())
860 elif isinstance(other
, PortRef
):
861 if other
.peer
is not self
:
865 "assigning non-port reference '%s' to port '%s'" \
868 def clone(self
, simobj
, memo
):
869 if memo
.has_key(self
):
871 newRef
= copy
.copy(self
)
873 newRef
.simobj
= simobj
874 assert(isSimObject(newRef
.simobj
))
875 if self
.peer
and not proxy
.isproxy(self
.peer
):
876 peerObj
= self
.peer
.simobj(_memo
=memo
)
877 newRef
.peer
= self
.peer
.clone(peerObj
, memo
)
878 assert(not isinstance(newRef
.peer
, VectorPortRef
))
881 def unproxy(self
, simobj
):
882 assert(simobj
is self
.simobj
)
883 if proxy
.isproxy(self
.peer
):
885 realPeer
= self
.peer
.unproxy(self
.simobj
)
887 print "Error in unproxying port '%s' of %s" % \
888 (self
.name
, self
.simobj
.path())
890 self
.connect(realPeer
)
892 # Call C++ to create corresponding port connection between C++ objects
894 if self
.ccConnected
: # already done this
897 internal
.main
.connectPorts(self
.simobj
.getCCObject(), self
.name
,
898 self
.index
, peer
.simobj
.getCCObject(),
899 peer
.name
, peer
.index
)
900 self
.ccConnected
= True
901 peer
.ccConnected
= True
903 # A reference to an individual element of a VectorPort... much like a
904 # PortRef, but has an index.
905 class VectorPortElementRef(PortRef
):
906 def __init__(self
, simobj
, name
, index
):
907 PortRef
.__init
__(self
, simobj
, name
)
911 return '%s.%s[%d]' % (self
.simobj
, self
.name
, self
.index
)
913 # A reference to a complete vector-valued port (not just a single element).
914 # Can be indexed to retrieve individual VectorPortElementRef instances.
915 class VectorPortRef(object):
916 def __init__(self
, simobj
, name
):
917 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
923 return '%s.%s[:]' % (self
.simobj
, self
.name
)
925 # for config.ini, print peer's name (not ours)
927 return ' '.join([el
.ini_str() for el
in self
.elements
])
929 def __getitem__(self
, key
):
930 if not isinstance(key
, int):
931 raise TypeError, "VectorPort index must be integer"
932 if key
>= len(self
.elements
):
933 # need to extend list
934 ext
= [VectorPortElementRef(self
.simobj
, self
.name
, i
)
935 for i
in range(len(self
.elements
), key
+1)]
936 self
.elements
.extend(ext
)
937 return self
.elements
[key
]
940 return self
[len(self
.elements
)]
942 def __setitem__(self
, key
, value
):
943 if not isinstance(key
, int):
944 raise TypeError, "VectorPort index must be integer"
945 self
[key
].connect(value
)
947 def connect(self
, other
):
948 if isinstance(other
, (list, tuple)):
949 # Assign list of port refs to vector port.
950 # For now, append them... not sure if that's the right semantics
951 # or if it should replace the current vector.
953 self
._get
_next
().connect(ref
)
955 # scalar assignment to plain VectorPort is implicit append
956 self
._get
_next
().connect(other
)
958 def clone(self
, simobj
, memo
):
959 if memo
.has_key(self
):
961 newRef
= copy
.copy(self
)
963 newRef
.simobj
= simobj
964 assert(isSimObject(newRef
.simobj
))
965 newRef
.elements
= [el
.clone(simobj
, memo
) for el
in self
.elements
]
968 def unproxy(self
, simobj
):
969 [el
.unproxy(simobj
) for el
in self
.elements
]
972 [el
.ccConnect() for el
in self
.elements
]
974 # Port description object. Like a ParamDesc object, this represents a
975 # logical port in the SimObject class, not a particular port on a
976 # SimObject instance. The latter are represented by PortRef objects.
978 # Port("description") or Port(default, "description")
979 def __init__(self
, *args
):
983 self
.default
= args
[0]
986 raise TypeError, 'wrong number of arguments'
987 # self.name is set by SimObject class on assignment
988 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
990 # Generate a PortRef for this port on the given SimObject with the
992 def makeRef(self
, simobj
):
993 return PortRef(simobj
, self
.name
)
995 # Connect an instance of this port (on the given SimObject with
996 # the given name) with the port described by the supplied PortRef
997 def connect(self
, simobj
, ref
):
998 self
.makeRef(simobj
).connect(ref
)
1000 # VectorPort description object. Like Port, but represents a vector
1001 # of connections (e.g., as on a Bus).
1002 class VectorPort(Port
):
1003 def __init__(self
, *args
):
1004 Port
.__init
__(self
, *args
)
1007 def makeRef(self
, simobj
):
1008 return VectorPortRef(simobj
, self
.name
)
1010 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1011 # proxy objects (via set_param_desc()) so that proxy error messages
1013 class PortParamDesc(object):
1014 __metaclass__
= Singleton
1020 __all__
= ['Param', 'VectorParam',
1021 'Enum', 'Bool', 'String', 'Float',
1022 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1023 'Int32', 'UInt32', 'Int64', 'UInt64',
1024 'Counter', 'Addr', 'Tick', 'Percent',
1025 'TcpPort', 'UdpPort', 'EthernetAddr',
1026 'MemorySize', 'MemorySize32',
1027 'Latency', 'Frequency', 'RootClock', 'Clock',
1028 'NetworkBandwidth', 'MemoryBandwidth',
1029 'Range', 'AddrRange', 'TickRange',
1030 'MaxAddr', 'MaxTick', 'AllMemory',
1032 'NextEthernetAddr', 'NULL',
1033 'Port', 'VectorPort']
1035 # see comment on imports at end of __init__.py.
1036 from SimObject
import isSimObject
, isSimObjectSequence
, isSimObjectClass