dfc3ede3bd59cdc2e57c46bffeac1e1495e3cc72
[gem5.git] / src / python / m5 / params.py
1 # Copyright (c) 2012-2013 ARM Limited
2 # All rights reserved.
3 #
4 # The license below extends only to copyright in the software and shall
5 # not be construed as granting a license to any other intellectual
6 # property including but not limited to intellectual property relating
7 # to a hardware implementation of the functionality of the software
8 # licensed hereunder. You may use the software subject to the license
9 # terms below provided that you ensure that this notice is replicated
10 # unmodified and in its entirety in all distributions of the software,
11 # modified or unmodified, in source code or in binary form.
12 #
13 # Copyright (c) 2004-2006 The Regents of The University of Michigan
14 # Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
15 # All rights reserved.
16 #
17 # Redistribution and use in source and binary forms, with or without
18 # modification, are permitted provided that the following conditions are
19 # met: redistributions of source code must retain the above copyright
20 # notice, this list of conditions and the following disclaimer;
21 # redistributions in binary form must reproduce the above copyright
22 # notice, this list of conditions and the following disclaimer in the
23 # documentation and/or other materials provided with the distribution;
24 # neither the name of the copyright holders nor the names of its
25 # contributors may be used to endorse or promote products derived from
26 # this software without specific prior written permission.
27 #
28 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 #
40 # Authors: Steve Reinhardt
41 # Nathan Binkert
42 # Gabe Black
43 # Andreas Hansson
44
45 #####################################################################
46 #
47 # Parameter description classes
48 #
49 # The _params dictionary in each class maps parameter names to either
50 # a Param or a VectorParam object. These objects contain the
51 # parameter description string, the parameter type, and the default
52 # value (if any). The convert() method on these objects is used to
53 # force whatever value is assigned to the parameter to the appropriate
54 # type.
55 #
56 # Note that the default values are loaded into the class's attribute
57 # space when the parameter dictionary is initialized (in
58 # MetaSimObject._new_param()); after that point they aren't used.
59 #
60 #####################################################################
61
62 import copy
63 import datetime
64 import re
65 import sys
66 import time
67 import math
68
69 import proxy
70 import ticks
71 from util import *
72
73 def isSimObject(*args, **kwargs):
74 return SimObject.isSimObject(*args, **kwargs)
75
76 def isSimObjectSequence(*args, **kwargs):
77 return SimObject.isSimObjectSequence(*args, **kwargs)
78
79 def isSimObjectClass(*args, **kwargs):
80 return SimObject.isSimObjectClass(*args, **kwargs)
81
82 allParams = {}
83
84 class MetaParamValue(type):
85 def __new__(mcls, name, bases, dct):
86 cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
87 assert name not in allParams
88 allParams[name] = cls
89 return cls
90
91
92 # Dummy base class to identify types that are legitimate for SimObject
93 # parameters.
94 class ParamValue(object):
95 __metaclass__ = MetaParamValue
96 cmd_line_settable = False
97
98 # Generate the code needed as a prerequisite for declaring a C++
99 # object of this type. Typically generates one or more #include
100 # statements. Used when declaring parameters of this type.
101 @classmethod
102 def cxx_predecls(cls, code):
103 pass
104
105 # Generate the code needed as a prerequisite for including a
106 # reference to a C++ object of this type in a SWIG .i file.
107 # Typically generates one or more %import or %include statements.
108 @classmethod
109 def swig_predecls(cls, code):
110 pass
111
112 # default for printing to .ini file is regular string conversion.
113 # will be overridden in some cases
114 def ini_str(self):
115 return str(self)
116
117 # allows us to blithely call unproxy() on things without checking
118 # if they're really proxies or not
119 def unproxy(self, base):
120 return self
121
122 # Produce a human readable version of the stored value
123 def pretty_print(self, value):
124 return str(value)
125
126 # Regular parameter description.
127 class ParamDesc(object):
128 def __init__(self, ptype_str, ptype, *args, **kwargs):
129 self.ptype_str = ptype_str
130 # remember ptype only if it is provided
131 if ptype != None:
132 self.ptype = ptype
133
134 if args:
135 if len(args) == 1:
136 self.desc = args[0]
137 elif len(args) == 2:
138 self.default = args[0]
139 self.desc = args[1]
140 else:
141 raise TypeError, 'too many arguments'
142
143 if kwargs.has_key('desc'):
144 assert(not hasattr(self, 'desc'))
145 self.desc = kwargs['desc']
146 del kwargs['desc']
147
148 if kwargs.has_key('default'):
149 assert(not hasattr(self, 'default'))
150 self.default = kwargs['default']
151 del kwargs['default']
152
153 if kwargs:
154 raise TypeError, 'extra unknown kwargs %s' % kwargs
155
156 if not hasattr(self, 'desc'):
157 raise TypeError, 'desc attribute missing'
158
159 def __getattr__(self, attr):
160 if attr == 'ptype':
161 ptype = SimObject.allClasses[self.ptype_str]
162 assert isSimObjectClass(ptype)
163 self.ptype = ptype
164 return ptype
165
166 raise AttributeError, "'%s' object has no attribute '%s'" % \
167 (type(self).__name__, attr)
168
169 def example_str(self):
170 if hasattr(self.ptype, "ex_str"):
171 return self.ptype.ex_str
172 else:
173 return self.ptype_str
174
175 # Is the param available to be exposed on the command line
176 def isCmdLineSettable(self):
177 if hasattr(self.ptype, "cmd_line_settable"):
178 return self.ptype.cmd_line_settable
179 else:
180 return False
181
182 def convert(self, value):
183 if isinstance(value, proxy.BaseProxy):
184 value.set_param_desc(self)
185 return value
186 if not hasattr(self, 'ptype') and isNullPointer(value):
187 # deferred evaluation of SimObject; continue to defer if
188 # we're just assigning a null pointer
189 return value
190 if isinstance(value, self.ptype):
191 return value
192 if isNullPointer(value) and isSimObjectClass(self.ptype):
193 return value
194 return self.ptype(value)
195
196 def pretty_print(self, value):
197 if isinstance(value, proxy.BaseProxy):
198 return str(value)
199 if isNullPointer(value):
200 return NULL
201 return self.ptype(value).pretty_print(value)
202
203 def cxx_predecls(self, code):
204 code('#include <cstddef>')
205 self.ptype.cxx_predecls(code)
206
207 def swig_predecls(self, code):
208 self.ptype.swig_predecls(code)
209
210 def cxx_decl(self, code):
211 code('${{self.ptype.cxx_type}} ${{self.name}};')
212
213 # Vector-valued parameter description. Just like ParamDesc, except
214 # that the value is a vector (list) of the specified type instead of a
215 # single value.
216
217 class VectorParamValue(list):
218 __metaclass__ = MetaParamValue
219 def __setattr__(self, attr, value):
220 raise AttributeError, \
221 "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
222
223 def ini_str(self):
224 return ' '.join([v.ini_str() for v in self])
225
226 def getValue(self):
227 return [ v.getValue() for v in self ]
228
229 def unproxy(self, base):
230 if len(self) == 1 and isinstance(self[0], proxy.AllProxy):
231 return self[0].unproxy(base)
232 else:
233 return [v.unproxy(base) for v in self]
234
235 class SimObjectVector(VectorParamValue):
236 # support clone operation
237 def __call__(self, **kwargs):
238 return SimObjectVector([v(**kwargs) for v in self])
239
240 def clear_parent(self, old_parent):
241 for v in self:
242 v.clear_parent(old_parent)
243
244 def set_parent(self, parent, name):
245 if len(self) == 1:
246 self[0].set_parent(parent, name)
247 else:
248 width = int(math.ceil(math.log(len(self))/math.log(10)))
249 for i,v in enumerate(self):
250 v.set_parent(parent, "%s%0*d" % (name, width, i))
251
252 def has_parent(self):
253 return reduce(lambda x,y: x and y, [v.has_parent() for v in self])
254
255 # return 'cpu0 cpu1' etc. for print_ini()
256 def get_name(self):
257 return ' '.join([v._name for v in self])
258
259 # By iterating through the constituent members of the vector here
260 # we can nicely handle iterating over all a SimObject's children
261 # without having to provide lots of special functions on
262 # SimObjectVector directly.
263 def descendants(self):
264 for v in self:
265 for obj in v.descendants():
266 yield obj
267
268 def get_config_as_dict(self):
269 a = []
270 for v in self:
271 a.append(v.get_config_as_dict())
272 return a
273
274 # If we are replacing an item in the vector, make sure to set the
275 # parent reference of the new SimObject to be the same as the parent
276 # of the SimObject being replaced. Useful to have if we created
277 # a SimObjectVector of temporary objects that will be modified later in
278 # configuration scripts.
279 def __setitem__(self, key, value):
280 val = self[key]
281 if value.has_parent():
282 warn("SimObject %s already has a parent" % value.get_name() +\
283 " that is being overwritten by a SimObjectVector")
284 value.set_parent(val.get_parent(), val._name)
285 super(SimObjectVector, self).__setitem__(key, value)
286
287 # Enumerate the params of each member of the SimObject vector. Creates
288 # strings that will allow indexing into the vector by the python code and
289 # allow it to be specified on the command line.
290 def enumerateParams(self, flags_dict = {},
291 cmd_line_str = "",
292 access_str = ""):
293 if hasattr(self, "_paramEnumed"):
294 print "Cycle detected enumerating params at %s?!" % (cmd_line_str)
295 else:
296 x = 0
297 for vals in self:
298 # Each entry in the SimObjectVector should be an
299 # instance of a SimObject
300 flags_dict = vals.enumerateParams(flags_dict,
301 cmd_line_str + "%d." % x,
302 access_str + "[%d]." % x)
303 x = x + 1
304
305 return flags_dict
306
307 class VectorParamDesc(ParamDesc):
308 # Convert assigned value to appropriate type. If the RHS is not a
309 # list or tuple, it generates a single-element list.
310 def convert(self, value):
311 if isinstance(value, (list, tuple)):
312 # list: coerce each element into new list
313 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
314 else:
315 # singleton: coerce to a single-element list
316 tmp_list = [ ParamDesc.convert(self, value) ]
317
318 if isSimObjectSequence(tmp_list):
319 return SimObjectVector(tmp_list)
320 else:
321 return VectorParamValue(tmp_list)
322
323 # Produce a human readable example string that describes
324 # how to set this vector parameter in the absence of a default
325 # value.
326 def example_str(self):
327 s = super(VectorParamDesc, self).example_str()
328 help_str = "[" + s + "," + s + ", ...]"
329 return help_str
330
331 # Produce a human readable representation of the value of this vector param.
332 def pretty_print(self, value):
333 if isinstance(value, (list, tuple)):
334 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ]
335 elif isinstance(value, str):
336 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ]
337 else:
338 tmp_list = [ ParamDesc.pretty_print(self, value) ]
339
340 return tmp_list
341
342 # This is a helper function for the new config system
343 def __call__(self, value):
344 if isinstance(value, (list, tuple)):
345 # list: coerce each element into new list
346 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
347 elif isinstance(value, str):
348 # If input is a csv string
349 tmp_list = [ ParamDesc.convert(self, v) for v in value.split(',') ]
350 else:
351 # singleton: coerce to a single-element list
352 tmp_list = [ ParamDesc.convert(self, value) ]
353
354 return VectorParamValue(tmp_list)
355
356 def swig_module_name(self):
357 return "%s_vector" % self.ptype_str
358
359 def swig_predecls(self, code):
360 code('%import "${{self.swig_module_name()}}.i"')
361
362 def swig_decl(self, code):
363 code('%module(package="m5.internal") ${{self.swig_module_name()}}')
364 code('%{')
365 self.ptype.cxx_predecls(code)
366 code('%}')
367 code()
368 # Make sure the SWIGPY_SLICE_ARG is defined through this inclusion
369 code('%include "std_container.i"')
370 code()
371 self.ptype.swig_predecls(code)
372 code()
373 code('%include "std_vector.i"')
374 code()
375
376 ptype = self.ptype_str
377 cxx_type = self.ptype.cxx_type
378
379 code('%template(vector_$ptype) std::vector< $cxx_type >;')
380
381 def cxx_predecls(self, code):
382 code('#include <vector>')
383 self.ptype.cxx_predecls(code)
384
385 def cxx_decl(self, code):
386 code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
387
388 class ParamFactory(object):
389 def __init__(self, param_desc_class, ptype_str = None):
390 self.param_desc_class = param_desc_class
391 self.ptype_str = ptype_str
392
393 def __getattr__(self, attr):
394 if self.ptype_str:
395 attr = self.ptype_str + '.' + attr
396 return ParamFactory(self.param_desc_class, attr)
397
398 # E.g., Param.Int(5, "number of widgets")
399 def __call__(self, *args, **kwargs):
400 ptype = None
401 try:
402 ptype = allParams[self.ptype_str]
403 except KeyError:
404 # if name isn't defined yet, assume it's a SimObject, and
405 # try to resolve it later
406 pass
407 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
408
409 Param = ParamFactory(ParamDesc)
410 VectorParam = ParamFactory(VectorParamDesc)
411
412 #####################################################################
413 #
414 # Parameter Types
415 #
416 # Though native Python types could be used to specify parameter types
417 # (the 'ptype' field of the Param and VectorParam classes), it's more
418 # flexible to define our own set of types. This gives us more control
419 # over how Python expressions are converted to values (via the
420 # __init__() constructor) and how these values are printed out (via
421 # the __str__() conversion method).
422 #
423 #####################################################################
424
425 # String-valued parameter. Just mixin the ParamValue class with the
426 # built-in str class.
427 class String(ParamValue,str):
428 cxx_type = 'std::string'
429 cmd_line_settable = True
430
431 @classmethod
432 def cxx_predecls(self, code):
433 code('#include <string>')
434
435 @classmethod
436 def swig_predecls(cls, code):
437 code('%include "std_string.i"')
438
439 def __call__(self, value):
440 self = value
441 return value
442
443 def getValue(self):
444 return self
445
446 # superclass for "numeric" parameter values, to emulate math
447 # operations in a type-safe way. e.g., a Latency times an int returns
448 # a new Latency object.
449 class NumericParamValue(ParamValue):
450 def __str__(self):
451 return str(self.value)
452
453 def __float__(self):
454 return float(self.value)
455
456 def __long__(self):
457 return long(self.value)
458
459 def __int__(self):
460 return int(self.value)
461
462 # hook for bounds checking
463 def _check(self):
464 return
465
466 def __mul__(self, other):
467 newobj = self.__class__(self)
468 newobj.value *= other
469 newobj._check()
470 return newobj
471
472 __rmul__ = __mul__
473
474 def __div__(self, other):
475 newobj = self.__class__(self)
476 newobj.value /= other
477 newobj._check()
478 return newobj
479
480 def __sub__(self, other):
481 newobj = self.__class__(self)
482 newobj.value -= other
483 newobj._check()
484 return newobj
485
486 # Metaclass for bounds-checked integer parameters. See CheckedInt.
487 class CheckedIntType(MetaParamValue):
488 def __init__(cls, name, bases, dict):
489 super(CheckedIntType, cls).__init__(name, bases, dict)
490
491 # CheckedInt is an abstract base class, so we actually don't
492 # want to do any processing on it... the rest of this code is
493 # just for classes that derive from CheckedInt.
494 if name == 'CheckedInt':
495 return
496
497 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
498 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
499 panic("CheckedInt subclass %s must define either\n" \
500 " 'min' and 'max' or 'size' and 'unsigned'\n",
501 name);
502 if cls.unsigned:
503 cls.min = 0
504 cls.max = 2 ** cls.size - 1
505 else:
506 cls.min = -(2 ** (cls.size - 1))
507 cls.max = (2 ** (cls.size - 1)) - 1
508
509 # Abstract superclass for bounds-checked integer parameters. This
510 # class is subclassed to generate parameter classes with specific
511 # bounds. Initialization of the min and max bounds is done in the
512 # metaclass CheckedIntType.__init__.
513 class CheckedInt(NumericParamValue):
514 __metaclass__ = CheckedIntType
515 cmd_line_settable = True
516
517 def _check(self):
518 if not self.min <= self.value <= self.max:
519 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
520 (self.min, self.value, self.max)
521
522 def __init__(self, value):
523 if isinstance(value, str):
524 self.value = convert.toInteger(value)
525 elif isinstance(value, (int, long, float, NumericParamValue)):
526 self.value = long(value)
527 else:
528 raise TypeError, "Can't convert object of type %s to CheckedInt" \
529 % type(value).__name__
530 self._check()
531
532 def __call__(self, value):
533 self.__init__(value)
534 return value
535
536 @classmethod
537 def cxx_predecls(cls, code):
538 # most derived types require this, so we just do it here once
539 code('#include "base/types.hh"')
540
541 @classmethod
542 def swig_predecls(cls, code):
543 # most derived types require this, so we just do it here once
544 code('%import "stdint.i"')
545 code('%import "base/types.hh"')
546
547 def getValue(self):
548 return long(self.value)
549
550 class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
551 class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
552
553 class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
554 class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
555 class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
556 class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
557 class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
558 class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
559 class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
560 class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
561
562 class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
563 class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
564 class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
565 class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
566
567 class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
568
569 class Cycles(CheckedInt):
570 cxx_type = 'Cycles'
571 size = 64
572 unsigned = True
573
574 def getValue(self):
575 from m5.internal.core import Cycles
576 return Cycles(self.value)
577
578 class Float(ParamValue, float):
579 cxx_type = 'double'
580 cmdLineSettable = True
581
582 def __init__(self, value):
583 if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
584 self.value = float(value)
585 else:
586 raise TypeError, "Can't convert object of type %s to Float" \
587 % type(value).__name__
588
589 def __call__(self, value):
590 self.__init__(value)
591 return value
592
593 def getValue(self):
594 return float(self.value)
595
596 class MemorySize(CheckedInt):
597 cxx_type = 'uint64_t'
598 ex_str = '512MB'
599 size = 64
600 unsigned = True
601 def __init__(self, value):
602 if isinstance(value, MemorySize):
603 self.value = value.value
604 else:
605 self.value = convert.toMemorySize(value)
606 self._check()
607
608 class MemorySize32(CheckedInt):
609 cxx_type = 'uint32_t'
610 ex_str = '512MB'
611 size = 32
612 unsigned = True
613 def __init__(self, value):
614 if isinstance(value, MemorySize):
615 self.value = value.value
616 else:
617 self.value = convert.toMemorySize(value)
618 self._check()
619
620 class Addr(CheckedInt):
621 cxx_type = 'Addr'
622 size = 64
623 unsigned = True
624 def __init__(self, value):
625 if isinstance(value, Addr):
626 self.value = value.value
627 else:
628 try:
629 # Often addresses are referred to with sizes. Ex: A device
630 # base address is at "512MB". Use toMemorySize() to convert
631 # these into addresses. If the address is not specified with a
632 # "size", an exception will occur and numeric translation will
633 # proceed below.
634 self.value = convert.toMemorySize(value)
635 except (TypeError, ValueError):
636 # Convert number to string and use long() to do automatic
637 # base conversion (requires base=0 for auto-conversion)
638 self.value = long(str(value), base=0)
639
640 self._check()
641 def __add__(self, other):
642 if isinstance(other, Addr):
643 return self.value + other.value
644 else:
645 return self.value + other
646 def pretty_print(self, value):
647 try:
648 val = convert.toMemorySize(value)
649 except TypeError:
650 val = long(value)
651 return "0x%x" % long(val)
652
653 class AddrRange(ParamValue):
654 cxx_type = 'AddrRange'
655
656 def __init__(self, *args, **kwargs):
657 # Disable interleaving by default
658 self.intlvHighBit = 0
659 self.intlvBits = 0
660 self.intlvMatch = 0
661
662 def handle_kwargs(self, kwargs):
663 # An address range needs to have an upper limit, specified
664 # either explicitly with an end, or as an offset using the
665 # size keyword.
666 if 'end' in kwargs:
667 self.end = Addr(kwargs.pop('end'))
668 elif 'size' in kwargs:
669 self.end = self.start + Addr(kwargs.pop('size')) - 1
670 else:
671 raise TypeError, "Either end or size must be specified"
672
673 # Now on to the optional bit
674 if 'intlvHighBit' in kwargs:
675 self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
676 if 'intlvBits' in kwargs:
677 self.intlvBits = int(kwargs.pop('intlvBits'))
678 if 'intlvMatch' in kwargs:
679 self.intlvMatch = int(kwargs.pop('intlvMatch'))
680
681 if len(args) == 0:
682 self.start = Addr(kwargs.pop('start'))
683 handle_kwargs(self, kwargs)
684
685 elif len(args) == 1:
686 if kwargs:
687 self.start = Addr(args[0])
688 handle_kwargs(self, kwargs)
689 elif isinstance(args[0], (list, tuple)):
690 self.start = Addr(args[0][0])
691 self.end = Addr(args[0][1])
692 else:
693 self.start = Addr(0)
694 self.end = Addr(args[0]) - 1
695
696 elif len(args) == 2:
697 self.start = Addr(args[0])
698 self.end = Addr(args[1])
699 else:
700 raise TypeError, "Too many arguments specified"
701
702 if kwargs:
703 raise TypeError, "Too many keywords: %s" % kwargs.keys()
704
705 def __str__(self):
706 return '%s:%s' % (self.start, self.end)
707
708 def size(self):
709 # Divide the size by the size of the interleaving slice
710 return (long(self.end) - long(self.start) + 1) >> self.intlvBits
711
712 @classmethod
713 def cxx_predecls(cls, code):
714 Addr.cxx_predecls(code)
715 code('#include "base/addr_range.hh"')
716
717 @classmethod
718 def swig_predecls(cls, code):
719 Addr.swig_predecls(code)
720
721 def getValue(self):
722 # Go from the Python class to the wrapped C++ class generated
723 # by swig
724 from m5.internal.range import AddrRange
725
726 return AddrRange(long(self.start), long(self.end),
727 int(self.intlvHighBit), int(self.intlvBits),
728 int(self.intlvMatch))
729
730 # Boolean parameter type. Python doesn't let you subclass bool, since
731 # it doesn't want to let you create multiple instances of True and
732 # False. Thus this is a little more complicated than String.
733 class Bool(ParamValue):
734 cxx_type = 'bool'
735 cmd_line_settable = True
736
737 def __init__(self, value):
738 try:
739 self.value = convert.toBool(value)
740 except TypeError:
741 self.value = bool(value)
742
743 def __call__(self, value):
744 self.__init__(value)
745 return value
746
747 def getValue(self):
748 return bool(self.value)
749
750 def __str__(self):
751 return str(self.value)
752
753 # implement truth value testing for Bool parameters so that these params
754 # evaluate correctly during the python configuration phase
755 def __nonzero__(self):
756 return bool(self.value)
757
758 def ini_str(self):
759 if self.value:
760 return 'true'
761 return 'false'
762
763 def IncEthernetAddr(addr, val = 1):
764 bytes = map(lambda x: int(x, 16), addr.split(':'))
765 bytes[5] += val
766 for i in (5, 4, 3, 2, 1):
767 val,rem = divmod(bytes[i], 256)
768 bytes[i] = rem
769 if val == 0:
770 break
771 bytes[i - 1] += val
772 assert(bytes[0] <= 255)
773 return ':'.join(map(lambda x: '%02x' % x, bytes))
774
775 _NextEthernetAddr = "00:90:00:00:00:01"
776 def NextEthernetAddr():
777 global _NextEthernetAddr
778
779 value = _NextEthernetAddr
780 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
781 return value
782
783 class EthernetAddr(ParamValue):
784 cxx_type = 'Net::EthAddr'
785 ex_str = "00:90:00:00:00:01"
786 cmd_line_settable = True
787
788 @classmethod
789 def cxx_predecls(cls, code):
790 code('#include "base/inet.hh"')
791
792 @classmethod
793 def swig_predecls(cls, code):
794 code('%include "python/swig/inet.i"')
795
796 def __init__(self, value):
797 if value == NextEthernetAddr:
798 self.value = value
799 return
800
801 if not isinstance(value, str):
802 raise TypeError, "expected an ethernet address and didn't get one"
803
804 bytes = value.split(':')
805 if len(bytes) != 6:
806 raise TypeError, 'invalid ethernet address %s' % value
807
808 for byte in bytes:
809 if not 0 <= int(byte, base=16) <= 0xff:
810 raise TypeError, 'invalid ethernet address %s' % value
811
812 self.value = value
813
814 def __call__(self, value):
815 self.__init__(value)
816 return value
817
818 def unproxy(self, base):
819 if self.value == NextEthernetAddr:
820 return EthernetAddr(self.value())
821 return self
822
823 def getValue(self):
824 from m5.internal.params import EthAddr
825 return EthAddr(self.value)
826
827 def ini_str(self):
828 return self.value
829
830 # When initializing an IpAddress, pass in an existing IpAddress, a string of
831 # the form "a.b.c.d", or an integer representing an IP.
832 class IpAddress(ParamValue):
833 cxx_type = 'Net::IpAddress'
834 ex_str = "127.0.0.1"
835 cmd_line_settable = True
836
837 @classmethod
838 def cxx_predecls(cls, code):
839 code('#include "base/inet.hh"')
840
841 @classmethod
842 def swig_predecls(cls, code):
843 code('%include "python/swig/inet.i"')
844
845 def __init__(self, value):
846 if isinstance(value, IpAddress):
847 self.ip = value.ip
848 else:
849 try:
850 self.ip = convert.toIpAddress(value)
851 except TypeError:
852 self.ip = long(value)
853 self.verifyIp()
854
855 def __call__(self, value):
856 self.__init__(value)
857 return value
858
859 def __str__(self):
860 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)]
861 return '%d.%d.%d.%d' % tuple(tup)
862
863 def __eq__(self, other):
864 if isinstance(other, IpAddress):
865 return self.ip == other.ip
866 elif isinstance(other, str):
867 try:
868 return self.ip == convert.toIpAddress(other)
869 except:
870 return False
871 else:
872 return self.ip == other
873
874 def __ne__(self, other):
875 return not (self == other)
876
877 def verifyIp(self):
878 if self.ip < 0 or self.ip >= (1 << 32):
879 raise TypeError, "invalid ip address %#08x" % self.ip
880
881 def getValue(self):
882 from m5.internal.params import IpAddress
883 return IpAddress(self.ip)
884
885 # When initializing an IpNetmask, pass in an existing IpNetmask, a string of
886 # the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
887 # positional or keyword arguments.
888 class IpNetmask(IpAddress):
889 cxx_type = 'Net::IpNetmask'
890 ex_str = "127.0.0.0/24"
891 cmd_line_settable = True
892
893 @classmethod
894 def cxx_predecls(cls, code):
895 code('#include "base/inet.hh"')
896
897 @classmethod
898 def swig_predecls(cls, code):
899 code('%include "python/swig/inet.i"')
900
901 def __init__(self, *args, **kwargs):
902 def handle_kwarg(self, kwargs, key, elseVal = None):
903 if key in kwargs:
904 setattr(self, key, kwargs.pop(key))
905 elif elseVal:
906 setattr(self, key, elseVal)
907 else:
908 raise TypeError, "No value set for %s" % key
909
910 if len(args) == 0:
911 handle_kwarg(self, kwargs, 'ip')
912 handle_kwarg(self, kwargs, 'netmask')
913
914 elif len(args) == 1:
915 if kwargs:
916 if not 'ip' in kwargs and not 'netmask' in kwargs:
917 raise TypeError, "Invalid arguments"
918 handle_kwarg(self, kwargs, 'ip', args[0])
919 handle_kwarg(self, kwargs, 'netmask', args[0])
920 elif isinstance(args[0], IpNetmask):
921 self.ip = args[0].ip
922 self.netmask = args[0].netmask
923 else:
924 (self.ip, self.netmask) = convert.toIpNetmask(args[0])
925
926 elif len(args) == 2:
927 self.ip = args[0]
928 self.netmask = args[1]
929 else:
930 raise TypeError, "Too many arguments specified"
931
932 if kwargs:
933 raise TypeError, "Too many keywords: %s" % kwargs.keys()
934
935 self.verify()
936
937 def __call__(self, value):
938 self.__init__(value)
939 return value
940
941 def __str__(self):
942 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
943
944 def __eq__(self, other):
945 if isinstance(other, IpNetmask):
946 return self.ip == other.ip and self.netmask == other.netmask
947 elif isinstance(other, str):
948 try:
949 return (self.ip, self.netmask) == convert.toIpNetmask(other)
950 except:
951 return False
952 else:
953 return False
954
955 def verify(self):
956 self.verifyIp()
957 if self.netmask < 0 or self.netmask > 32:
958 raise TypeError, "invalid netmask %d" % netmask
959
960 def getValue(self):
961 from m5.internal.params import IpNetmask
962 return IpNetmask(self.ip, self.netmask)
963
964 # When initializing an IpWithPort, pass in an existing IpWithPort, a string of
965 # the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
966 class IpWithPort(IpAddress):
967 cxx_type = 'Net::IpWithPort'
968 ex_str = "127.0.0.1:80"
969 cmd_line_settable = True
970
971 @classmethod
972 def cxx_predecls(cls, code):
973 code('#include "base/inet.hh"')
974
975 @classmethod
976 def swig_predecls(cls, code):
977 code('%include "python/swig/inet.i"')
978
979 def __init__(self, *args, **kwargs):
980 def handle_kwarg(self, kwargs, key, elseVal = None):
981 if key in kwargs:
982 setattr(self, key, kwargs.pop(key))
983 elif elseVal:
984 setattr(self, key, elseVal)
985 else:
986 raise TypeError, "No value set for %s" % key
987
988 if len(args) == 0:
989 handle_kwarg(self, kwargs, 'ip')
990 handle_kwarg(self, kwargs, 'port')
991
992 elif len(args) == 1:
993 if kwargs:
994 if not 'ip' in kwargs and not 'port' in kwargs:
995 raise TypeError, "Invalid arguments"
996 handle_kwarg(self, kwargs, 'ip', args[0])
997 handle_kwarg(self, kwargs, 'port', args[0])
998 elif isinstance(args[0], IpWithPort):
999 self.ip = args[0].ip
1000 self.port = args[0].port
1001 else:
1002 (self.ip, self.port) = convert.toIpWithPort(args[0])
1003
1004 elif len(args) == 2:
1005 self.ip = args[0]
1006 self.port = args[1]
1007 else:
1008 raise TypeError, "Too many arguments specified"
1009
1010 if kwargs:
1011 raise TypeError, "Too many keywords: %s" % kwargs.keys()
1012
1013 self.verify()
1014
1015 def __call__(self, value):
1016 self.__init__(value)
1017 return value
1018
1019 def __str__(self):
1020 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1021
1022 def __eq__(self, other):
1023 if isinstance(other, IpWithPort):
1024 return self.ip == other.ip and self.port == other.port
1025 elif isinstance(other, str):
1026 try:
1027 return (self.ip, self.port) == convert.toIpWithPort(other)
1028 except:
1029 return False
1030 else:
1031 return False
1032
1033 def verify(self):
1034 self.verifyIp()
1035 if self.port < 0 or self.port > 0xffff:
1036 raise TypeError, "invalid port %d" % self.port
1037
1038 def getValue(self):
1039 from m5.internal.params import IpWithPort
1040 return IpWithPort(self.ip, self.port)
1041
1042 time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1043 "%a %b %d %H:%M:%S %Z %Y",
1044 "%Y/%m/%d %H:%M:%S",
1045 "%Y/%m/%d %H:%M",
1046 "%Y/%m/%d",
1047 "%m/%d/%Y %H:%M:%S",
1048 "%m/%d/%Y %H:%M",
1049 "%m/%d/%Y",
1050 "%m/%d/%y %H:%M:%S",
1051 "%m/%d/%y %H:%M",
1052 "%m/%d/%y"]
1053
1054
1055 def parse_time(value):
1056 from time import gmtime, strptime, struct_time, time
1057 from datetime import datetime, date
1058
1059 if isinstance(value, struct_time):
1060 return value
1061
1062 if isinstance(value, (int, long)):
1063 return gmtime(value)
1064
1065 if isinstance(value, (datetime, date)):
1066 return value.timetuple()
1067
1068 if isinstance(value, str):
1069 if value in ('Now', 'Today'):
1070 return time.gmtime(time.time())
1071
1072 for format in time_formats:
1073 try:
1074 return strptime(value, format)
1075 except ValueError:
1076 pass
1077
1078 raise ValueError, "Could not parse '%s' as a time" % value
1079
1080 class Time(ParamValue):
1081 cxx_type = 'tm'
1082
1083 @classmethod
1084 def cxx_predecls(cls, code):
1085 code('#include <time.h>')
1086
1087 @classmethod
1088 def swig_predecls(cls, code):
1089 code('%include "python/swig/time.i"')
1090
1091 def __init__(self, value):
1092 self.value = parse_time(value)
1093
1094 def __call__(self, value):
1095 self.__init__(value)
1096 return value
1097
1098 def getValue(self):
1099 from m5.internal.params import tm
1100
1101 c_time = tm()
1102 py_time = self.value
1103
1104 # UNIX is years since 1900
1105 c_time.tm_year = py_time.tm_year - 1900;
1106
1107 # Python starts at 1, UNIX starts at 0
1108 c_time.tm_mon = py_time.tm_mon - 1;
1109 c_time.tm_mday = py_time.tm_mday;
1110 c_time.tm_hour = py_time.tm_hour;
1111 c_time.tm_min = py_time.tm_min;
1112 c_time.tm_sec = py_time.tm_sec;
1113
1114 # Python has 0 as Monday, UNIX is 0 as sunday
1115 c_time.tm_wday = py_time.tm_wday + 1
1116 if c_time.tm_wday > 6:
1117 c_time.tm_wday -= 7;
1118
1119 # Python starts at 1, Unix starts at 0
1120 c_time.tm_yday = py_time.tm_yday - 1;
1121
1122 return c_time
1123
1124 def __str__(self):
1125 return time.asctime(self.value)
1126
1127 def ini_str(self):
1128 return str(self)
1129
1130 def get_config_as_dict(self):
1131 return str(self)
1132
1133 # Enumerated types are a little more complex. The user specifies the
1134 # type as Enum(foo) where foo is either a list or dictionary of
1135 # alternatives (typically strings, but not necessarily so). (In the
1136 # long run, the integer value of the parameter will be the list index
1137 # or the corresponding dictionary value. For now, since we only check
1138 # that the alternative is valid and then spit it into a .ini file,
1139 # there's not much point in using the dictionary.)
1140
1141 # What Enum() must do is generate a new type encapsulating the
1142 # provided list/dictionary so that specific values of the parameter
1143 # can be instances of that type. We define two hidden internal
1144 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1145 # derive the new type from the appropriate base class on the fly.
1146
1147 allEnums = {}
1148 # Metaclass for Enum types
1149 class MetaEnum(MetaParamValue):
1150 def __new__(mcls, name, bases, dict):
1151 assert name not in allEnums
1152
1153 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1154 allEnums[name] = cls
1155 return cls
1156
1157 def __init__(cls, name, bases, init_dict):
1158 if init_dict.has_key('map'):
1159 if not isinstance(cls.map, dict):
1160 raise TypeError, "Enum-derived class attribute 'map' " \
1161 "must be of type dict"
1162 # build list of value strings from map
1163 cls.vals = cls.map.keys()
1164 cls.vals.sort()
1165 elif init_dict.has_key('vals'):
1166 if not isinstance(cls.vals, list):
1167 raise TypeError, "Enum-derived class attribute 'vals' " \
1168 "must be of type list"
1169 # build string->value map from vals sequence
1170 cls.map = {}
1171 for idx,val in enumerate(cls.vals):
1172 cls.map[val] = idx
1173 else:
1174 raise TypeError, "Enum-derived class must define "\
1175 "attribute 'map' or 'vals'"
1176
1177 cls.cxx_type = 'Enums::%s' % name
1178
1179 super(MetaEnum, cls).__init__(name, bases, init_dict)
1180
1181 # Generate C++ class declaration for this enum type.
1182 # Note that we wrap the enum in a class/struct to act as a namespace,
1183 # so that the enum strings can be brief w/o worrying about collisions.
1184 def cxx_decl(cls, code):
1185 wrapper_name = cls.wrapper_name
1186 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1187 name = cls.__name__ if cls.enum_name is None else cls.enum_name
1188 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1189
1190 code('''\
1191 #ifndef $idem_macro
1192 #define $idem_macro
1193
1194 $wrapper $wrapper_name {
1195 enum $name {
1196 ''')
1197 code.indent(2)
1198 for val in cls.vals:
1199 code('$val = ${{cls.map[val]}},')
1200 code('Num_$name = ${{len(cls.vals)}}')
1201 code.dedent(2)
1202 code(' };')
1203
1204 if cls.wrapper_is_struct:
1205 code(' static const char *${name}Strings[Num_${name}];')
1206 code('};')
1207 else:
1208 code('extern const char *${name}Strings[Num_${name}];')
1209 code('}')
1210
1211 code()
1212 code('#endif // $idem_macro')
1213
1214 def cxx_def(cls, code):
1215 wrapper_name = cls.wrapper_name
1216 file_name = cls.__name__
1217 name = cls.__name__ if cls.enum_name is None else cls.enum_name
1218
1219 code('#include "enums/$file_name.hh"')
1220 if cls.wrapper_is_struct:
1221 code('const char *${wrapper_name}::${name}Strings'
1222 '[Num_${name}] =')
1223 else:
1224 code('namespace Enums {')
1225 code.indent(1)
1226 code(' const char *${name}Strings[Num_${name}] =')
1227
1228 code('{')
1229 code.indent(1)
1230 for val in cls.vals:
1231 code('"$val",')
1232 code.dedent(1)
1233 code('};')
1234
1235 if not cls.wrapper_is_struct:
1236 code('} // namespace $wrapper_name')
1237 code.dedent(1)
1238
1239 def swig_decl(cls, code):
1240 name = cls.__name__
1241 code('''\
1242 %module(package="m5.internal") enum_$name
1243
1244 %{
1245 #include "enums/$name.hh"
1246 %}
1247
1248 %include "enums/$name.hh"
1249 ''')
1250
1251
1252 # Base class for enum types.
1253 class Enum(ParamValue):
1254 __metaclass__ = MetaEnum
1255 vals = []
1256 cmd_line_settable = True
1257
1258 # The name of the wrapping namespace or struct
1259 wrapper_name = 'Enums'
1260
1261 # If true, the enum is wrapped in a struct rather than a namespace
1262 wrapper_is_struct = False
1263
1264 # If not None, use this as the enum name rather than this class name
1265 enum_name = None
1266
1267 def __init__(self, value):
1268 if value not in self.map:
1269 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1270 % (value, self.vals)
1271 self.value = value
1272
1273 def __call__(self, value):
1274 self.__init__(value)
1275 return value
1276
1277 @classmethod
1278 def cxx_predecls(cls, code):
1279 code('#include "enums/$0.hh"', cls.__name__)
1280
1281 @classmethod
1282 def swig_predecls(cls, code):
1283 code('%import "python/m5/internal/enum_$0.i"', cls.__name__)
1284
1285 def getValue(self):
1286 return int(self.map[self.value])
1287
1288 def __str__(self):
1289 return self.value
1290
1291 # how big does a rounding error need to be before we warn about it?
1292 frequency_tolerance = 0.001 # 0.1%
1293
1294 class TickParamValue(NumericParamValue):
1295 cxx_type = 'Tick'
1296 ex_str = "1MHz"
1297 cmd_line_settable = True
1298
1299 @classmethod
1300 def cxx_predecls(cls, code):
1301 code('#include "base/types.hh"')
1302
1303 @classmethod
1304 def swig_predecls(cls, code):
1305 code('%import "stdint.i"')
1306 code('%import "base/types.hh"')
1307
1308 def __call__(self, value):
1309 self.__init__(value)
1310 return value
1311
1312 def getValue(self):
1313 return long(self.value)
1314
1315 class Latency(TickParamValue):
1316 ex_str = "100ns"
1317
1318 def __init__(self, value):
1319 if isinstance(value, (Latency, Clock)):
1320 self.ticks = value.ticks
1321 self.value = value.value
1322 elif isinstance(value, Frequency):
1323 self.ticks = value.ticks
1324 self.value = 1.0 / value.value
1325 elif value.endswith('t'):
1326 self.ticks = True
1327 self.value = int(value[:-1])
1328 else:
1329 self.ticks = False
1330 self.value = convert.toLatency(value)
1331
1332 def __call__(self, value):
1333 self.__init__(value)
1334 return value
1335
1336 def __getattr__(self, attr):
1337 if attr in ('latency', 'period'):
1338 return self
1339 if attr == 'frequency':
1340 return Frequency(self)
1341 raise AttributeError, "Latency object has no attribute '%s'" % attr
1342
1343 def getValue(self):
1344 if self.ticks or self.value == 0:
1345 value = self.value
1346 else:
1347 value = ticks.fromSeconds(self.value)
1348 return long(value)
1349
1350 # convert latency to ticks
1351 def ini_str(self):
1352 return '%d' % self.getValue()
1353
1354 class Frequency(TickParamValue):
1355 ex_str = "1GHz"
1356
1357 def __init__(self, value):
1358 if isinstance(value, (Latency, Clock)):
1359 if value.value == 0:
1360 self.value = 0
1361 else:
1362 self.value = 1.0 / value.value
1363 self.ticks = value.ticks
1364 elif isinstance(value, Frequency):
1365 self.value = value.value
1366 self.ticks = value.ticks
1367 else:
1368 self.ticks = False
1369 self.value = convert.toFrequency(value)
1370
1371 def __call__(self, value):
1372 self.__init__(value)
1373 return value
1374
1375 def __getattr__(self, attr):
1376 if attr == 'frequency':
1377 return self
1378 if attr in ('latency', 'period'):
1379 return Latency(self)
1380 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1381
1382 # convert latency to ticks
1383 def getValue(self):
1384 if self.ticks or self.value == 0:
1385 value = self.value
1386 else:
1387 value = ticks.fromSeconds(1.0 / self.value)
1388 return long(value)
1389
1390 def ini_str(self):
1391 return '%d' % self.getValue()
1392
1393 # A generic Frequency and/or Latency value. Value is stored as a
1394 # latency, just like Latency and Frequency.
1395 class Clock(TickParamValue):
1396 def __init__(self, value):
1397 if isinstance(value, (Latency, Clock)):
1398 self.ticks = value.ticks
1399 self.value = value.value
1400 elif isinstance(value, Frequency):
1401 self.ticks = value.ticks
1402 self.value = 1.0 / value.value
1403 elif value.endswith('t'):
1404 self.ticks = True
1405 self.value = int(value[:-1])
1406 else:
1407 self.ticks = False
1408 self.value = convert.anyToLatency(value)
1409
1410 def __call__(self, value):
1411 self.__init__(value)
1412 return value
1413
1414 def __str__(self):
1415 return "%s" % Latency(self)
1416
1417 def __getattr__(self, attr):
1418 if attr == 'frequency':
1419 return Frequency(self)
1420 if attr in ('latency', 'period'):
1421 return Latency(self)
1422 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1423
1424 def getValue(self):
1425 return self.period.getValue()
1426
1427 def ini_str(self):
1428 return self.period.ini_str()
1429
1430 class Voltage(float,ParamValue):
1431 cxx_type = 'double'
1432 ex_str = "1V"
1433 cmd_line_settable = False
1434
1435 def __new__(cls, value):
1436 # convert to voltage
1437 val = convert.toVoltage(value)
1438 return super(cls, Voltage).__new__(cls, val)
1439
1440 def __call__(self, value):
1441 val = convert.toVoltage(value)
1442 self.__init__(val)
1443 return value
1444
1445 def __str__(self):
1446 return str(self.getValue())
1447
1448 def getValue(self):
1449 value = float(self)
1450 return value
1451
1452 def ini_str(self):
1453 return '%f' % self.getValue()
1454
1455 class NetworkBandwidth(float,ParamValue):
1456 cxx_type = 'float'
1457 ex_str = "1Gbps"
1458 cmd_line_settable = True
1459
1460 def __new__(cls, value):
1461 # convert to bits per second
1462 val = convert.toNetworkBandwidth(value)
1463 return super(cls, NetworkBandwidth).__new__(cls, val)
1464
1465 def __str__(self):
1466 return str(self.val)
1467
1468 def __call__(self, value):
1469 val = convert.toNetworkBandwidth(value)
1470 self.__init__(val)
1471 return value
1472
1473 def getValue(self):
1474 # convert to seconds per byte
1475 value = 8.0 / float(self)
1476 # convert to ticks per byte
1477 value = ticks.fromSeconds(value)
1478 return float(value)
1479
1480 def ini_str(self):
1481 return '%f' % self.getValue()
1482
1483 class MemoryBandwidth(float,ParamValue):
1484 cxx_type = 'float'
1485 ex_str = "1GB/s"
1486 cmd_line_settable = True
1487
1488 def __new__(cls, value):
1489 # convert to bytes per second
1490 val = convert.toMemoryBandwidth(value)
1491 return super(cls, MemoryBandwidth).__new__(cls, val)
1492
1493 def __call__(self, value):
1494 val = convert.toMemoryBandwidth(value)
1495 self.__init__(val)
1496 return value
1497
1498 def getValue(self):
1499 # convert to seconds per byte
1500 value = float(self)
1501 if value:
1502 value = 1.0 / float(self)
1503 # convert to ticks per byte
1504 value = ticks.fromSeconds(value)
1505 return float(value)
1506
1507 def ini_str(self):
1508 return '%f' % self.getValue()
1509
1510 #
1511 # "Constants"... handy aliases for various values.
1512 #
1513
1514 # Special class for NULL pointers. Note the special check in
1515 # make_param_value() above that lets these be assigned where a
1516 # SimObject is required.
1517 # only one copy of a particular node
1518 class NullSimObject(object):
1519 __metaclass__ = Singleton
1520
1521 def __call__(cls):
1522 return cls
1523
1524 def _instantiate(self, parent = None, path = ''):
1525 pass
1526
1527 def ini_str(self):
1528 return 'Null'
1529
1530 def unproxy(self, base):
1531 return self
1532
1533 def set_path(self, parent, name):
1534 pass
1535
1536 def __str__(self):
1537 return 'Null'
1538
1539 def getValue(self):
1540 return None
1541
1542 # The only instance you'll ever need...
1543 NULL = NullSimObject()
1544
1545 def isNullPointer(value):
1546 return isinstance(value, NullSimObject)
1547
1548 # Some memory range specifications use this as a default upper bound.
1549 MaxAddr = Addr.max
1550 MaxTick = Tick.max
1551 AllMemory = AddrRange(0, MaxAddr)
1552
1553
1554 #####################################################################
1555 #
1556 # Port objects
1557 #
1558 # Ports are used to interconnect objects in the memory system.
1559 #
1560 #####################################################################
1561
1562 # Port reference: encapsulates a reference to a particular port on a
1563 # particular SimObject.
1564 class PortRef(object):
1565 def __init__(self, simobj, name, role):
1566 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1567 self.simobj = simobj
1568 self.name = name
1569 self.role = role
1570 self.peer = None # not associated with another port yet
1571 self.ccConnected = False # C++ port connection done?
1572 self.index = -1 # always -1 for non-vector ports
1573
1574 def __str__(self):
1575 return '%s.%s' % (self.simobj, self.name)
1576
1577 def __len__(self):
1578 # Return the number of connected ports, i.e. 0 is we have no
1579 # peer and 1 if we do.
1580 return int(self.peer != None)
1581
1582 # for config.ini, print peer's name (not ours)
1583 def ini_str(self):
1584 return str(self.peer)
1585
1586 # for config.json
1587 def get_config_as_dict(self):
1588 return {'role' : self.role, 'peer' : str(self.peer)}
1589
1590 def __getattr__(self, attr):
1591 if attr == 'peerObj':
1592 # shorthand for proxies
1593 return self.peer.simobj
1594 raise AttributeError, "'%s' object has no attribute '%s'" % \
1595 (self.__class__.__name__, attr)
1596
1597 # Full connection is symmetric (both ways). Called via
1598 # SimObject.__setattr__ as a result of a port assignment, e.g.,
1599 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1600 # e.g., "obj1.portA[3] = obj2.portB".
1601 def connect(self, other):
1602 if isinstance(other, VectorPortRef):
1603 # reference to plain VectorPort is implicit append
1604 other = other._get_next()
1605 if self.peer and not proxy.isproxy(self.peer):
1606 fatal("Port %s is already connected to %s, cannot connect %s\n",
1607 self, self.peer, other);
1608 self.peer = other
1609 if proxy.isproxy(other):
1610 other.set_param_desc(PortParamDesc())
1611 elif isinstance(other, PortRef):
1612 if other.peer is not self:
1613 other.connect(self)
1614 else:
1615 raise TypeError, \
1616 "assigning non-port reference '%s' to port '%s'" \
1617 % (other, self)
1618
1619 def clone(self, simobj, memo):
1620 if memo.has_key(self):
1621 return memo[self]
1622 newRef = copy.copy(self)
1623 memo[self] = newRef
1624 newRef.simobj = simobj
1625 assert(isSimObject(newRef.simobj))
1626 if self.peer and not proxy.isproxy(self.peer):
1627 peerObj = self.peer.simobj(_memo=memo)
1628 newRef.peer = self.peer.clone(peerObj, memo)
1629 assert(not isinstance(newRef.peer, VectorPortRef))
1630 return newRef
1631
1632 def unproxy(self, simobj):
1633 assert(simobj is self.simobj)
1634 if proxy.isproxy(self.peer):
1635 try:
1636 realPeer = self.peer.unproxy(self.simobj)
1637 except:
1638 print "Error in unproxying port '%s' of %s" % \
1639 (self.name, self.simobj.path())
1640 raise
1641 self.connect(realPeer)
1642
1643 # Call C++ to create corresponding port connection between C++ objects
1644 def ccConnect(self):
1645 from m5.internal.pyobject import connectPorts
1646
1647 if self.role == 'SLAVE':
1648 # do nothing and let the master take care of it
1649 return
1650
1651 if self.ccConnected: # already done this
1652 return
1653 peer = self.peer
1654 if not self.peer: # nothing to connect to
1655 return
1656
1657 # check that we connect a master to a slave
1658 if self.role == peer.role:
1659 raise TypeError, \
1660 "cannot connect '%s' and '%s' due to identical role '%s'" \
1661 % (peer, self, self.role)
1662
1663 try:
1664 # self is always the master and peer the slave
1665 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1666 peer.simobj.getCCObject(), peer.name, peer.index)
1667 except:
1668 print "Error connecting port %s.%s to %s.%s" % \
1669 (self.simobj.path(), self.name,
1670 peer.simobj.path(), peer.name)
1671 raise
1672 self.ccConnected = True
1673 peer.ccConnected = True
1674
1675 # A reference to an individual element of a VectorPort... much like a
1676 # PortRef, but has an index.
1677 class VectorPortElementRef(PortRef):
1678 def __init__(self, simobj, name, role, index):
1679 PortRef.__init__(self, simobj, name, role)
1680 self.index = index
1681
1682 def __str__(self):
1683 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1684
1685 # A reference to a complete vector-valued port (not just a single element).
1686 # Can be indexed to retrieve individual VectorPortElementRef instances.
1687 class VectorPortRef(object):
1688 def __init__(self, simobj, name, role):
1689 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1690 self.simobj = simobj
1691 self.name = name
1692 self.role = role
1693 self.elements = []
1694
1695 def __str__(self):
1696 return '%s.%s[:]' % (self.simobj, self.name)
1697
1698 def __len__(self):
1699 # Return the number of connected peers, corresponding the the
1700 # length of the elements.
1701 return len(self.elements)
1702
1703 # for config.ini, print peer's name (not ours)
1704 def ini_str(self):
1705 return ' '.join([el.ini_str() for el in self.elements])
1706
1707 # for config.json
1708 def get_config_as_dict(self):
1709 return {'role' : self.role,
1710 'peer' : [el.ini_str() for el in self.elements]}
1711
1712 def __getitem__(self, key):
1713 if not isinstance(key, int):
1714 raise TypeError, "VectorPort index must be integer"
1715 if key >= len(self.elements):
1716 # need to extend list
1717 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1718 for i in range(len(self.elements), key+1)]
1719 self.elements.extend(ext)
1720 return self.elements[key]
1721
1722 def _get_next(self):
1723 return self[len(self.elements)]
1724
1725 def __setitem__(self, key, value):
1726 if not isinstance(key, int):
1727 raise TypeError, "VectorPort index must be integer"
1728 self[key].connect(value)
1729
1730 def connect(self, other):
1731 if isinstance(other, (list, tuple)):
1732 # Assign list of port refs to vector port.
1733 # For now, append them... not sure if that's the right semantics
1734 # or if it should replace the current vector.
1735 for ref in other:
1736 self._get_next().connect(ref)
1737 else:
1738 # scalar assignment to plain VectorPort is implicit append
1739 self._get_next().connect(other)
1740
1741 def clone(self, simobj, memo):
1742 if memo.has_key(self):
1743 return memo[self]
1744 newRef = copy.copy(self)
1745 memo[self] = newRef
1746 newRef.simobj = simobj
1747 assert(isSimObject(newRef.simobj))
1748 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1749 return newRef
1750
1751 def unproxy(self, simobj):
1752 [el.unproxy(simobj) for el in self.elements]
1753
1754 def ccConnect(self):
1755 [el.ccConnect() for el in self.elements]
1756
1757 # Port description object. Like a ParamDesc object, this represents a
1758 # logical port in the SimObject class, not a particular port on a
1759 # SimObject instance. The latter are represented by PortRef objects.
1760 class Port(object):
1761 # Generate a PortRef for this port on the given SimObject with the
1762 # given name
1763 def makeRef(self, simobj):
1764 return PortRef(simobj, self.name, self.role)
1765
1766 # Connect an instance of this port (on the given SimObject with
1767 # the given name) with the port described by the supplied PortRef
1768 def connect(self, simobj, ref):
1769 self.makeRef(simobj).connect(ref)
1770
1771 # No need for any pre-declarations at the moment as we merely rely
1772 # on an unsigned int.
1773 def cxx_predecls(self, code):
1774 pass
1775
1776 # Declare an unsigned int with the same name as the port, that
1777 # will eventually hold the number of connected ports (and thus the
1778 # number of elements for a VectorPort).
1779 def cxx_decl(self, code):
1780 code('unsigned int port_${{self.name}}_connection_count;')
1781
1782 class MasterPort(Port):
1783 # MasterPort("description")
1784 def __init__(self, *args):
1785 if len(args) == 1:
1786 self.desc = args[0]
1787 self.role = 'MASTER'
1788 else:
1789 raise TypeError, 'wrong number of arguments'
1790
1791 class SlavePort(Port):
1792 # SlavePort("description")
1793 def __init__(self, *args):
1794 if len(args) == 1:
1795 self.desc = args[0]
1796 self.role = 'SLAVE'
1797 else:
1798 raise TypeError, 'wrong number of arguments'
1799
1800 # VectorPort description object. Like Port, but represents a vector
1801 # of connections (e.g., as on a Bus).
1802 class VectorPort(Port):
1803 def __init__(self, *args):
1804 self.isVec = True
1805
1806 def makeRef(self, simobj):
1807 return VectorPortRef(simobj, self.name, self.role)
1808
1809 class VectorMasterPort(VectorPort):
1810 # VectorMasterPort("description")
1811 def __init__(self, *args):
1812 if len(args) == 1:
1813 self.desc = args[0]
1814 self.role = 'MASTER'
1815 VectorPort.__init__(self, *args)
1816 else:
1817 raise TypeError, 'wrong number of arguments'
1818
1819 class VectorSlavePort(VectorPort):
1820 # VectorSlavePort("description")
1821 def __init__(self, *args):
1822 if len(args) == 1:
1823 self.desc = args[0]
1824 self.role = 'SLAVE'
1825 VectorPort.__init__(self, *args)
1826 else:
1827 raise TypeError, 'wrong number of arguments'
1828
1829 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1830 # proxy objects (via set_param_desc()) so that proxy error messages
1831 # make sense.
1832 class PortParamDesc(object):
1833 __metaclass__ = Singleton
1834
1835 ptype_str = 'Port'
1836 ptype = Port
1837
1838 baseEnums = allEnums.copy()
1839 baseParams = allParams.copy()
1840
1841 def clear():
1842 global allEnums, allParams
1843
1844 allEnums = baseEnums.copy()
1845 allParams = baseParams.copy()
1846
1847 __all__ = ['Param', 'VectorParam',
1848 'Enum', 'Bool', 'String', 'Float',
1849 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1850 'Int32', 'UInt32', 'Int64', 'UInt64',
1851 'Counter', 'Addr', 'Tick', 'Percent',
1852 'TcpPort', 'UdpPort', 'EthernetAddr',
1853 'IpAddress', 'IpNetmask', 'IpWithPort',
1854 'MemorySize', 'MemorySize32',
1855 'Latency', 'Frequency', 'Clock', 'Voltage',
1856 'NetworkBandwidth', 'MemoryBandwidth',
1857 'AddrRange',
1858 'MaxAddr', 'MaxTick', 'AllMemory',
1859 'Time',
1860 'NextEthernetAddr', 'NULL',
1861 'MasterPort', 'SlavePort',
1862 'VectorMasterPort', 'VectorSlavePort']
1863
1864 import SimObject