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