params.py (4762:c94e103c83ad) params.py (4859:97c7749896a6)
1# Copyright (c) 2004-2006 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Steve Reinhardt
28# Nathan Binkert
29
30#####################################################################
31#
32# Parameter description classes
33#
34# The _params dictionary in each class maps parameter names to either
35# a Param or a VectorParam object. These objects contain the
36# parameter description string, the parameter type, and the default
37# value (if any). The convert() method on these objects is used to
38# force whatever value is assigned to the parameter to the appropriate
39# type.
40#
41# Note that the default values are loaded into the class's attribute
42# space when the parameter dictionary is initialized (in
43# MetaSimObject._new_param()); after that point they aren't used.
44#
45#####################################################################
46
47import copy
48import datetime
49import inspect
50import re
51import sys
52import time
53
54import convert
55import proxy
56import ticks
57from util import *
58
59import SimObject
60
61def isSimObject(*args, **kwargs):
62 return SimObject.isSimObject(*args, **kwargs)
63
64def isSimObjectSequence(*args, **kwargs):
65 return SimObject.isSimObjectSequence(*args, **kwargs)
66
67def isSimObjectClass(*args, **kwargs):
68 return SimObject.isSimObjectClass(*args, **kwargs)
69
70# Dummy base class to identify types that are legitimate for SimObject
71# parameters.
72class ParamValue(object):
73
74 cxx_predecls = []
75 swig_predecls = []
76
77 # default for printing to .ini file is regular string conversion.
78 # will be overridden in some cases
79 def ini_str(self):
80 return str(self)
81
82 # allows us to blithely call unproxy() on things without checking
83 # if they're really proxies or not
84 def unproxy(self, base):
85 return self
86
87# Regular parameter description.
88class ParamDesc(object):
89 def __init__(self, ptype_str, ptype, *args, **kwargs):
90 self.ptype_str = ptype_str
91 # remember ptype only if it is provided
92 if ptype != None:
93 self.ptype = ptype
94
95 if args:
96 if len(args) == 1:
97 self.desc = args[0]
98 elif len(args) == 2:
99 self.default = args[0]
100 self.desc = args[1]
101 else:
102 raise TypeError, 'too many arguments'
103
104 if kwargs.has_key('desc'):
105 assert(not hasattr(self, 'desc'))
106 self.desc = kwargs['desc']
107 del kwargs['desc']
108
109 if kwargs.has_key('default'):
110 assert(not hasattr(self, 'default'))
111 self.default = kwargs['default']
112 del kwargs['default']
113
114 if kwargs:
115 raise TypeError, 'extra unknown kwargs %s' % kwargs
116
117 if not hasattr(self, 'desc'):
118 raise TypeError, 'desc attribute missing'
119
120 def __getattr__(self, attr):
121 if attr == 'ptype':
122 try:
123 ptype = SimObject.allClasses[self.ptype_str]
124 if not isinstance(ptype, type):
125 raise NameError
126 self.ptype = ptype
127 return ptype
128 except NameError:
129 raise
130 #raise TypeError, \
131 # "Param qualifier '%s' is not a type" % self.ptype_str
132 raise AttributeError, "'%s' object has no attribute '%s'" % \
133 (type(self).__name__, attr)
134
135 def convert(self, value):
136 if isinstance(value, proxy.BaseProxy):
137 value.set_param_desc(self)
138 return value
139 if not hasattr(self, 'ptype') and isNullPointer(value):
140 # deferred evaluation of SimObject; continue to defer if
141 # we're just assigning a null pointer
142 return value
143 if isinstance(value, self.ptype):
144 return value
145 if isNullPointer(value) and isSimObjectClass(self.ptype):
146 return value
147 return self.ptype(value)
148
149 def cxx_predecls(self):
150 return self.ptype.cxx_predecls
151
152 def swig_predecls(self):
153 return self.ptype.swig_predecls
154
155 def cxx_decl(self):
156 return '%s %s;' % (self.ptype.cxx_type, self.name)
157
158# Vector-valued parameter description. Just like ParamDesc, except
159# that the value is a vector (list) of the specified type instead of a
160# single value.
161
162class VectorParamValue(list):
163 def ini_str(self):
164 return ' '.join([v.ini_str() for v in self])
165
166 def getValue(self):
167 return [ v.getValue() for v in self ]
168
169 def unproxy(self, base):
170 return [v.unproxy(base) for v in self]
171
172class SimObjVector(VectorParamValue):
173 def print_ini(self):
174 for v in self:
175 v.print_ini()
176
177class VectorParamDesc(ParamDesc):
178 # Convert assigned value to appropriate type. If the RHS is not a
179 # list or tuple, it generates a single-element list.
180 def convert(self, value):
181 if isinstance(value, (list, tuple)):
182 # list: coerce each element into new list
183 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
184 else:
185 # singleton: coerce to a single-element list
186 tmp_list = [ ParamDesc.convert(self, value) ]
187
188 if isSimObjectSequence(tmp_list):
189 return SimObjVector(tmp_list)
190 else:
191 return VectorParamValue(tmp_list)
192
193 def swig_predecls(self):
194 return ['%%include "%s_vptype.i"' % self.ptype_str]
195
196 def swig_decl(self):
197 cxx_type = re.sub('std::', '', self.ptype.cxx_type)
198 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
199 (self.ptype_str, cxx_type)
200 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
201
202 def cxx_predecls(self):
203 return ['#include <vector>'] + self.ptype.cxx_predecls
204
205 def cxx_decl(self):
206 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
207
208class ParamFactory(object):
209 def __init__(self, param_desc_class, ptype_str = None):
210 self.param_desc_class = param_desc_class
211 self.ptype_str = ptype_str
212
213 def __getattr__(self, attr):
214 if self.ptype_str:
215 attr = self.ptype_str + '.' + attr
216 return ParamFactory(self.param_desc_class, attr)
217
218 # E.g., Param.Int(5, "number of widgets")
219 def __call__(self, *args, **kwargs):
220 caller_frame = inspect.currentframe().f_back
221 ptype = None
222 try:
223 ptype = eval(self.ptype_str,
224 caller_frame.f_globals, caller_frame.f_locals)
225 if not isinstance(ptype, type):
226 raise TypeError, \
227 "Param qualifier is not a type: %s" % ptype
228 except NameError:
229 # if name isn't defined yet, assume it's a SimObject, and
230 # try to resolve it later
231 pass
232 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
233
234Param = ParamFactory(ParamDesc)
235VectorParam = ParamFactory(VectorParamDesc)
236
237#####################################################################
238#
239# Parameter Types
240#
241# Though native Python types could be used to specify parameter types
242# (the 'ptype' field of the Param and VectorParam classes), it's more
243# flexible to define our own set of types. This gives us more control
244# over how Python expressions are converted to values (via the
245# __init__() constructor) and how these values are printed out (via
246# the __str__() conversion method).
247#
248#####################################################################
249
250# String-valued parameter. Just mixin the ParamValue class with the
251# built-in str class.
252class String(ParamValue,str):
253 cxx_type = 'std::string'
254 cxx_predecls = ['#include <string>']
255 swig_predecls = ['%include "std_string.i"\n' +
256 '%apply const std::string& {std::string *};']
257 swig_predecls = ['%include "std_string.i"' ]
258
259 def getValue(self):
260 return self
261
262# superclass for "numeric" parameter values, to emulate math
263# operations in a type-safe way. e.g., a Latency times an int returns
264# a new Latency object.
265class NumericParamValue(ParamValue):
266 def __str__(self):
267 return str(self.value)
268
269 def __float__(self):
270 return float(self.value)
271
272 def __long__(self):
273 return long(self.value)
274
275 def __int__(self):
276 return int(self.value)
277
278 # hook for bounds checking
279 def _check(self):
280 return
281
282 def __mul__(self, other):
283 newobj = self.__class__(self)
284 newobj.value *= other
285 newobj._check()
286 return newobj
287
288 __rmul__ = __mul__
289
290 def __div__(self, other):
291 newobj = self.__class__(self)
292 newobj.value /= other
293 newobj._check()
294 return newobj
295
296 def __sub__(self, other):
297 newobj = self.__class__(self)
298 newobj.value -= other
299 newobj._check()
300 return newobj
301
302# Metaclass for bounds-checked integer parameters. See CheckedInt.
303class CheckedIntType(type):
304 def __init__(cls, name, bases, dict):
305 super(CheckedIntType, cls).__init__(name, bases, dict)
306
307 # CheckedInt is an abstract base class, so we actually don't
308 # want to do any processing on it... the rest of this code is
309 # just for classes that derive from CheckedInt.
310 if name == 'CheckedInt':
311 return
312
313 if not cls.cxx_predecls:
314 # most derived types require this, so we just do it here once
315 cls.cxx_predecls = ['#include "sim/host.hh"']
316
317 if not cls.swig_predecls:
318 # most derived types require this, so we just do it here once
319 cls.swig_predecls = ['%import "stdint.i"\n' +
320 '%import "sim/host.hh"']
321
322 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
323 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
324 panic("CheckedInt subclass %s must define either\n" \
325 " 'min' and 'max' or 'size' and 'unsigned'\n" \
326 % name);
327 if cls.unsigned:
328 cls.min = 0
329 cls.max = 2 ** cls.size - 1
330 else:
331 cls.min = -(2 ** (cls.size - 1))
332 cls.max = (2 ** (cls.size - 1)) - 1
333
334# Abstract superclass for bounds-checked integer parameters. This
335# class is subclassed to generate parameter classes with specific
336# bounds. Initialization of the min and max bounds is done in the
337# metaclass CheckedIntType.__init__.
338class CheckedInt(NumericParamValue):
339 __metaclass__ = CheckedIntType
340
341 def _check(self):
342 if not self.min <= self.value <= self.max:
343 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
344 (self.min, self.value, self.max)
345
346 def __init__(self, value):
347 if isinstance(value, str):
348 self.value = convert.toInteger(value)
349 elif isinstance(value, (int, long, float, NumericParamValue)):
350 self.value = long(value)
351 else:
352 raise TypeError, "Can't convert object of type %s to CheckedInt" \
353 % type(value).__name__
354 self._check()
355
356 def getValue(self):
357 return long(self.value)
358
359class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
360class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
361
362class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
363class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
364class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
365class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
366class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
367class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
368class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
369class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
370
371class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
372class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
373class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
374class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
375
376class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
377
378class Float(ParamValue, float):
379 cxx_type = 'double'
380
381 def getValue(self):
382 return float(self.value)
383
384class MemorySize(CheckedInt):
385 cxx_type = 'uint64_t'
386 size = 64
387 unsigned = True
388 def __init__(self, value):
389 if isinstance(value, MemorySize):
390 self.value = value.value
391 else:
392 self.value = convert.toMemorySize(value)
393 self._check()
394
395class MemorySize32(CheckedInt):
396 cxx_type = 'uint32_t'
397 size = 32
398 unsigned = True
399 def __init__(self, value):
400 if isinstance(value, MemorySize):
401 self.value = value.value
402 else:
403 self.value = convert.toMemorySize(value)
404 self._check()
405
406class Addr(CheckedInt):
407 cxx_type = 'Addr'
408 cxx_predecls = ['#include "arch/isa_traits.hh"']
409 size = 64
410 unsigned = True
411 def __init__(self, value):
412 if isinstance(value, Addr):
413 self.value = value.value
414 else:
415 try:
416 self.value = convert.toMemorySize(value)
417 except TypeError:
418 self.value = long(value)
419 self._check()
420 def __add__(self, other):
421 if isinstance(other, Addr):
422 return self.value + other.value
423 else:
424 return self.value + other
425
426
427class MetaRange(type):
428 def __init__(cls, name, bases, dict):
429 super(MetaRange, cls).__init__(name, bases, dict)
430 if name == 'Range':
431 return
432 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
433 cls.cxx_predecls = \
434 ['#include "base/range.hh"'] + cls.type.cxx_predecls
435
436class Range(ParamValue):
437 __metaclass__ = MetaRange
438 type = Int # default; can be overridden in subclasses
439 def __init__(self, *args, **kwargs):
440 def handle_kwargs(self, kwargs):
441 if 'end' in kwargs:
442 self.second = self.type(kwargs.pop('end'))
443 elif 'size' in kwargs:
444 self.second = self.first + self.type(kwargs.pop('size')) - 1
445 else:
446 raise TypeError, "Either end or size must be specified"
447
448 if len(args) == 0:
449 self.first = self.type(kwargs.pop('start'))
450 handle_kwargs(self, kwargs)
451
452 elif len(args) == 1:
453 if kwargs:
454 self.first = self.type(args[0])
455 handle_kwargs(self, kwargs)
456 elif isinstance(args[0], Range):
457 self.first = self.type(args[0].first)
458 self.second = self.type(args[0].second)
459 else:
460 self.first = self.type(0)
461 self.second = self.type(args[0]) - 1
462
463 elif len(args) == 2:
464 self.first = self.type(args[0])
465 self.second = self.type(args[1])
466 else:
467 raise TypeError, "Too many arguments specified"
468
469 if kwargs:
470 raise TypeError, "too many keywords: %s" % kwargs.keys()
471
472 def __str__(self):
473 return '%s:%s' % (self.first, self.second)
474
475class AddrRange(Range):
476 type = Addr
477 swig_predecls = ['%include "python/swig/range.i"']
478
479 def getValue(self):
480 from m5.objects.params import AddrRange
481
482 value = AddrRange()
483 value.start = long(self.first)
484 value.end = long(self.second)
485 return value
486
487class TickRange(Range):
488 type = Tick
489 swig_predecls = ['%include "python/swig/range.i"']
490
491 def getValue(self):
492 from m5.objects.params import TickRange
493
494 value = TickRange()
495 value.start = long(self.first)
496 value.end = long(self.second)
497 return value
498
499# Boolean parameter type. Python doesn't let you subclass bool, since
500# it doesn't want to let you create multiple instances of True and
501# False. Thus this is a little more complicated than String.
502class Bool(ParamValue):
503 cxx_type = 'bool'
504 def __init__(self, value):
505 try:
506 self.value = convert.toBool(value)
507 except TypeError:
508 self.value = bool(value)
509
510 def getValue(self):
511 return bool(self.value)
512
513 def __str__(self):
514 return str(self.value)
515
516 def ini_str(self):
517 if self.value:
518 return 'true'
519 return 'false'
520
521def IncEthernetAddr(addr, val = 1):
522 bytes = map(lambda x: int(x, 16), addr.split(':'))
523 bytes[5] += val
524 for i in (5, 4, 3, 2, 1):
525 val,rem = divmod(bytes[i], 256)
526 bytes[i] = rem
527 if val == 0:
528 break
529 bytes[i - 1] += val
530 assert(bytes[0] <= 255)
531 return ':'.join(map(lambda x: '%02x' % x, bytes))
532
533_NextEthernetAddr = "00:90:00:00:00:01"
534def NextEthernetAddr():
535 global _NextEthernetAddr
536
537 value = _NextEthernetAddr
538 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
539 return value
540
541class EthernetAddr(ParamValue):
542 cxx_type = 'Net::EthAddr'
543 cxx_predecls = ['#include "base/inet.hh"']
544 swig_predecls = ['%include "python/swig/inet.i"']
545 def __init__(self, value):
546 if value == NextEthernetAddr:
547 self.value = value
548 return
549
550 if not isinstance(value, str):
551 raise TypeError, "expected an ethernet address and didn't get one"
552
553 bytes = value.split(':')
554 if len(bytes) != 6:
555 raise TypeError, 'invalid ethernet address %s' % value
556
557 for byte in bytes:
558 if not 0 <= int(byte) <= 256:
559 raise TypeError, 'invalid ethernet address %s' % value
560
561 self.value = value
562
563 def unproxy(self, base):
564 if self.value == NextEthernetAddr:
565 return EthernetAddr(self.value())
566 return self
567
568 def getValue(self):
569 from m5.objects.params import EthAddr
570 return EthAddr(self.value)
571
572 def ini_str(self):
573 return self.value
574
575time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
576 "%a %b %d %H:%M:%S %Z %Y",
577 "%Y/%m/%d %H:%M:%S",
578 "%Y/%m/%d %H:%M",
579 "%Y/%m/%d",
580 "%m/%d/%Y %H:%M:%S",
581 "%m/%d/%Y %H:%M",
582 "%m/%d/%Y",
583 "%m/%d/%y %H:%M:%S",
584 "%m/%d/%y %H:%M",
585 "%m/%d/%y"]
586
587
588def parse_time(value):
589 from time import gmtime, strptime, struct_time, time
590 from datetime import datetime, date
591
592 if isinstance(value, struct_time):
593 return value
594
595 if isinstance(value, (int, long)):
596 return gmtime(value)
597
598 if isinstance(value, (datetime, date)):
599 return value.timetuple()
600
601 if isinstance(value, str):
602 if value in ('Now', 'Today'):
603 return time.gmtime(time.time())
604
605 for format in time_formats:
606 try:
607 return strptime(value, format)
608 except ValueError:
609 pass
610
611 raise ValueError, "Could not parse '%s' as a time" % value
612
613class Time(ParamValue):
614 cxx_type = 'tm'
615 cxx_predecls = [ '#include <time.h>' ]
616 swig_predecls = [ '%include "python/swig/time.i"' ]
617 def __init__(self, value):
618 self.value = parse_time(value)
619
620 def getValue(self):
621 from m5.objects.params import tm
622
623 c_time = tm()
624 py_time = self.value
625
626 # UNIX is years since 1900
627 c_time.tm_year = py_time.tm_year - 1900;
628
629 # Python starts at 1, UNIX starts at 0
630 c_time.tm_mon = py_time.tm_mon - 1;
631 c_time.tm_mday = py_time.tm_mday;
632 c_time.tm_hour = py_time.tm_hour;
633 c_time.tm_min = py_time.tm_min;
634 c_time.tm_sec = py_time.tm_sec;
635
636 # Python has 0 as Monday, UNIX is 0 as sunday
637 c_time.tm_wday = py_time.tm_wday + 1
638 if c_time.tm_wday > 6:
639 c_time.tm_wday -= 7;
640
641 # Python starts at 1, Unix starts at 0
642 c_time.tm_yday = py_time.tm_yday - 1;
643
644 return c_time
645
646 def __str__(self):
647 return time.asctime(self.value)
648
649 def ini_str(self):
650 return str(self)
651
652# Enumerated types are a little more complex. The user specifies the
653# type as Enum(foo) where foo is either a list or dictionary of
654# alternatives (typically strings, but not necessarily so). (In the
655# long run, the integer value of the parameter will be the list index
656# or the corresponding dictionary value. For now, since we only check
657# that the alternative is valid and then spit it into a .ini file,
658# there's not much point in using the dictionary.)
659
660# What Enum() must do is generate a new type encapsulating the
661# provided list/dictionary so that specific values of the parameter
662# can be instances of that type. We define two hidden internal
663# classes (_ListEnum and _DictEnum) to serve as base classes, then
664# derive the new type from the appropriate base class on the fly.
665
666allEnums = {}
667# Metaclass for Enum types
668class MetaEnum(type):
669 def __new__(mcls, name, bases, dict):
670 assert name not in allEnums
671
672 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
673 allEnums[name] = cls
674 return cls
675
676 def __init__(cls, name, bases, init_dict):
677 if init_dict.has_key('map'):
678 if not isinstance(cls.map, dict):
679 raise TypeError, "Enum-derived class attribute 'map' " \
680 "must be of type dict"
681 # build list of value strings from map
682 cls.vals = cls.map.keys()
683 cls.vals.sort()
684 elif init_dict.has_key('vals'):
685 if not isinstance(cls.vals, list):
686 raise TypeError, "Enum-derived class attribute 'vals' " \
687 "must be of type list"
688 # build string->value map from vals sequence
689 cls.map = {}
690 for idx,val in enumerate(cls.vals):
691 cls.map[val] = idx
692 else:
693 raise TypeError, "Enum-derived class must define "\
694 "attribute 'map' or 'vals'"
695
696 cls.cxx_type = 'Enums::%s' % name
697
698 super(MetaEnum, cls).__init__(name, bases, init_dict)
699
700 def __str__(cls):
701 return cls.__name__
702
703 # Generate C++ class declaration for this enum type.
704 # Note that we wrap the enum in a class/struct to act as a namespace,
705 # so that the enum strings can be brief w/o worrying about collisions.
706 def cxx_decl(cls):
707 code = "#ifndef __ENUM__%s\n" % cls
708 code += '#define __ENUM__%s\n' % cls
709 code += '\n'
710 code += 'namespace Enums {\n'
711 code += ' enum %s {\n' % cls
712 for val in cls.vals:
713 code += ' %s = %d,\n' % (val, cls.map[val])
714 code += ' Num_%s = %d,\n' % (cls, len(cls.vals))
715 code += ' };\n'
716 code += ' extern const char *%sStrings[Num_%s];\n' % (cls, cls)
717 code += '}\n'
718 code += '\n'
719 code += '#endif\n'
720 return code
721
722 def cxx_def(cls):
723 code = '#include "enums/%s.hh"\n' % cls
724 code += 'namespace Enums {\n'
725 code += ' const char *%sStrings[Num_%s] =\n' % (cls, cls)
726 code += ' {\n'
727 for val in cls.vals:
728 code += ' "%s",\n' % val
729 code += ' };\n'
730 code += '}\n'
731 return code
732
733# Base class for enum types.
734class Enum(ParamValue):
735 __metaclass__ = MetaEnum
736 vals = []
737
738 def __init__(self, value):
739 if value not in self.map:
740 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
741 % (value, self.vals)
742 self.value = value
743
744 def getValue(self):
745 return int(self.map[self.value])
746
747 def __str__(self):
748 return self.value
749
750# how big does a rounding error need to be before we warn about it?
751frequency_tolerance = 0.001 # 0.1%
752
753class TickParamValue(NumericParamValue):
754 cxx_type = 'Tick'
755 cxx_predecls = ['#include "sim/host.hh"']
756 swig_predecls = ['%import "stdint.i"\n' +
757 '%import "sim/host.hh"']
758
759 def getValue(self):
760 return long(self.value)
761
762class Latency(TickParamValue):
763 def __init__(self, value):
764 if isinstance(value, (Latency, Clock)):
765 self.ticks = value.ticks
766 self.value = value.value
767 elif isinstance(value, Frequency):
768 self.ticks = value.ticks
769 self.value = 1.0 / value.value
770 elif value.endswith('t'):
771 self.ticks = True
772 self.value = int(value[:-1])
773 else:
774 self.ticks = False
775 self.value = convert.toLatency(value)
776
777 def __getattr__(self, attr):
778 if attr in ('latency', 'period'):
779 return self
780 if attr == 'frequency':
781 return Frequency(self)
782 raise AttributeError, "Latency object has no attribute '%s'" % attr
783
784 def getValue(self):
785 if self.ticks or self.value == 0:
786 value = self.value
787 else:
788 value = ticks.fromSeconds(self.value)
789 return long(value)
790
791 # convert latency to ticks
792 def ini_str(self):
793 return '%d' % self.getValue()
794
795class Frequency(TickParamValue):
796 def __init__(self, value):
797 if isinstance(value, (Latency, Clock)):
798 if value.value == 0:
799 self.value = 0
800 else:
801 self.value = 1.0 / value.value
802 self.ticks = value.ticks
803 elif isinstance(value, Frequency):
804 self.value = value.value
805 self.ticks = value.ticks
806 else:
807 self.ticks = False
808 self.value = convert.toFrequency(value)
809
810 def __getattr__(self, attr):
811 if attr == 'frequency':
812 return self
813 if attr in ('latency', 'period'):
814 return Latency(self)
815 raise AttributeError, "Frequency object has no attribute '%s'" % attr
816
817 # convert latency to ticks
818 def getValue(self):
819 if self.ticks or self.value == 0:
820 value = self.value
821 else:
822 value = ticks.fromSeconds(1.0 / self.value)
823 return long(value)
824
825 def ini_str(self):
826 return '%d' % self.getValue()
827
828# A generic frequency and/or Latency value. Value is stored as a latency,
829# but to avoid ambiguity this object does not support numeric ops (* or /).
830# An explicit conversion to a Latency or Frequency must be made first.
831class Clock(ParamValue):
832 cxx_type = 'Tick'
833 cxx_predecls = ['#include "sim/host.hh"']
834 swig_predecls = ['%import "stdint.i"\n' +
835 '%import "sim/host.hh"']
836 def __init__(self, value):
837 if isinstance(value, (Latency, Clock)):
838 self.ticks = value.ticks
839 self.value = value.value
840 elif isinstance(value, Frequency):
841 self.ticks = value.ticks
842 self.value = 1.0 / value.value
843 elif value.endswith('t'):
844 self.ticks = True
845 self.value = int(value[:-1])
846 else:
847 self.ticks = False
848 self.value = convert.anyToLatency(value)
849
850 def __getattr__(self, attr):
851 if attr == 'frequency':
852 return Frequency(self)
853 if attr in ('latency', 'period'):
854 return Latency(self)
855 raise AttributeError, "Frequency object has no attribute '%s'" % attr
856
857 def getValue(self):
858 return self.period.getValue()
859
860 def ini_str(self):
861 return self.period.ini_str()
862
863class NetworkBandwidth(float,ParamValue):
864 cxx_type = 'float'
865 def __new__(cls, value):
866 # convert to bits per second
867 val = convert.toNetworkBandwidth(value)
868 return super(cls, NetworkBandwidth).__new__(cls, val)
869
870 def __str__(self):
871 return str(self.val)
872
873 def getValue(self):
874 # convert to seconds per byte
875 value = 8.0 / float(self)
876 # convert to ticks per byte
877 value = ticks.fromSeconds(value)
878 return float(value)
879
880 def ini_str(self):
881 return '%f' % self.getValue()
882
883class MemoryBandwidth(float,ParamValue):
884 cxx_type = 'float'
885 def __new__(self, value):
886 # we want the number of ticks per byte of data
887 val = convert.toMemoryBandwidth(value)
888 return super(cls, MemoryBandwidth).__new__(cls, val)
889
890 def __str__(self):
891 return str(self.val)
892
893 def getValue(self):
894 # convert to seconds per byte
895 value = 1.0 / float(self)
896 # convert to ticks per byte
897 value = ticks.fromSeconds(value)
898 return float(value)
899
900 def ini_str(self):
901 return '%f' % self.getValue()
902
903#
904# "Constants"... handy aliases for various values.
905#
906
907# Special class for NULL pointers. Note the special check in
908# make_param_value() above that lets these be assigned where a
909# SimObject is required.
910# only one copy of a particular node
911class NullSimObject(object):
912 __metaclass__ = Singleton
913
914 def __call__(cls):
915 return cls
916
917 def _instantiate(self, parent = None, path = ''):
918 pass
919
920 def ini_str(self):
921 return 'Null'
922
923 def unproxy(self, base):
924 return self
925
926 def set_path(self, parent, name):
927 pass
928
929 def __str__(self):
930 return 'Null'
931
932 def getValue(self):
933 return None
934
935# The only instance you'll ever need...
936NULL = NullSimObject()
937
938def isNullPointer(value):
939 return isinstance(value, NullSimObject)
940
941# Some memory range specifications use this as a default upper bound.
942MaxAddr = Addr.max
943MaxTick = Tick.max
944AllMemory = AddrRange(0, MaxAddr)
945
946
947#####################################################################
948#
949# Port objects
950#
951# Ports are used to interconnect objects in the memory system.
952#
953#####################################################################
954
955# Port reference: encapsulates a reference to a particular port on a
956# particular SimObject.
957class PortRef(object):
958 def __init__(self, simobj, name):
959 assert(isSimObject(simobj) or isSimObjectClass(simobj))
960 self.simobj = simobj
961 self.name = name
962 self.peer = None # not associated with another port yet
963 self.ccConnected = False # C++ port connection done?
964 self.index = -1 # always -1 for non-vector ports
965
966 def __str__(self):
967 return '%s.%s' % (self.simobj, self.name)
968
969 # for config.ini, print peer's name (not ours)
970 def ini_str(self):
971 return str(self.peer)
972
973 def __getattr__(self, attr):
974 if attr == 'peerObj':
975 # shorthand for proxies
976 return self.peer.simobj
977 raise AttributeError, "'%s' object has no attribute '%s'" % \
978 (self.__class__.__name__, attr)
979
980 # Full connection is symmetric (both ways). Called via
981 # SimObject.__setattr__ as a result of a port assignment, e.g.,
982 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
983 # e.g., "obj1.portA[3] = obj2.portB".
984 def connect(self, other):
985 if isinstance(other, VectorPortRef):
986 # reference to plain VectorPort is implicit append
987 other = other._get_next()
988 if self.peer and not proxy.isproxy(self.peer):
989 print "warning: overwriting port", self, \
990 "value", self.peer, "with", other
991 self.peer = other
992 if proxy.isproxy(other):
993 other.set_param_desc(PortParamDesc())
994 elif isinstance(other, PortRef):
995 if other.peer is not self:
996 other.connect(self)
997 else:
998 raise TypeError, \
999 "assigning non-port reference '%s' to port '%s'" \
1000 % (other, self)
1001
1002 def clone(self, simobj, memo):
1003 if memo.has_key(self):
1004 return memo[self]
1005 newRef = copy.copy(self)
1006 memo[self] = newRef
1007 newRef.simobj = simobj
1008 assert(isSimObject(newRef.simobj))
1009 if self.peer and not proxy.isproxy(self.peer):
1010 peerObj = self.peer.simobj(_memo=memo)
1011 newRef.peer = self.peer.clone(peerObj, memo)
1012 assert(not isinstance(newRef.peer, VectorPortRef))
1013 return newRef
1014
1015 def unproxy(self, simobj):
1016 assert(simobj is self.simobj)
1017 if proxy.isproxy(self.peer):
1018 try:
1019 realPeer = self.peer.unproxy(self.simobj)
1020 except:
1021 print "Error in unproxying port '%s' of %s" % \
1022 (self.name, self.simobj.path())
1023 raise
1024 self.connect(realPeer)
1025
1026 # Call C++ to create corresponding port connection between C++ objects
1027 def ccConnect(self):
1# Copyright (c) 2004-2006 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Steve Reinhardt
28# Nathan Binkert
29
30#####################################################################
31#
32# Parameter description classes
33#
34# The _params dictionary in each class maps parameter names to either
35# a Param or a VectorParam object. These objects contain the
36# parameter description string, the parameter type, and the default
37# value (if any). The convert() method on these objects is used to
38# force whatever value is assigned to the parameter to the appropriate
39# type.
40#
41# Note that the default values are loaded into the class's attribute
42# space when the parameter dictionary is initialized (in
43# MetaSimObject._new_param()); after that point they aren't used.
44#
45#####################################################################
46
47import copy
48import datetime
49import inspect
50import re
51import sys
52import time
53
54import convert
55import proxy
56import ticks
57from util import *
58
59import SimObject
60
61def isSimObject(*args, **kwargs):
62 return SimObject.isSimObject(*args, **kwargs)
63
64def isSimObjectSequence(*args, **kwargs):
65 return SimObject.isSimObjectSequence(*args, **kwargs)
66
67def isSimObjectClass(*args, **kwargs):
68 return SimObject.isSimObjectClass(*args, **kwargs)
69
70# Dummy base class to identify types that are legitimate for SimObject
71# parameters.
72class ParamValue(object):
73
74 cxx_predecls = []
75 swig_predecls = []
76
77 # default for printing to .ini file is regular string conversion.
78 # will be overridden in some cases
79 def ini_str(self):
80 return str(self)
81
82 # allows us to blithely call unproxy() on things without checking
83 # if they're really proxies or not
84 def unproxy(self, base):
85 return self
86
87# Regular parameter description.
88class ParamDesc(object):
89 def __init__(self, ptype_str, ptype, *args, **kwargs):
90 self.ptype_str = ptype_str
91 # remember ptype only if it is provided
92 if ptype != None:
93 self.ptype = ptype
94
95 if args:
96 if len(args) == 1:
97 self.desc = args[0]
98 elif len(args) == 2:
99 self.default = args[0]
100 self.desc = args[1]
101 else:
102 raise TypeError, 'too many arguments'
103
104 if kwargs.has_key('desc'):
105 assert(not hasattr(self, 'desc'))
106 self.desc = kwargs['desc']
107 del kwargs['desc']
108
109 if kwargs.has_key('default'):
110 assert(not hasattr(self, 'default'))
111 self.default = kwargs['default']
112 del kwargs['default']
113
114 if kwargs:
115 raise TypeError, 'extra unknown kwargs %s' % kwargs
116
117 if not hasattr(self, 'desc'):
118 raise TypeError, 'desc attribute missing'
119
120 def __getattr__(self, attr):
121 if attr == 'ptype':
122 try:
123 ptype = SimObject.allClasses[self.ptype_str]
124 if not isinstance(ptype, type):
125 raise NameError
126 self.ptype = ptype
127 return ptype
128 except NameError:
129 raise
130 #raise TypeError, \
131 # "Param qualifier '%s' is not a type" % self.ptype_str
132 raise AttributeError, "'%s' object has no attribute '%s'" % \
133 (type(self).__name__, attr)
134
135 def convert(self, value):
136 if isinstance(value, proxy.BaseProxy):
137 value.set_param_desc(self)
138 return value
139 if not hasattr(self, 'ptype') and isNullPointer(value):
140 # deferred evaluation of SimObject; continue to defer if
141 # we're just assigning a null pointer
142 return value
143 if isinstance(value, self.ptype):
144 return value
145 if isNullPointer(value) and isSimObjectClass(self.ptype):
146 return value
147 return self.ptype(value)
148
149 def cxx_predecls(self):
150 return self.ptype.cxx_predecls
151
152 def swig_predecls(self):
153 return self.ptype.swig_predecls
154
155 def cxx_decl(self):
156 return '%s %s;' % (self.ptype.cxx_type, self.name)
157
158# Vector-valued parameter description. Just like ParamDesc, except
159# that the value is a vector (list) of the specified type instead of a
160# single value.
161
162class VectorParamValue(list):
163 def ini_str(self):
164 return ' '.join([v.ini_str() for v in self])
165
166 def getValue(self):
167 return [ v.getValue() for v in self ]
168
169 def unproxy(self, base):
170 return [v.unproxy(base) for v in self]
171
172class SimObjVector(VectorParamValue):
173 def print_ini(self):
174 for v in self:
175 v.print_ini()
176
177class VectorParamDesc(ParamDesc):
178 # Convert assigned value to appropriate type. If the RHS is not a
179 # list or tuple, it generates a single-element list.
180 def convert(self, value):
181 if isinstance(value, (list, tuple)):
182 # list: coerce each element into new list
183 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
184 else:
185 # singleton: coerce to a single-element list
186 tmp_list = [ ParamDesc.convert(self, value) ]
187
188 if isSimObjectSequence(tmp_list):
189 return SimObjVector(tmp_list)
190 else:
191 return VectorParamValue(tmp_list)
192
193 def swig_predecls(self):
194 return ['%%include "%s_vptype.i"' % self.ptype_str]
195
196 def swig_decl(self):
197 cxx_type = re.sub('std::', '', self.ptype.cxx_type)
198 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
199 (self.ptype_str, cxx_type)
200 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
201
202 def cxx_predecls(self):
203 return ['#include <vector>'] + self.ptype.cxx_predecls
204
205 def cxx_decl(self):
206 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
207
208class ParamFactory(object):
209 def __init__(self, param_desc_class, ptype_str = None):
210 self.param_desc_class = param_desc_class
211 self.ptype_str = ptype_str
212
213 def __getattr__(self, attr):
214 if self.ptype_str:
215 attr = self.ptype_str + '.' + attr
216 return ParamFactory(self.param_desc_class, attr)
217
218 # E.g., Param.Int(5, "number of widgets")
219 def __call__(self, *args, **kwargs):
220 caller_frame = inspect.currentframe().f_back
221 ptype = None
222 try:
223 ptype = eval(self.ptype_str,
224 caller_frame.f_globals, caller_frame.f_locals)
225 if not isinstance(ptype, type):
226 raise TypeError, \
227 "Param qualifier is not a type: %s" % ptype
228 except NameError:
229 # if name isn't defined yet, assume it's a SimObject, and
230 # try to resolve it later
231 pass
232 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
233
234Param = ParamFactory(ParamDesc)
235VectorParam = ParamFactory(VectorParamDesc)
236
237#####################################################################
238#
239# Parameter Types
240#
241# Though native Python types could be used to specify parameter types
242# (the 'ptype' field of the Param and VectorParam classes), it's more
243# flexible to define our own set of types. This gives us more control
244# over how Python expressions are converted to values (via the
245# __init__() constructor) and how these values are printed out (via
246# the __str__() conversion method).
247#
248#####################################################################
249
250# String-valued parameter. Just mixin the ParamValue class with the
251# built-in str class.
252class String(ParamValue,str):
253 cxx_type = 'std::string'
254 cxx_predecls = ['#include <string>']
255 swig_predecls = ['%include "std_string.i"\n' +
256 '%apply const std::string& {std::string *};']
257 swig_predecls = ['%include "std_string.i"' ]
258
259 def getValue(self):
260 return self
261
262# superclass for "numeric" parameter values, to emulate math
263# operations in a type-safe way. e.g., a Latency times an int returns
264# a new Latency object.
265class NumericParamValue(ParamValue):
266 def __str__(self):
267 return str(self.value)
268
269 def __float__(self):
270 return float(self.value)
271
272 def __long__(self):
273 return long(self.value)
274
275 def __int__(self):
276 return int(self.value)
277
278 # hook for bounds checking
279 def _check(self):
280 return
281
282 def __mul__(self, other):
283 newobj = self.__class__(self)
284 newobj.value *= other
285 newobj._check()
286 return newobj
287
288 __rmul__ = __mul__
289
290 def __div__(self, other):
291 newobj = self.__class__(self)
292 newobj.value /= other
293 newobj._check()
294 return newobj
295
296 def __sub__(self, other):
297 newobj = self.__class__(self)
298 newobj.value -= other
299 newobj._check()
300 return newobj
301
302# Metaclass for bounds-checked integer parameters. See CheckedInt.
303class CheckedIntType(type):
304 def __init__(cls, name, bases, dict):
305 super(CheckedIntType, cls).__init__(name, bases, dict)
306
307 # CheckedInt is an abstract base class, so we actually don't
308 # want to do any processing on it... the rest of this code is
309 # just for classes that derive from CheckedInt.
310 if name == 'CheckedInt':
311 return
312
313 if not cls.cxx_predecls:
314 # most derived types require this, so we just do it here once
315 cls.cxx_predecls = ['#include "sim/host.hh"']
316
317 if not cls.swig_predecls:
318 # most derived types require this, so we just do it here once
319 cls.swig_predecls = ['%import "stdint.i"\n' +
320 '%import "sim/host.hh"']
321
322 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
323 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
324 panic("CheckedInt subclass %s must define either\n" \
325 " 'min' and 'max' or 'size' and 'unsigned'\n" \
326 % name);
327 if cls.unsigned:
328 cls.min = 0
329 cls.max = 2 ** cls.size - 1
330 else:
331 cls.min = -(2 ** (cls.size - 1))
332 cls.max = (2 ** (cls.size - 1)) - 1
333
334# Abstract superclass for bounds-checked integer parameters. This
335# class is subclassed to generate parameter classes with specific
336# bounds. Initialization of the min and max bounds is done in the
337# metaclass CheckedIntType.__init__.
338class CheckedInt(NumericParamValue):
339 __metaclass__ = CheckedIntType
340
341 def _check(self):
342 if not self.min <= self.value <= self.max:
343 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
344 (self.min, self.value, self.max)
345
346 def __init__(self, value):
347 if isinstance(value, str):
348 self.value = convert.toInteger(value)
349 elif isinstance(value, (int, long, float, NumericParamValue)):
350 self.value = long(value)
351 else:
352 raise TypeError, "Can't convert object of type %s to CheckedInt" \
353 % type(value).__name__
354 self._check()
355
356 def getValue(self):
357 return long(self.value)
358
359class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
360class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
361
362class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
363class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
364class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
365class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
366class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
367class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
368class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
369class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
370
371class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
372class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
373class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
374class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
375
376class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
377
378class Float(ParamValue, float):
379 cxx_type = 'double'
380
381 def getValue(self):
382 return float(self.value)
383
384class MemorySize(CheckedInt):
385 cxx_type = 'uint64_t'
386 size = 64
387 unsigned = True
388 def __init__(self, value):
389 if isinstance(value, MemorySize):
390 self.value = value.value
391 else:
392 self.value = convert.toMemorySize(value)
393 self._check()
394
395class MemorySize32(CheckedInt):
396 cxx_type = 'uint32_t'
397 size = 32
398 unsigned = True
399 def __init__(self, value):
400 if isinstance(value, MemorySize):
401 self.value = value.value
402 else:
403 self.value = convert.toMemorySize(value)
404 self._check()
405
406class Addr(CheckedInt):
407 cxx_type = 'Addr'
408 cxx_predecls = ['#include "arch/isa_traits.hh"']
409 size = 64
410 unsigned = True
411 def __init__(self, value):
412 if isinstance(value, Addr):
413 self.value = value.value
414 else:
415 try:
416 self.value = convert.toMemorySize(value)
417 except TypeError:
418 self.value = long(value)
419 self._check()
420 def __add__(self, other):
421 if isinstance(other, Addr):
422 return self.value + other.value
423 else:
424 return self.value + other
425
426
427class MetaRange(type):
428 def __init__(cls, name, bases, dict):
429 super(MetaRange, cls).__init__(name, bases, dict)
430 if name == 'Range':
431 return
432 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
433 cls.cxx_predecls = \
434 ['#include "base/range.hh"'] + cls.type.cxx_predecls
435
436class Range(ParamValue):
437 __metaclass__ = MetaRange
438 type = Int # default; can be overridden in subclasses
439 def __init__(self, *args, **kwargs):
440 def handle_kwargs(self, kwargs):
441 if 'end' in kwargs:
442 self.second = self.type(kwargs.pop('end'))
443 elif 'size' in kwargs:
444 self.second = self.first + self.type(kwargs.pop('size')) - 1
445 else:
446 raise TypeError, "Either end or size must be specified"
447
448 if len(args) == 0:
449 self.first = self.type(kwargs.pop('start'))
450 handle_kwargs(self, kwargs)
451
452 elif len(args) == 1:
453 if kwargs:
454 self.first = self.type(args[0])
455 handle_kwargs(self, kwargs)
456 elif isinstance(args[0], Range):
457 self.first = self.type(args[0].first)
458 self.second = self.type(args[0].second)
459 else:
460 self.first = self.type(0)
461 self.second = self.type(args[0]) - 1
462
463 elif len(args) == 2:
464 self.first = self.type(args[0])
465 self.second = self.type(args[1])
466 else:
467 raise TypeError, "Too many arguments specified"
468
469 if kwargs:
470 raise TypeError, "too many keywords: %s" % kwargs.keys()
471
472 def __str__(self):
473 return '%s:%s' % (self.first, self.second)
474
475class AddrRange(Range):
476 type = Addr
477 swig_predecls = ['%include "python/swig/range.i"']
478
479 def getValue(self):
480 from m5.objects.params import AddrRange
481
482 value = AddrRange()
483 value.start = long(self.first)
484 value.end = long(self.second)
485 return value
486
487class TickRange(Range):
488 type = Tick
489 swig_predecls = ['%include "python/swig/range.i"']
490
491 def getValue(self):
492 from m5.objects.params import TickRange
493
494 value = TickRange()
495 value.start = long(self.first)
496 value.end = long(self.second)
497 return value
498
499# Boolean parameter type. Python doesn't let you subclass bool, since
500# it doesn't want to let you create multiple instances of True and
501# False. Thus this is a little more complicated than String.
502class Bool(ParamValue):
503 cxx_type = 'bool'
504 def __init__(self, value):
505 try:
506 self.value = convert.toBool(value)
507 except TypeError:
508 self.value = bool(value)
509
510 def getValue(self):
511 return bool(self.value)
512
513 def __str__(self):
514 return str(self.value)
515
516 def ini_str(self):
517 if self.value:
518 return 'true'
519 return 'false'
520
521def IncEthernetAddr(addr, val = 1):
522 bytes = map(lambda x: int(x, 16), addr.split(':'))
523 bytes[5] += val
524 for i in (5, 4, 3, 2, 1):
525 val,rem = divmod(bytes[i], 256)
526 bytes[i] = rem
527 if val == 0:
528 break
529 bytes[i - 1] += val
530 assert(bytes[0] <= 255)
531 return ':'.join(map(lambda x: '%02x' % x, bytes))
532
533_NextEthernetAddr = "00:90:00:00:00:01"
534def NextEthernetAddr():
535 global _NextEthernetAddr
536
537 value = _NextEthernetAddr
538 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
539 return value
540
541class EthernetAddr(ParamValue):
542 cxx_type = 'Net::EthAddr'
543 cxx_predecls = ['#include "base/inet.hh"']
544 swig_predecls = ['%include "python/swig/inet.i"']
545 def __init__(self, value):
546 if value == NextEthernetAddr:
547 self.value = value
548 return
549
550 if not isinstance(value, str):
551 raise TypeError, "expected an ethernet address and didn't get one"
552
553 bytes = value.split(':')
554 if len(bytes) != 6:
555 raise TypeError, 'invalid ethernet address %s' % value
556
557 for byte in bytes:
558 if not 0 <= int(byte) <= 256:
559 raise TypeError, 'invalid ethernet address %s' % value
560
561 self.value = value
562
563 def unproxy(self, base):
564 if self.value == NextEthernetAddr:
565 return EthernetAddr(self.value())
566 return self
567
568 def getValue(self):
569 from m5.objects.params import EthAddr
570 return EthAddr(self.value)
571
572 def ini_str(self):
573 return self.value
574
575time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
576 "%a %b %d %H:%M:%S %Z %Y",
577 "%Y/%m/%d %H:%M:%S",
578 "%Y/%m/%d %H:%M",
579 "%Y/%m/%d",
580 "%m/%d/%Y %H:%M:%S",
581 "%m/%d/%Y %H:%M",
582 "%m/%d/%Y",
583 "%m/%d/%y %H:%M:%S",
584 "%m/%d/%y %H:%M",
585 "%m/%d/%y"]
586
587
588def parse_time(value):
589 from time import gmtime, strptime, struct_time, time
590 from datetime import datetime, date
591
592 if isinstance(value, struct_time):
593 return value
594
595 if isinstance(value, (int, long)):
596 return gmtime(value)
597
598 if isinstance(value, (datetime, date)):
599 return value.timetuple()
600
601 if isinstance(value, str):
602 if value in ('Now', 'Today'):
603 return time.gmtime(time.time())
604
605 for format in time_formats:
606 try:
607 return strptime(value, format)
608 except ValueError:
609 pass
610
611 raise ValueError, "Could not parse '%s' as a time" % value
612
613class Time(ParamValue):
614 cxx_type = 'tm'
615 cxx_predecls = [ '#include <time.h>' ]
616 swig_predecls = [ '%include "python/swig/time.i"' ]
617 def __init__(self, value):
618 self.value = parse_time(value)
619
620 def getValue(self):
621 from m5.objects.params import tm
622
623 c_time = tm()
624 py_time = self.value
625
626 # UNIX is years since 1900
627 c_time.tm_year = py_time.tm_year - 1900;
628
629 # Python starts at 1, UNIX starts at 0
630 c_time.tm_mon = py_time.tm_mon - 1;
631 c_time.tm_mday = py_time.tm_mday;
632 c_time.tm_hour = py_time.tm_hour;
633 c_time.tm_min = py_time.tm_min;
634 c_time.tm_sec = py_time.tm_sec;
635
636 # Python has 0 as Monday, UNIX is 0 as sunday
637 c_time.tm_wday = py_time.tm_wday + 1
638 if c_time.tm_wday > 6:
639 c_time.tm_wday -= 7;
640
641 # Python starts at 1, Unix starts at 0
642 c_time.tm_yday = py_time.tm_yday - 1;
643
644 return c_time
645
646 def __str__(self):
647 return time.asctime(self.value)
648
649 def ini_str(self):
650 return str(self)
651
652# Enumerated types are a little more complex. The user specifies the
653# type as Enum(foo) where foo is either a list or dictionary of
654# alternatives (typically strings, but not necessarily so). (In the
655# long run, the integer value of the parameter will be the list index
656# or the corresponding dictionary value. For now, since we only check
657# that the alternative is valid and then spit it into a .ini file,
658# there's not much point in using the dictionary.)
659
660# What Enum() must do is generate a new type encapsulating the
661# provided list/dictionary so that specific values of the parameter
662# can be instances of that type. We define two hidden internal
663# classes (_ListEnum and _DictEnum) to serve as base classes, then
664# derive the new type from the appropriate base class on the fly.
665
666allEnums = {}
667# Metaclass for Enum types
668class MetaEnum(type):
669 def __new__(mcls, name, bases, dict):
670 assert name not in allEnums
671
672 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
673 allEnums[name] = cls
674 return cls
675
676 def __init__(cls, name, bases, init_dict):
677 if init_dict.has_key('map'):
678 if not isinstance(cls.map, dict):
679 raise TypeError, "Enum-derived class attribute 'map' " \
680 "must be of type dict"
681 # build list of value strings from map
682 cls.vals = cls.map.keys()
683 cls.vals.sort()
684 elif init_dict.has_key('vals'):
685 if not isinstance(cls.vals, list):
686 raise TypeError, "Enum-derived class attribute 'vals' " \
687 "must be of type list"
688 # build string->value map from vals sequence
689 cls.map = {}
690 for idx,val in enumerate(cls.vals):
691 cls.map[val] = idx
692 else:
693 raise TypeError, "Enum-derived class must define "\
694 "attribute 'map' or 'vals'"
695
696 cls.cxx_type = 'Enums::%s' % name
697
698 super(MetaEnum, cls).__init__(name, bases, init_dict)
699
700 def __str__(cls):
701 return cls.__name__
702
703 # Generate C++ class declaration for this enum type.
704 # Note that we wrap the enum in a class/struct to act as a namespace,
705 # so that the enum strings can be brief w/o worrying about collisions.
706 def cxx_decl(cls):
707 code = "#ifndef __ENUM__%s\n" % cls
708 code += '#define __ENUM__%s\n' % cls
709 code += '\n'
710 code += 'namespace Enums {\n'
711 code += ' enum %s {\n' % cls
712 for val in cls.vals:
713 code += ' %s = %d,\n' % (val, cls.map[val])
714 code += ' Num_%s = %d,\n' % (cls, len(cls.vals))
715 code += ' };\n'
716 code += ' extern const char *%sStrings[Num_%s];\n' % (cls, cls)
717 code += '}\n'
718 code += '\n'
719 code += '#endif\n'
720 return code
721
722 def cxx_def(cls):
723 code = '#include "enums/%s.hh"\n' % cls
724 code += 'namespace Enums {\n'
725 code += ' const char *%sStrings[Num_%s] =\n' % (cls, cls)
726 code += ' {\n'
727 for val in cls.vals:
728 code += ' "%s",\n' % val
729 code += ' };\n'
730 code += '}\n'
731 return code
732
733# Base class for enum types.
734class Enum(ParamValue):
735 __metaclass__ = MetaEnum
736 vals = []
737
738 def __init__(self, value):
739 if value not in self.map:
740 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
741 % (value, self.vals)
742 self.value = value
743
744 def getValue(self):
745 return int(self.map[self.value])
746
747 def __str__(self):
748 return self.value
749
750# how big does a rounding error need to be before we warn about it?
751frequency_tolerance = 0.001 # 0.1%
752
753class TickParamValue(NumericParamValue):
754 cxx_type = 'Tick'
755 cxx_predecls = ['#include "sim/host.hh"']
756 swig_predecls = ['%import "stdint.i"\n' +
757 '%import "sim/host.hh"']
758
759 def getValue(self):
760 return long(self.value)
761
762class Latency(TickParamValue):
763 def __init__(self, value):
764 if isinstance(value, (Latency, Clock)):
765 self.ticks = value.ticks
766 self.value = value.value
767 elif isinstance(value, Frequency):
768 self.ticks = value.ticks
769 self.value = 1.0 / value.value
770 elif value.endswith('t'):
771 self.ticks = True
772 self.value = int(value[:-1])
773 else:
774 self.ticks = False
775 self.value = convert.toLatency(value)
776
777 def __getattr__(self, attr):
778 if attr in ('latency', 'period'):
779 return self
780 if attr == 'frequency':
781 return Frequency(self)
782 raise AttributeError, "Latency object has no attribute '%s'" % attr
783
784 def getValue(self):
785 if self.ticks or self.value == 0:
786 value = self.value
787 else:
788 value = ticks.fromSeconds(self.value)
789 return long(value)
790
791 # convert latency to ticks
792 def ini_str(self):
793 return '%d' % self.getValue()
794
795class Frequency(TickParamValue):
796 def __init__(self, value):
797 if isinstance(value, (Latency, Clock)):
798 if value.value == 0:
799 self.value = 0
800 else:
801 self.value = 1.0 / value.value
802 self.ticks = value.ticks
803 elif isinstance(value, Frequency):
804 self.value = value.value
805 self.ticks = value.ticks
806 else:
807 self.ticks = False
808 self.value = convert.toFrequency(value)
809
810 def __getattr__(self, attr):
811 if attr == 'frequency':
812 return self
813 if attr in ('latency', 'period'):
814 return Latency(self)
815 raise AttributeError, "Frequency object has no attribute '%s'" % attr
816
817 # convert latency to ticks
818 def getValue(self):
819 if self.ticks or self.value == 0:
820 value = self.value
821 else:
822 value = ticks.fromSeconds(1.0 / self.value)
823 return long(value)
824
825 def ini_str(self):
826 return '%d' % self.getValue()
827
828# A generic frequency and/or Latency value. Value is stored as a latency,
829# but to avoid ambiguity this object does not support numeric ops (* or /).
830# An explicit conversion to a Latency or Frequency must be made first.
831class Clock(ParamValue):
832 cxx_type = 'Tick'
833 cxx_predecls = ['#include "sim/host.hh"']
834 swig_predecls = ['%import "stdint.i"\n' +
835 '%import "sim/host.hh"']
836 def __init__(self, value):
837 if isinstance(value, (Latency, Clock)):
838 self.ticks = value.ticks
839 self.value = value.value
840 elif isinstance(value, Frequency):
841 self.ticks = value.ticks
842 self.value = 1.0 / value.value
843 elif value.endswith('t'):
844 self.ticks = True
845 self.value = int(value[:-1])
846 else:
847 self.ticks = False
848 self.value = convert.anyToLatency(value)
849
850 def __getattr__(self, attr):
851 if attr == 'frequency':
852 return Frequency(self)
853 if attr in ('latency', 'period'):
854 return Latency(self)
855 raise AttributeError, "Frequency object has no attribute '%s'" % attr
856
857 def getValue(self):
858 return self.period.getValue()
859
860 def ini_str(self):
861 return self.period.ini_str()
862
863class NetworkBandwidth(float,ParamValue):
864 cxx_type = 'float'
865 def __new__(cls, value):
866 # convert to bits per second
867 val = convert.toNetworkBandwidth(value)
868 return super(cls, NetworkBandwidth).__new__(cls, val)
869
870 def __str__(self):
871 return str(self.val)
872
873 def getValue(self):
874 # convert to seconds per byte
875 value = 8.0 / float(self)
876 # convert to ticks per byte
877 value = ticks.fromSeconds(value)
878 return float(value)
879
880 def ini_str(self):
881 return '%f' % self.getValue()
882
883class MemoryBandwidth(float,ParamValue):
884 cxx_type = 'float'
885 def __new__(self, value):
886 # we want the number of ticks per byte of data
887 val = convert.toMemoryBandwidth(value)
888 return super(cls, MemoryBandwidth).__new__(cls, val)
889
890 def __str__(self):
891 return str(self.val)
892
893 def getValue(self):
894 # convert to seconds per byte
895 value = 1.0 / float(self)
896 # convert to ticks per byte
897 value = ticks.fromSeconds(value)
898 return float(value)
899
900 def ini_str(self):
901 return '%f' % self.getValue()
902
903#
904# "Constants"... handy aliases for various values.
905#
906
907# Special class for NULL pointers. Note the special check in
908# make_param_value() above that lets these be assigned where a
909# SimObject is required.
910# only one copy of a particular node
911class NullSimObject(object):
912 __metaclass__ = Singleton
913
914 def __call__(cls):
915 return cls
916
917 def _instantiate(self, parent = None, path = ''):
918 pass
919
920 def ini_str(self):
921 return 'Null'
922
923 def unproxy(self, base):
924 return self
925
926 def set_path(self, parent, name):
927 pass
928
929 def __str__(self):
930 return 'Null'
931
932 def getValue(self):
933 return None
934
935# The only instance you'll ever need...
936NULL = NullSimObject()
937
938def isNullPointer(value):
939 return isinstance(value, NullSimObject)
940
941# Some memory range specifications use this as a default upper bound.
942MaxAddr = Addr.max
943MaxTick = Tick.max
944AllMemory = AddrRange(0, MaxAddr)
945
946
947#####################################################################
948#
949# Port objects
950#
951# Ports are used to interconnect objects in the memory system.
952#
953#####################################################################
954
955# Port reference: encapsulates a reference to a particular port on a
956# particular SimObject.
957class PortRef(object):
958 def __init__(self, simobj, name):
959 assert(isSimObject(simobj) or isSimObjectClass(simobj))
960 self.simobj = simobj
961 self.name = name
962 self.peer = None # not associated with another port yet
963 self.ccConnected = False # C++ port connection done?
964 self.index = -1 # always -1 for non-vector ports
965
966 def __str__(self):
967 return '%s.%s' % (self.simobj, self.name)
968
969 # for config.ini, print peer's name (not ours)
970 def ini_str(self):
971 return str(self.peer)
972
973 def __getattr__(self, attr):
974 if attr == 'peerObj':
975 # shorthand for proxies
976 return self.peer.simobj
977 raise AttributeError, "'%s' object has no attribute '%s'" % \
978 (self.__class__.__name__, attr)
979
980 # Full connection is symmetric (both ways). Called via
981 # SimObject.__setattr__ as a result of a port assignment, e.g.,
982 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
983 # e.g., "obj1.portA[3] = obj2.portB".
984 def connect(self, other):
985 if isinstance(other, VectorPortRef):
986 # reference to plain VectorPort is implicit append
987 other = other._get_next()
988 if self.peer and not proxy.isproxy(self.peer):
989 print "warning: overwriting port", self, \
990 "value", self.peer, "with", other
991 self.peer = other
992 if proxy.isproxy(other):
993 other.set_param_desc(PortParamDesc())
994 elif isinstance(other, PortRef):
995 if other.peer is not self:
996 other.connect(self)
997 else:
998 raise TypeError, \
999 "assigning non-port reference '%s' to port '%s'" \
1000 % (other, self)
1001
1002 def clone(self, simobj, memo):
1003 if memo.has_key(self):
1004 return memo[self]
1005 newRef = copy.copy(self)
1006 memo[self] = newRef
1007 newRef.simobj = simobj
1008 assert(isSimObject(newRef.simobj))
1009 if self.peer and not proxy.isproxy(self.peer):
1010 peerObj = self.peer.simobj(_memo=memo)
1011 newRef.peer = self.peer.clone(peerObj, memo)
1012 assert(not isinstance(newRef.peer, VectorPortRef))
1013 return newRef
1014
1015 def unproxy(self, simobj):
1016 assert(simobj is self.simobj)
1017 if proxy.isproxy(self.peer):
1018 try:
1019 realPeer = self.peer.unproxy(self.simobj)
1020 except:
1021 print "Error in unproxying port '%s' of %s" % \
1022 (self.name, self.simobj.path())
1023 raise
1024 self.connect(realPeer)
1025
1026 # Call C++ to create corresponding port connection between C++ objects
1027 def ccConnect(self):
1028 import internal
1028 from m5.objects.params import connectPorts
1029
1030 if self.ccConnected: # already done this
1031 return
1032 peer = self.peer
1029
1030 if self.ccConnected: # already done this
1031 return
1032 peer = self.peer
1033 internal.sim_object.connectPorts(self.simobj.getCCObject(), self.name,
1034 self.index, peer.simobj.getCCObject(), peer.name, peer.index)
1033 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1034 peer.simobj.getCCObject(), peer.name, peer.index)
1035 self.ccConnected = True
1036 peer.ccConnected = True
1037
1038# A reference to an individual element of a VectorPort... much like a
1039# PortRef, but has an index.
1040class VectorPortElementRef(PortRef):
1041 def __init__(self, simobj, name, index):
1042 PortRef.__init__(self, simobj, name)
1043 self.index = index
1044
1045 def __str__(self):
1046 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1047
1048# A reference to a complete vector-valued port (not just a single element).
1049# Can be indexed to retrieve individual VectorPortElementRef instances.
1050class VectorPortRef(object):
1051 def __init__(self, simobj, name):
1052 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1053 self.simobj = simobj
1054 self.name = name
1055 self.elements = []
1056
1057 def __str__(self):
1058 return '%s.%s[:]' % (self.simobj, self.name)
1059
1060 # for config.ini, print peer's name (not ours)
1061 def ini_str(self):
1062 return ' '.join([el.ini_str() for el in self.elements])
1063
1064 def __getitem__(self, key):
1065 if not isinstance(key, int):
1066 raise TypeError, "VectorPort index must be integer"
1067 if key >= len(self.elements):
1068 # need to extend list
1069 ext = [VectorPortElementRef(self.simobj, self.name, i)
1070 for i in range(len(self.elements), key+1)]
1071 self.elements.extend(ext)
1072 return self.elements[key]
1073
1074 def _get_next(self):
1075 return self[len(self.elements)]
1076
1077 def __setitem__(self, key, value):
1078 if not isinstance(key, int):
1079 raise TypeError, "VectorPort index must be integer"
1080 self[key].connect(value)
1081
1082 def connect(self, other):
1083 if isinstance(other, (list, tuple)):
1084 # Assign list of port refs to vector port.
1085 # For now, append them... not sure if that's the right semantics
1086 # or if it should replace the current vector.
1087 for ref in other:
1088 self._get_next().connect(ref)
1089 else:
1090 # scalar assignment to plain VectorPort is implicit append
1091 self._get_next().connect(other)
1092
1093 def clone(self, simobj, memo):
1094 if memo.has_key(self):
1095 return memo[self]
1096 newRef = copy.copy(self)
1097 memo[self] = newRef
1098 newRef.simobj = simobj
1099 assert(isSimObject(newRef.simobj))
1100 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1101 return newRef
1102
1103 def unproxy(self, simobj):
1104 [el.unproxy(simobj) for el in self.elements]
1105
1106 def ccConnect(self):
1107 [el.ccConnect() for el in self.elements]
1108
1109# Port description object. Like a ParamDesc object, this represents a
1110# logical port in the SimObject class, not a particular port on a
1111# SimObject instance. The latter are represented by PortRef objects.
1112class Port(object):
1113 # Port("description") or Port(default, "description")
1114 def __init__(self, *args):
1115 if len(args) == 1:
1116 self.desc = args[0]
1117 elif len(args) == 2:
1118 self.default = args[0]
1119 self.desc = args[1]
1120 else:
1121 raise TypeError, 'wrong number of arguments'
1122 # self.name is set by SimObject class on assignment
1123 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1124
1125 # Generate a PortRef for this port on the given SimObject with the
1126 # given name
1127 def makeRef(self, simobj):
1128 return PortRef(simobj, self.name)
1129
1130 # Connect an instance of this port (on the given SimObject with
1131 # the given name) with the port described by the supplied PortRef
1132 def connect(self, simobj, ref):
1133 self.makeRef(simobj).connect(ref)
1134
1135# VectorPort description object. Like Port, but represents a vector
1136# of connections (e.g., as on a Bus).
1137class VectorPort(Port):
1138 def __init__(self, *args):
1139 Port.__init__(self, *args)
1140 self.isVec = True
1141
1142 def makeRef(self, simobj):
1143 return VectorPortRef(simobj, self.name)
1144
1145# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1146# proxy objects (via set_param_desc()) so that proxy error messages
1147# make sense.
1148class PortParamDesc(object):
1149 __metaclass__ = Singleton
1150
1151 ptype_str = 'Port'
1152 ptype = Port
1153
1154__all__ = ['Param', 'VectorParam',
1155 'Enum', 'Bool', 'String', 'Float',
1156 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1157 'Int32', 'UInt32', 'Int64', 'UInt64',
1158 'Counter', 'Addr', 'Tick', 'Percent',
1159 'TcpPort', 'UdpPort', 'EthernetAddr',
1160 'MemorySize', 'MemorySize32',
1161 'Latency', 'Frequency', 'Clock',
1162 'NetworkBandwidth', 'MemoryBandwidth',
1163 'Range', 'AddrRange', 'TickRange',
1164 'MaxAddr', 'MaxTick', 'AllMemory',
1165 'Time',
1166 'NextEthernetAddr', 'NULL',
1167 'Port', 'VectorPort']
1035 self.ccConnected = True
1036 peer.ccConnected = True
1037
1038# A reference to an individual element of a VectorPort... much like a
1039# PortRef, but has an index.
1040class VectorPortElementRef(PortRef):
1041 def __init__(self, simobj, name, index):
1042 PortRef.__init__(self, simobj, name)
1043 self.index = index
1044
1045 def __str__(self):
1046 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1047
1048# A reference to a complete vector-valued port (not just a single element).
1049# Can be indexed to retrieve individual VectorPortElementRef instances.
1050class VectorPortRef(object):
1051 def __init__(self, simobj, name):
1052 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1053 self.simobj = simobj
1054 self.name = name
1055 self.elements = []
1056
1057 def __str__(self):
1058 return '%s.%s[:]' % (self.simobj, self.name)
1059
1060 # for config.ini, print peer's name (not ours)
1061 def ini_str(self):
1062 return ' '.join([el.ini_str() for el in self.elements])
1063
1064 def __getitem__(self, key):
1065 if not isinstance(key, int):
1066 raise TypeError, "VectorPort index must be integer"
1067 if key >= len(self.elements):
1068 # need to extend list
1069 ext = [VectorPortElementRef(self.simobj, self.name, i)
1070 for i in range(len(self.elements), key+1)]
1071 self.elements.extend(ext)
1072 return self.elements[key]
1073
1074 def _get_next(self):
1075 return self[len(self.elements)]
1076
1077 def __setitem__(self, key, value):
1078 if not isinstance(key, int):
1079 raise TypeError, "VectorPort index must be integer"
1080 self[key].connect(value)
1081
1082 def connect(self, other):
1083 if isinstance(other, (list, tuple)):
1084 # Assign list of port refs to vector port.
1085 # For now, append them... not sure if that's the right semantics
1086 # or if it should replace the current vector.
1087 for ref in other:
1088 self._get_next().connect(ref)
1089 else:
1090 # scalar assignment to plain VectorPort is implicit append
1091 self._get_next().connect(other)
1092
1093 def clone(self, simobj, memo):
1094 if memo.has_key(self):
1095 return memo[self]
1096 newRef = copy.copy(self)
1097 memo[self] = newRef
1098 newRef.simobj = simobj
1099 assert(isSimObject(newRef.simobj))
1100 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1101 return newRef
1102
1103 def unproxy(self, simobj):
1104 [el.unproxy(simobj) for el in self.elements]
1105
1106 def ccConnect(self):
1107 [el.ccConnect() for el in self.elements]
1108
1109# Port description object. Like a ParamDesc object, this represents a
1110# logical port in the SimObject class, not a particular port on a
1111# SimObject instance. The latter are represented by PortRef objects.
1112class Port(object):
1113 # Port("description") or Port(default, "description")
1114 def __init__(self, *args):
1115 if len(args) == 1:
1116 self.desc = args[0]
1117 elif len(args) == 2:
1118 self.default = args[0]
1119 self.desc = args[1]
1120 else:
1121 raise TypeError, 'wrong number of arguments'
1122 # self.name is set by SimObject class on assignment
1123 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1124
1125 # Generate a PortRef for this port on the given SimObject with the
1126 # given name
1127 def makeRef(self, simobj):
1128 return PortRef(simobj, self.name)
1129
1130 # Connect an instance of this port (on the given SimObject with
1131 # the given name) with the port described by the supplied PortRef
1132 def connect(self, simobj, ref):
1133 self.makeRef(simobj).connect(ref)
1134
1135# VectorPort description object. Like Port, but represents a vector
1136# of connections (e.g., as on a Bus).
1137class VectorPort(Port):
1138 def __init__(self, *args):
1139 Port.__init__(self, *args)
1140 self.isVec = True
1141
1142 def makeRef(self, simobj):
1143 return VectorPortRef(simobj, self.name)
1144
1145# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1146# proxy objects (via set_param_desc()) so that proxy error messages
1147# make sense.
1148class PortParamDesc(object):
1149 __metaclass__ = Singleton
1150
1151 ptype_str = 'Port'
1152 ptype = Port
1153
1154__all__ = ['Param', 'VectorParam',
1155 'Enum', 'Bool', 'String', 'Float',
1156 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1157 'Int32', 'UInt32', 'Int64', 'UInt64',
1158 'Counter', 'Addr', 'Tick', 'Percent',
1159 'TcpPort', 'UdpPort', 'EthernetAddr',
1160 'MemorySize', 'MemorySize32',
1161 'Latency', 'Frequency', 'Clock',
1162 'NetworkBandwidth', 'MemoryBandwidth',
1163 'Range', 'AddrRange', 'TickRange',
1164 'MaxAddr', 'MaxTick', 'AllMemory',
1165 'Time',
1166 'NextEthernetAddr', 'NULL',
1167 'Port', 'VectorPort']