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