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