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