params.py (13711:e796a82c5154) params.py (13714:35636064b7a1)
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
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 *
71from . import proxy
72from . import 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 def __index__(self):
610 return int(self.value)
611
612 @classmethod
613 def cxx_predecls(cls, code):
614 # most derived types require this, so we just do it here once
615 code('#include "base/types.hh"')
616
617 def getValue(self):
618 return long(self.value)
619
620class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
621class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
622
623class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
624class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
625class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
626class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
627class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
628class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
629class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
630class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
631
632class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
633class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
634class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
635class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
636
637class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
638
639class Cycles(CheckedInt):
640 cxx_type = 'Cycles'
641 size = 64
642 unsigned = True
643
644 def getValue(self):
645 from _m5.core import Cycles
646 return Cycles(self.value)
647
648 @classmethod
649 def cxx_ini_predecls(cls, code):
650 # Assume that base/str.hh will be included anyway
651 # code('#include "base/str.hh"')
652 pass
653
654 @classmethod
655 def cxx_ini_parse(cls, code, src, dest, ret):
656 code('uint64_t _temp;')
657 code('bool _ret = to_number(%s, _temp);' % src)
658 code('if (_ret)')
659 code(' %s = Cycles(_temp);' % dest)
660 code('%s _ret;' % ret)
661
662class Float(ParamValue, float):
663 cxx_type = 'double'
664 cmd_line_settable = True
665
666 def __init__(self, value):
667 if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
668 self.value = float(value)
669 else:
670 raise TypeError("Can't convert object of type %s to Float" \
671 % type(value).__name__)
672
673 def __call__(self, value):
674 self.__init__(value)
675 return value
676
677 def getValue(self):
678 return float(self.value)
679
680 def config_value(self):
681 return self
682
683 @classmethod
684 def cxx_ini_predecls(cls, code):
685 code('#include <sstream>')
686
687 @classmethod
688 def cxx_ini_parse(self, code, src, dest, ret):
689 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
690
691class MemorySize(CheckedInt):
692 cxx_type = 'uint64_t'
693 ex_str = '512MB'
694 size = 64
695 unsigned = True
696 def __init__(self, value):
697 if isinstance(value, MemorySize):
698 self.value = value.value
699 else:
700 self.value = convert.toMemorySize(value)
701 self._check()
702
703class MemorySize32(CheckedInt):
704 cxx_type = 'uint32_t'
705 ex_str = '512MB'
706 size = 32
707 unsigned = True
708 def __init__(self, value):
709 if isinstance(value, MemorySize):
710 self.value = value.value
711 else:
712 self.value = convert.toMemorySize(value)
713 self._check()
714
715class Addr(CheckedInt):
716 cxx_type = 'Addr'
717 size = 64
718 unsigned = True
719 def __init__(self, value):
720 if isinstance(value, Addr):
721 self.value = value.value
722 else:
723 try:
724 # Often addresses are referred to with sizes. Ex: A device
725 # base address is at "512MB". Use toMemorySize() to convert
726 # these into addresses. If the address is not specified with a
727 # "size", an exception will occur and numeric translation will
728 # proceed below.
729 self.value = convert.toMemorySize(value)
730 except (TypeError, ValueError):
731 # Convert number to string and use long() to do automatic
732 # base conversion (requires base=0 for auto-conversion)
733 self.value = long(str(value), base=0)
734
735 self._check()
736 def __add__(self, other):
737 if isinstance(other, Addr):
738 return self.value + other.value
739 else:
740 return self.value + other
741 def pretty_print(self, value):
742 try:
743 val = convert.toMemorySize(value)
744 except TypeError:
745 val = long(value)
746 return "0x%x" % long(val)
747
748class AddrRange(ParamValue):
749 cxx_type = 'AddrRange'
750
751 def __init__(self, *args, **kwargs):
752 # Disable interleaving and hashing by default
753 self.intlvHighBit = 0
754 self.xorHighBit = 0
755 self.intlvBits = 0
756 self.intlvMatch = 0
757
758 def handle_kwargs(self, kwargs):
759 # An address range needs to have an upper limit, specified
760 # either explicitly with an end, or as an offset using the
761 # size keyword.
762 if 'end' in kwargs:
763 self.end = Addr(kwargs.pop('end'))
764 elif 'size' in kwargs:
765 self.end = self.start + Addr(kwargs.pop('size')) - 1
766 else:
767 raise TypeError("Either end or size must be specified")
768
769 # Now on to the optional bit
770 if 'intlvHighBit' in kwargs:
771 self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
772 if 'xorHighBit' in kwargs:
773 self.xorHighBit = int(kwargs.pop('xorHighBit'))
774 if 'intlvBits' in kwargs:
775 self.intlvBits = int(kwargs.pop('intlvBits'))
776 if 'intlvMatch' in kwargs:
777 self.intlvMatch = int(kwargs.pop('intlvMatch'))
778
779 if len(args) == 0:
780 self.start = Addr(kwargs.pop('start'))
781 handle_kwargs(self, kwargs)
782
783 elif len(args) == 1:
784 if kwargs:
785 self.start = Addr(args[0])
786 handle_kwargs(self, kwargs)
787 elif isinstance(args[0], (list, tuple)):
788 self.start = Addr(args[0][0])
789 self.end = Addr(args[0][1])
790 else:
791 self.start = Addr(0)
792 self.end = Addr(args[0]) - 1
793
794 elif len(args) == 2:
795 self.start = Addr(args[0])
796 self.end = Addr(args[1])
797 else:
798 raise TypeError("Too many arguments specified")
799
800 if kwargs:
801 raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
802
803 def __str__(self):
804 return '%s:%s:%s:%s:%s:%s' \
805 % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\
806 self.intlvBits, self.intlvMatch)
807
808 def size(self):
809 # Divide the size by the size of the interleaving slice
810 return (long(self.end) - long(self.start) + 1) >> self.intlvBits
811
812 @classmethod
813 def cxx_predecls(cls, code):
814 Addr.cxx_predecls(code)
815 code('#include "base/addr_range.hh"')
816
817 @classmethod
818 def pybind_predecls(cls, code):
819 Addr.pybind_predecls(code)
820 code('#include "base/addr_range.hh"')
821
822 @classmethod
823 def cxx_ini_predecls(cls, code):
824 code('#include <sstream>')
825
826 @classmethod
827 def cxx_ini_parse(cls, code, src, dest, ret):
828 code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;')
829 code('uint64_t _intlvBits = 0, _intlvMatch = 0;')
830 code('char _sep;')
831 code('std::istringstream _stream(${src});')
832 code('_stream >> _start;')
833 code('_stream.get(_sep);')
834 code('_stream >> _end;')
835 code('if (!_stream.fail() && !_stream.eof()) {')
836 code(' _stream.get(_sep);')
837 code(' _stream >> _intlvHighBit;')
838 code(' _stream.get(_sep);')
839 code(' _stream >> _xorHighBit;')
840 code(' _stream.get(_sep);')
841 code(' _stream >> _intlvBits;')
842 code(' _stream.get(_sep);')
843 code(' _stream >> _intlvMatch;')
844 code('}')
845 code('bool _ret = !_stream.fail() &&'
846 '_stream.eof() && _sep == \':\';')
847 code('if (_ret)')
848 code(' ${dest} = AddrRange(_start, _end, _intlvHighBit, \
849 _xorHighBit, _intlvBits, _intlvMatch);')
850 code('${ret} _ret;')
851
852 def getValue(self):
853 # Go from the Python class to the wrapped C++ class
854 from _m5.range import AddrRange
855
856 return AddrRange(long(self.start), long(self.end),
857 int(self.intlvHighBit), int(self.xorHighBit),
858 int(self.intlvBits), int(self.intlvMatch))
859
860# Boolean parameter type. Python doesn't let you subclass bool, since
861# it doesn't want to let you create multiple instances of True and
862# False. Thus this is a little more complicated than String.
863class Bool(ParamValue):
864 cxx_type = 'bool'
865 cmd_line_settable = True
866
867 def __init__(self, value):
868 try:
869 self.value = convert.toBool(value)
870 except TypeError:
871 self.value = bool(value)
872
873 def __call__(self, value):
874 self.__init__(value)
875 return value
876
877 def getValue(self):
878 return bool(self.value)
879
880 def __str__(self):
881 return str(self.value)
882
883 # implement truth value testing for Bool parameters so that these params
884 # evaluate correctly during the python configuration phase
885 def __bool__(self):
886 return bool(self.value)
887
888 # Python 2.7 uses __nonzero__ instead of __bool__
889 __nonzero__ = __bool__
890
891 def ini_str(self):
892 if self.value:
893 return 'true'
894 return 'false'
895
896 def config_value(self):
897 return self.value
898
899 @classmethod
900 def cxx_ini_predecls(cls, code):
901 # Assume that base/str.hh will be included anyway
902 # code('#include "base/str.hh"')
903 pass
904
905 @classmethod
906 def cxx_ini_parse(cls, code, src, dest, ret):
907 code('%s to_bool(%s, %s);' % (ret, src, dest))
908
909def IncEthernetAddr(addr, val = 1):
910 bytes = [ int(x, 16) for x in addr.split(':') ]
911 bytes[5] += val
912 for i in (5, 4, 3, 2, 1):
913 val,rem = divmod(bytes[i], 256)
914 bytes[i] = rem
915 if val == 0:
916 break
917 bytes[i - 1] += val
918 assert(bytes[0] <= 255)
919 return ':'.join(map(lambda x: '%02x' % x, bytes))
920
921_NextEthernetAddr = "00:90:00:00:00:01"
922def NextEthernetAddr():
923 global _NextEthernetAddr
924
925 value = _NextEthernetAddr
926 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
927 return value
928
929class EthernetAddr(ParamValue):
930 cxx_type = 'Net::EthAddr'
931 ex_str = "00:90:00:00:00:01"
932 cmd_line_settable = True
933
934 @classmethod
935 def cxx_predecls(cls, code):
936 code('#include "base/inet.hh"')
937
938 def __init__(self, value):
939 if value == NextEthernetAddr:
940 self.value = value
941 return
942
943 if not isinstance(value, str):
944 raise TypeError("expected an ethernet address and didn't get one")
945
946 bytes = value.split(':')
947 if len(bytes) != 6:
948 raise TypeError('invalid ethernet address %s' % value)
949
950 for byte in bytes:
951 if not 0 <= int(byte, base=16) <= 0xff:
952 raise TypeError('invalid ethernet address %s' % value)
953
954 self.value = value
955
956 def __call__(self, value):
957 self.__init__(value)
958 return value
959
960 def unproxy(self, base):
961 if self.value == NextEthernetAddr:
962 return EthernetAddr(self.value())
963 return self
964
965 def getValue(self):
966 from _m5.net import EthAddr
967 return EthAddr(self.value)
968
969 def __str__(self):
970 return self.value
971
972 def ini_str(self):
973 return self.value
974
975 @classmethod
976 def cxx_ini_parse(self, code, src, dest, ret):
977 code('%s = Net::EthAddr(%s);' % (dest, src))
978 code('%s true;' % ret)
979
980# When initializing an IpAddress, pass in an existing IpAddress, a string of
981# the form "a.b.c.d", or an integer representing an IP.
982class IpAddress(ParamValue):
983 cxx_type = 'Net::IpAddress'
984 ex_str = "127.0.0.1"
985 cmd_line_settable = True
986
987 @classmethod
988 def cxx_predecls(cls, code):
989 code('#include "base/inet.hh"')
990
991 def __init__(self, value):
992 if isinstance(value, IpAddress):
993 self.ip = value.ip
994 else:
995 try:
996 self.ip = convert.toIpAddress(value)
997 except TypeError:
998 self.ip = long(value)
999 self.verifyIp()
1000
1001 def __call__(self, value):
1002 self.__init__(value)
1003 return value
1004
1005 def __str__(self):
1006 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)]
1007 return '%d.%d.%d.%d' % tuple(tup)
1008
1009 def __eq__(self, other):
1010 if isinstance(other, IpAddress):
1011 return self.ip == other.ip
1012 elif isinstance(other, str):
1013 try:
1014 return self.ip == convert.toIpAddress(other)
1015 except:
1016 return False
1017 else:
1018 return self.ip == other
1019
1020 def __ne__(self, other):
1021 return not (self == other)
1022
1023 def verifyIp(self):
1024 if self.ip < 0 or self.ip >= (1 << 32):
1025 raise TypeError("invalid ip address %#08x" % self.ip)
1026
1027 def getValue(self):
1028 from _m5.net import IpAddress
1029 return IpAddress(self.ip)
1030
1031# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
1032# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
1033# positional or keyword arguments.
1034class IpNetmask(IpAddress):
1035 cxx_type = 'Net::IpNetmask'
1036 ex_str = "127.0.0.0/24"
1037 cmd_line_settable = True
1038
1039 @classmethod
1040 def cxx_predecls(cls, code):
1041 code('#include "base/inet.hh"')
1042
1043 def __init__(self, *args, **kwargs):
1044 def handle_kwarg(self, kwargs, key, elseVal = None):
1045 if key in kwargs:
1046 setattr(self, key, kwargs.pop(key))
1047 elif elseVal:
1048 setattr(self, key, elseVal)
1049 else:
1050 raise TypeError("No value set for %s" % key)
1051
1052 if len(args) == 0:
1053 handle_kwarg(self, kwargs, 'ip')
1054 handle_kwarg(self, kwargs, 'netmask')
1055
1056 elif len(args) == 1:
1057 if kwargs:
1058 if not 'ip' in kwargs and not 'netmask' in kwargs:
1059 raise TypeError("Invalid arguments")
1060 handle_kwarg(self, kwargs, 'ip', args[0])
1061 handle_kwarg(self, kwargs, 'netmask', args[0])
1062 elif isinstance(args[0], IpNetmask):
1063 self.ip = args[0].ip
1064 self.netmask = args[0].netmask
1065 else:
1066 (self.ip, self.netmask) = convert.toIpNetmask(args[0])
1067
1068 elif len(args) == 2:
1069 self.ip = args[0]
1070 self.netmask = args[1]
1071 else:
1072 raise TypeError("Too many arguments specified")
1073
1074 if kwargs:
1075 raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1076
1077 self.verify()
1078
1079 def __call__(self, value):
1080 self.__init__(value)
1081 return value
1082
1083 def __str__(self):
1084 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
1085
1086 def __eq__(self, other):
1087 if isinstance(other, IpNetmask):
1088 return self.ip == other.ip and self.netmask == other.netmask
1089 elif isinstance(other, str):
1090 try:
1091 return (self.ip, self.netmask) == convert.toIpNetmask(other)
1092 except:
1093 return False
1094 else:
1095 return False
1096
1097 def verify(self):
1098 self.verifyIp()
1099 if self.netmask < 0 or self.netmask > 32:
1100 raise TypeError("invalid netmask %d" % netmask)
1101
1102 def getValue(self):
1103 from _m5.net import IpNetmask
1104 return IpNetmask(self.ip, self.netmask)
1105
1106# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1107# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1108class IpWithPort(IpAddress):
1109 cxx_type = 'Net::IpWithPort'
1110 ex_str = "127.0.0.1:80"
1111 cmd_line_settable = True
1112
1113 @classmethod
1114 def cxx_predecls(cls, code):
1115 code('#include "base/inet.hh"')
1116
1117 def __init__(self, *args, **kwargs):
1118 def handle_kwarg(self, kwargs, key, elseVal = None):
1119 if key in kwargs:
1120 setattr(self, key, kwargs.pop(key))
1121 elif elseVal:
1122 setattr(self, key, elseVal)
1123 else:
1124 raise TypeError("No value set for %s" % key)
1125
1126 if len(args) == 0:
1127 handle_kwarg(self, kwargs, 'ip')
1128 handle_kwarg(self, kwargs, 'port')
1129
1130 elif len(args) == 1:
1131 if kwargs:
1132 if not 'ip' in kwargs and not 'port' in kwargs:
1133 raise TypeError("Invalid arguments")
1134 handle_kwarg(self, kwargs, 'ip', args[0])
1135 handle_kwarg(self, kwargs, 'port', args[0])
1136 elif isinstance(args[0], IpWithPort):
1137 self.ip = args[0].ip
1138 self.port = args[0].port
1139 else:
1140 (self.ip, self.port) = convert.toIpWithPort(args[0])
1141
1142 elif len(args) == 2:
1143 self.ip = args[0]
1144 self.port = args[1]
1145 else:
1146 raise TypeError("Too many arguments specified")
1147
1148 if kwargs:
1149 raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1150
1151 self.verify()
1152
1153 def __call__(self, value):
1154 self.__init__(value)
1155 return value
1156
1157 def __str__(self):
1158 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1159
1160 def __eq__(self, other):
1161 if isinstance(other, IpWithPort):
1162 return self.ip == other.ip and self.port == other.port
1163 elif isinstance(other, str):
1164 try:
1165 return (self.ip, self.port) == convert.toIpWithPort(other)
1166 except:
1167 return False
1168 else:
1169 return False
1170
1171 def verify(self):
1172 self.verifyIp()
1173 if self.port < 0 or self.port > 0xffff:
1174 raise TypeError("invalid port %d" % self.port)
1175
1176 def getValue(self):
1177 from _m5.net import IpWithPort
1178 return IpWithPort(self.ip, self.port)
1179
1180time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1181 "%a %b %d %H:%M:%S %Y",
1182 "%Y/%m/%d %H:%M:%S",
1183 "%Y/%m/%d %H:%M",
1184 "%Y/%m/%d",
1185 "%m/%d/%Y %H:%M:%S",
1186 "%m/%d/%Y %H:%M",
1187 "%m/%d/%Y",
1188 "%m/%d/%y %H:%M:%S",
1189 "%m/%d/%y %H:%M",
1190 "%m/%d/%y"]
1191
1192
1193def parse_time(value):
1194 from time import gmtime, strptime, struct_time, time
1195 from datetime import datetime, date
1196
1197 if isinstance(value, struct_time):
1198 return value
1199
1200 if isinstance(value, (int, long)):
1201 return gmtime(value)
1202
1203 if isinstance(value, (datetime, date)):
1204 return value.timetuple()
1205
1206 if isinstance(value, str):
1207 if value in ('Now', 'Today'):
1208 return time.gmtime(time.time())
1209
1210 for format in time_formats:
1211 try:
1212 return strptime(value, format)
1213 except ValueError:
1214 pass
1215
1216 raise ValueError("Could not parse '%s' as a time" % value)
1217
1218class Time(ParamValue):
1219 cxx_type = 'tm'
1220
1221 @classmethod
1222 def cxx_predecls(cls, code):
1223 code('#include <time.h>')
1224
1225 def __init__(self, value):
1226 self.value = parse_time(value)
1227
1228 def __call__(self, value):
1229 self.__init__(value)
1230 return value
1231
1232 def getValue(self):
1233 from _m5.core import tm
1234 import calendar
1235
1236 return tm.gmtime(calendar.timegm(self.value))
1237
1238 def __str__(self):
1239 return time.asctime(self.value)
1240
1241 def ini_str(self):
1242 return str(self)
1243
1244 def get_config_as_dict(self):
1245 assert false
1246 return str(self)
1247
1248 @classmethod
1249 def cxx_ini_predecls(cls, code):
1250 code('#include <time.h>')
1251
1252 @classmethod
1253 def cxx_ini_parse(cls, code, src, dest, ret):
1254 code('char *_parse_ret = strptime((${src}).c_str(),')
1255 code(' "%a %b %d %H:%M:%S %Y", &(${dest}));')
1256 code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1257
1258# Enumerated types are a little more complex. The user specifies the
1259# type as Enum(foo) where foo is either a list or dictionary of
1260# alternatives (typically strings, but not necessarily so). (In the
1261# long run, the integer value of the parameter will be the list index
1262# or the corresponding dictionary value. For now, since we only check
1263# that the alternative is valid and then spit it into a .ini file,
1264# there's not much point in using the dictionary.)
1265
1266# What Enum() must do is generate a new type encapsulating the
1267# provided list/dictionary so that specific values of the parameter
1268# can be instances of that type. We define two hidden internal
1269# classes (_ListEnum and _DictEnum) to serve as base classes, then
1270# derive the new type from the appropriate base class on the fly.
1271
1272allEnums = {}
1273# Metaclass for Enum types
1274class MetaEnum(MetaParamValue):
1275 def __new__(mcls, name, bases, dict):
1276 assert name not in allEnums
1277
1278 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1279 allEnums[name] = cls
1280 return cls
1281
1282 def __init__(cls, name, bases, init_dict):
1283 if 'map' in init_dict:
1284 if not isinstance(cls.map, dict):
1285 raise TypeError("Enum-derived class attribute 'map' " \
1286 "must be of type dict")
1287 # build list of value strings from map
1288 cls.vals = list(cls.map.keys())
1289 cls.vals.sort()
1290 elif 'vals' in init_dict:
1291 if not isinstance(cls.vals, list):
1292 raise TypeError("Enum-derived class attribute 'vals' " \
1293 "must be of type list")
1294 # build string->value map from vals sequence
1295 cls.map = {}
1296 for idx,val in enumerate(cls.vals):
1297 cls.map[val] = idx
1298 else:
1299 raise TypeError("Enum-derived class must define "\
1300 "attribute 'map' or 'vals'")
1301
1302 if cls.is_class:
1303 cls.cxx_type = '%s' % name
1304 else:
1305 cls.cxx_type = 'Enums::%s' % name
1306
1307 super(MetaEnum, cls).__init__(name, bases, init_dict)
1308
1309 # Generate C++ class declaration for this enum type.
1310 # Note that we wrap the enum in a class/struct to act as a namespace,
1311 # so that the enum strings can be brief w/o worrying about collisions.
1312 def cxx_decl(cls, code):
1313 wrapper_name = cls.wrapper_name
1314 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1315 name = cls.__name__ if cls.enum_name is None else cls.enum_name
1316 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1317
1318 code('''\
1319#ifndef $idem_macro
1320#define $idem_macro
1321
1322''')
1323 if cls.is_class:
1324 code('''\
1325enum class $name {
1326''')
1327 else:
1328 code('''\
1329$wrapper $wrapper_name {
1330 enum $name {
1331''')
1332 code.indent(1)
1333 code.indent(1)
1334 for val in cls.vals:
1335 code('$val = ${{cls.map[val]}},')
1336 code('Num_$name = ${{len(cls.vals)}}')
1337 code.dedent(1)
1338 code('};')
1339
1340 if cls.is_class:
1341 code('''\
1342extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
1343''')
1344 elif cls.wrapper_is_struct:
1345 code('static const char *${name}Strings[Num_${name}];')
1346 else:
1347 code('extern const char *${name}Strings[Num_${name}];')
1348
1349 if not cls.is_class:
1350 code.dedent(1)
1351 code('};')
1352
1353 code()
1354 code('#endif // $idem_macro')
1355
1356 def cxx_def(cls, code):
1357 wrapper_name = cls.wrapper_name
1358 file_name = cls.__name__
1359 name = cls.__name__ if cls.enum_name is None else cls.enum_name
1360
1361 code('#include "enums/$file_name.hh"')
1362 if cls.wrapper_is_struct:
1363 code('const char *${wrapper_name}::${name}Strings'
1364 '[Num_${name}] =')
1365 else:
1366 if cls.is_class:
1367 code('''\
1368const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
1369''')
1370 else:
1371 code('namespace Enums {')
1372 code.indent(1)
1373 code('const char *${name}Strings[Num_${name}] =')
1374
1375 code('{')
1376 code.indent(1)
1377 for val in cls.vals:
1378 code('"$val",')
1379 code.dedent(1)
1380 code('};')
1381
1382 if not cls.wrapper_is_struct and not cls.is_class:
1383 code.dedent(1)
1384 code('} // namespace $wrapper_name')
1385
1386
1387 def pybind_def(cls, code):
1388 name = cls.__name__
1389 enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
1390 wrapper_name = enum_name if cls.is_class else cls.wrapper_name
1391
1392 code('''#include "pybind11/pybind11.h"
1393#include "pybind11/stl.h"
1394
1395#include <sim/init.hh>
1396
1397namespace py = pybind11;
1398
1399static void
1400module_init(py::module &m_internal)
1401{
1402 py::module m = m_internal.def_submodule("enum_${name}");
1403
1404''')
1405 if cls.is_class:
1406 code('py::enum_<${enum_name}>(m, "enum_${name}")')
1407 else:
1408 code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
1409
1410 code.indent()
1411 code.indent()
1412 for val in cls.vals:
1413 code('.value("${val}", ${wrapper_name}::${val})')
1414 code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1415 code('.export_values()')
1416 code(';')
1417 code.dedent()
1418
1419 code('}')
1420 code.dedent()
1421 code()
1422 code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1423
1424
1425# Base class for enum types.
1426class Enum(ParamValue):
1427 __metaclass__ = MetaEnum
1428 vals = []
1429 cmd_line_settable = True
1430
1431 # The name of the wrapping namespace or struct
1432 wrapper_name = 'Enums'
1433
1434 # If true, the enum is wrapped in a struct rather than a namespace
1435 wrapper_is_struct = False
1436
1437 is_class = False
1438
1439 # If not None, use this as the enum name rather than this class name
1440 enum_name = None
1441
1442 def __init__(self, value):
1443 if value not in self.map:
1444 raise TypeError("Enum param got bad value '%s' (not in %s)" \
1445 % (value, self.vals))
1446 self.value = value
1447
1448 def __call__(self, value):
1449 self.__init__(value)
1450 return value
1451
1452 @classmethod
1453 def cxx_predecls(cls, code):
1454 code('#include "enums/$0.hh"', cls.__name__)
1455
1456 @classmethod
1457 def cxx_ini_parse(cls, code, src, dest, ret):
1458 code('if (false) {')
1459 for elem_name in cls.map.keys():
1460 code('} else if (%s == "%s") {' % (src, elem_name))
1461 code.indent()
1462 code('%s = Enums::%s;' % (dest, elem_name))
1463 code('%s true;' % ret)
1464 code.dedent()
1465 code('} else {')
1466 code(' %s false;' % ret)
1467 code('}')
1468
1469 def getValue(self):
1470 import m5.internal.params
1471 e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
1472 return e(self.map[self.value])
1473
1474 def __str__(self):
1475 return self.value
1476
1477# This param will generate a scoped c++ enum and its python bindings.
1478class ScopedEnum(Enum):
1479 __metaclass__ = MetaEnum
1480 vals = []
1481 cmd_line_settable = True
1482
1483 # The name of the wrapping namespace or struct
1484 wrapper_name = None
1485
1486 # If true, the enum is wrapped in a struct rather than a namespace
1487 wrapper_is_struct = False
1488
1489 # If true, the generated enum is a scoped enum
1490 is_class = True
1491
1492 # If not None, use this as the enum name rather than this class name
1493 enum_name = None
1494
1495# how big does a rounding error need to be before we warn about it?
1496frequency_tolerance = 0.001 # 0.1%
1497
1498class TickParamValue(NumericParamValue):
1499 cxx_type = 'Tick'
1500 ex_str = "1MHz"
1501 cmd_line_settable = True
1502
1503 @classmethod
1504 def cxx_predecls(cls, code):
1505 code('#include "base/types.hh"')
1506
1507 def __call__(self, value):
1508 self.__init__(value)
1509 return value
1510
1511 def getValue(self):
1512 return long(self.value)
1513
1514 @classmethod
1515 def cxx_ini_predecls(cls, code):
1516 code('#include <sstream>')
1517
1518 # Ticks are expressed in seconds in JSON files and in plain
1519 # Ticks in .ini files. Switch based on a config flag
1520 @classmethod
1521 def cxx_ini_parse(self, code, src, dest, ret):
1522 code('${ret} to_number(${src}, ${dest});')
1523
1524class Latency(TickParamValue):
1525 ex_str = "100ns"
1526
1527 def __init__(self, value):
1528 if isinstance(value, (Latency, Clock)):
1529 self.ticks = value.ticks
1530 self.value = value.value
1531 elif isinstance(value, Frequency):
1532 self.ticks = value.ticks
1533 self.value = 1.0 / value.value
1534 elif value.endswith('t'):
1535 self.ticks = True
1536 self.value = int(value[:-1])
1537 else:
1538 self.ticks = False
1539 self.value = convert.toLatency(value)
1540
1541 def __call__(self, value):
1542 self.__init__(value)
1543 return value
1544
1545 def __getattr__(self, attr):
1546 if attr in ('latency', 'period'):
1547 return self
1548 if attr == 'frequency':
1549 return Frequency(self)
1550 raise AttributeError("Latency object has no attribute '%s'" % attr)
1551
1552 def getValue(self):
1553 if self.ticks or self.value == 0:
1554 value = self.value
1555 else:
1556 value = ticks.fromSeconds(self.value)
1557 return long(value)
1558
1559 def config_value(self):
1560 return self.getValue()
1561
1562 # convert latency to ticks
1563 def ini_str(self):
1564 return '%d' % self.getValue()
1565
1566class Frequency(TickParamValue):
1567 ex_str = "1GHz"
1568
1569 def __init__(self, value):
1570 if isinstance(value, (Latency, Clock)):
1571 if value.value == 0:
1572 self.value = 0
1573 else:
1574 self.value = 1.0 / value.value
1575 self.ticks = value.ticks
1576 elif isinstance(value, Frequency):
1577 self.value = value.value
1578 self.ticks = value.ticks
1579 else:
1580 self.ticks = False
1581 self.value = convert.toFrequency(value)
1582
1583 def __call__(self, value):
1584 self.__init__(value)
1585 return value
1586
1587 def __getattr__(self, attr):
1588 if attr == 'frequency':
1589 return self
1590 if attr in ('latency', 'period'):
1591 return Latency(self)
1592 raise AttributeError("Frequency object has no attribute '%s'" % attr)
1593
1594 # convert latency to ticks
1595 def getValue(self):
1596 if self.ticks or self.value == 0:
1597 value = self.value
1598 else:
1599 value = ticks.fromSeconds(1.0 / self.value)
1600 return long(value)
1601
1602 def config_value(self):
1603 return self.getValue()
1604
1605 def ini_str(self):
1606 return '%d' % self.getValue()
1607
1608# A generic Frequency and/or Latency value. Value is stored as a
1609# latency, just like Latency and Frequency.
1610class Clock(TickParamValue):
1611 def __init__(self, value):
1612 if isinstance(value, (Latency, Clock)):
1613 self.ticks = value.ticks
1614 self.value = value.value
1615 elif isinstance(value, Frequency):
1616 self.ticks = value.ticks
1617 self.value = 1.0 / value.value
1618 elif value.endswith('t'):
1619 self.ticks = True
1620 self.value = int(value[:-1])
1621 else:
1622 self.ticks = False
1623 self.value = convert.anyToLatency(value)
1624
1625 def __call__(self, value):
1626 self.__init__(value)
1627 return value
1628
1629 def __str__(self):
1630 return "%s" % Latency(self)
1631
1632 def __getattr__(self, attr):
1633 if attr == 'frequency':
1634 return Frequency(self)
1635 if attr in ('latency', 'period'):
1636 return Latency(self)
1637 raise AttributeError("Frequency object has no attribute '%s'" % attr)
1638
1639 def getValue(self):
1640 return self.period.getValue()
1641
1642 def config_value(self):
1643 return self.period.config_value()
1644
1645 def ini_str(self):
1646 return self.period.ini_str()
1647
1648class Voltage(Float):
1649 ex_str = "1V"
1650
1651 def __new__(cls, value):
1652 value = convert.toVoltage(value)
1653 return super(cls, Voltage).__new__(cls, value)
1654
1655 def __init__(self, value):
1656 value = convert.toVoltage(value)
1657 super(Voltage, self).__init__(value)
1658
1659class Current(Float):
1660 ex_str = "1mA"
1661
1662 def __new__(cls, value):
1663 value = convert.toCurrent(value)
1664 return super(cls, Current).__new__(cls, value)
1665
1666 def __init__(self, value):
1667 value = convert.toCurrent(value)
1668 super(Current, self).__init__(value)
1669
1670class Energy(Float):
1671 ex_str = "1pJ"
1672
1673 def __new__(cls, value):
1674 value = convert.toEnergy(value)
1675 return super(cls, Energy).__new__(cls, value)
1676
1677 def __init__(self, value):
1678 value = convert.toEnergy(value)
1679 super(Energy, self).__init__(value)
1680
1681class NetworkBandwidth(float,ParamValue):
1682 cxx_type = 'float'
1683 ex_str = "1Gbps"
1684 cmd_line_settable = True
1685
1686 def __new__(cls, value):
1687 # convert to bits per second
1688 val = convert.toNetworkBandwidth(value)
1689 return super(cls, NetworkBandwidth).__new__(cls, val)
1690
1691 def __str__(self):
1692 return str(self.val)
1693
1694 def __call__(self, value):
1695 val = convert.toNetworkBandwidth(value)
1696 self.__init__(val)
1697 return value
1698
1699 def getValue(self):
1700 # convert to seconds per byte
1701 value = 8.0 / float(self)
1702 # convert to ticks per byte
1703 value = ticks.fromSeconds(value)
1704 return float(value)
1705
1706 def ini_str(self):
1707 return '%f' % self.getValue()
1708
1709 def config_value(self):
1710 return '%f' % self.getValue()
1711
1712 @classmethod
1713 def cxx_ini_predecls(cls, code):
1714 code('#include <sstream>')
1715
1716 @classmethod
1717 def cxx_ini_parse(self, code, src, dest, ret):
1718 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1719
1720class MemoryBandwidth(float,ParamValue):
1721 cxx_type = 'float'
1722 ex_str = "1GB/s"
1723 cmd_line_settable = True
1724
1725 def __new__(cls, value):
1726 # convert to bytes per second
1727 val = convert.toMemoryBandwidth(value)
1728 return super(cls, MemoryBandwidth).__new__(cls, val)
1729
1730 def __call__(self, value):
1731 val = convert.toMemoryBandwidth(value)
1732 self.__init__(val)
1733 return value
1734
1735 def getValue(self):
1736 # convert to seconds per byte
1737 value = float(self)
1738 if value:
1739 value = 1.0 / float(self)
1740 # convert to ticks per byte
1741 value = ticks.fromSeconds(value)
1742 return float(value)
1743
1744 def ini_str(self):
1745 return '%f' % self.getValue()
1746
1747 def config_value(self):
1748 return '%f' % self.getValue()
1749
1750 @classmethod
1751 def cxx_ini_predecls(cls, code):
1752 code('#include <sstream>')
1753
1754 @classmethod
1755 def cxx_ini_parse(self, code, src, dest, ret):
1756 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1757
1758#
1759# "Constants"... handy aliases for various values.
1760#
1761
1762# Special class for NULL pointers. Note the special check in
1763# make_param_value() above that lets these be assigned where a
1764# SimObject is required.
1765# only one copy of a particular node
1766class NullSimObject(object):
1767 __metaclass__ = Singleton
1768 _name = 'Null'
1769
1770 def __call__(cls):
1771 return cls
1772
1773 def _instantiate(self, parent = None, path = ''):
1774 pass
1775
1776 def ini_str(self):
1777 return 'Null'
1778
1779 def unproxy(self, base):
1780 return self
1781
1782 def set_path(self, parent, name):
1783 pass
1784
1785 def set_parent(self, parent, name):
1786 pass
1787
1788 def clear_parent(self, old_parent):
1789 pass
1790
1791 def descendants(self):
1792 return
1793 yield None
1794
1795 def get_config_as_dict(self):
1796 return {}
1797
1798 def __str__(self):
1799 return self._name
1800
1801 def config_value(self):
1802 return None
1803
1804 def getValue(self):
1805 return None
1806
1807# The only instance you'll ever need...
1808NULL = NullSimObject()
1809
1810def isNullPointer(value):
1811 return isinstance(value, NullSimObject)
1812
1813# Some memory range specifications use this as a default upper bound.
1814MaxAddr = Addr.max
1815MaxTick = Tick.max
1816AllMemory = AddrRange(0, MaxAddr)
1817
1818
1819#####################################################################
1820#
1821# Port objects
1822#
1823# Ports are used to interconnect objects in the memory system.
1824#
1825#####################################################################
1826
1827# Port reference: encapsulates a reference to a particular port on a
1828# particular SimObject.
1829class PortRef(object):
1830 def __init__(self, simobj, name, role):
1831 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1832 self.simobj = simobj
1833 self.name = name
1834 self.role = role
1835 self.peer = None # not associated with another port yet
1836 self.ccConnected = False # C++ port connection done?
1837 self.index = -1 # always -1 for non-vector ports
1838
1839 def __str__(self):
1840 return '%s.%s' % (self.simobj, self.name)
1841
1842 def __len__(self):
1843 # Return the number of connected ports, i.e. 0 is we have no
1844 # peer and 1 if we do.
1845 return int(self.peer != None)
1846
1847 # for config.ini, print peer's name (not ours)
1848 def ini_str(self):
1849 return str(self.peer)
1850
1851 # for config.json
1852 def get_config_as_dict(self):
1853 return {'role' : self.role, 'peer' : str(self.peer)}
1854
1855 def __getattr__(self, attr):
1856 if attr == 'peerObj':
1857 # shorthand for proxies
1858 return self.peer.simobj
1859 raise AttributeError("'%s' object has no attribute '%s'" % \
1860 (self.__class__.__name__, attr))
1861
1862 # Full connection is symmetric (both ways). Called via
1863 # SimObject.__setattr__ as a result of a port assignment, e.g.,
1864 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1865 # e.g., "obj1.portA[3] = obj2.portB".
1866 def connect(self, other):
1867 if isinstance(other, VectorPortRef):
1868 # reference to plain VectorPort is implicit append
1869 other = other._get_next()
1870 if self.peer and not proxy.isproxy(self.peer):
1871 fatal("Port %s is already connected to %s, cannot connect %s\n",
1872 self, self.peer, other);
1873 self.peer = other
1874 if proxy.isproxy(other):
1875 other.set_param_desc(PortParamDesc())
1876 elif isinstance(other, PortRef):
1877 if other.peer is not self:
1878 other.connect(self)
1879 else:
1880 raise TypeError("assigning non-port reference '%s' to port '%s'" \
1881 % (other, self))
1882
1883 # Allow a master/slave port pair to be spliced between
1884 # a port and its connected peer. Useful operation for connecting
1885 # instrumentation structures into a system when it is necessary
1886 # to connect the instrumentation after the full system has been
1887 # constructed.
1888 def splice(self, new_master_peer, new_slave_peer):
1889 if not self.peer or proxy.isproxy(self.peer):
1890 fatal("Port %s not connected, cannot splice in new peers\n", self)
1891
1892 if not isinstance(new_master_peer, PortRef) or \
1893 not isinstance(new_slave_peer, PortRef):
1894 raise TypeError(
1895 "Splicing non-port references '%s','%s' to port '%s'" % \
1896 (new_master_peer, new_slave_peer, self))
1897
1898 old_peer = self.peer
1899 if self.role == 'SLAVE':
1900 self.peer = new_master_peer
1901 old_peer.peer = new_slave_peer
1902 new_master_peer.connect(self)
1903 new_slave_peer.connect(old_peer)
1904 elif self.role == 'MASTER':
1905 self.peer = new_slave_peer
1906 old_peer.peer = new_master_peer
1907 new_slave_peer.connect(self)
1908 new_master_peer.connect(old_peer)
1909 else:
1910 panic("Port %s has unknown role, "+\
1911 "cannot splice in new peers\n", self)
1912
1913 def clone(self, simobj, memo):
1914 if self in memo:
1915 return memo[self]
1916 newRef = copy.copy(self)
1917 memo[self] = newRef
1918 newRef.simobj = simobj
1919 assert(isSimObject(newRef.simobj))
1920 if self.peer and not proxy.isproxy(self.peer):
1921 peerObj = self.peer.simobj(_memo=memo)
1922 newRef.peer = self.peer.clone(peerObj, memo)
1923 assert(not isinstance(newRef.peer, VectorPortRef))
1924 return newRef
1925
1926 def unproxy(self, simobj):
1927 assert(simobj is self.simobj)
1928 if proxy.isproxy(self.peer):
1929 try:
1930 realPeer = self.peer.unproxy(self.simobj)
1931 except:
1932 print("Error in unproxying port '%s' of %s" %
1933 (self.name, self.simobj.path()))
1934 raise
1935 self.connect(realPeer)
1936
1937 # Call C++ to create corresponding port connection between C++ objects
1938 def ccConnect(self):
1939 from _m5.pyobject import connectPorts
1940
1941 if self.ccConnected: # already done this
1942 return
1943
1944 peer = self.peer
1945 if not self.peer: # nothing to connect to
1946 return
1947
1948 # check that we connect a master to a slave
1949 if self.role == peer.role:
1950 raise TypeError(
1951 "cannot connect '%s' and '%s' due to identical role '%s'" % \
1952 (peer, self, self.role))
1953
1954 if self.role == 'SLAVE':
1955 # do nothing and let the master take care of it
1956 return
1957
1958 try:
1959 # self is always the master and peer the slave
1960 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1961 peer.simobj.getCCObject(), peer.name, peer.index)
1962 except:
1963 print("Error connecting port %s.%s to %s.%s" %
1964 (self.simobj.path(), self.name,
1965 peer.simobj.path(), peer.name))
1966 raise
1967 self.ccConnected = True
1968 peer.ccConnected = True
1969
1970# A reference to an individual element of a VectorPort... much like a
1971# PortRef, but has an index.
1972class VectorPortElementRef(PortRef):
1973 def __init__(self, simobj, name, role, index):
1974 PortRef.__init__(self, simobj, name, role)
1975 self.index = index
1976
1977 def __str__(self):
1978 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1979
1980# A reference to a complete vector-valued port (not just a single element).
1981# Can be indexed to retrieve individual VectorPortElementRef instances.
1982class VectorPortRef(object):
1983 def __init__(self, simobj, name, role):
1984 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1985 self.simobj = simobj
1986 self.name = name
1987 self.role = role
1988 self.elements = []
1989
1990 def __str__(self):
1991 return '%s.%s[:]' % (self.simobj, self.name)
1992
1993 def __len__(self):
1994 # Return the number of connected peers, corresponding the the
1995 # length of the elements.
1996 return len(self.elements)
1997
1998 # for config.ini, print peer's name (not ours)
1999 def ini_str(self):
2000 return ' '.join([el.ini_str() for el in self.elements])
2001
2002 # for config.json
2003 def get_config_as_dict(self):
2004 return {'role' : self.role,
2005 'peer' : [el.ini_str() for el in self.elements]}
2006
2007 def __getitem__(self, key):
2008 if not isinstance(key, int):
2009 raise TypeError("VectorPort index must be integer")
2010 if key >= len(self.elements):
2011 # need to extend list
2012 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
2013 for i in range(len(self.elements), key+1)]
2014 self.elements.extend(ext)
2015 return self.elements[key]
2016
2017 def _get_next(self):
2018 return self[len(self.elements)]
2019
2020 def __setitem__(self, key, value):
2021 if not isinstance(key, int):
2022 raise TypeError("VectorPort index must be integer")
2023 self[key].connect(value)
2024
2025 def connect(self, other):
2026 if isinstance(other, (list, tuple)):
2027 # Assign list of port refs to vector port.
2028 # For now, append them... not sure if that's the right semantics
2029 # or if it should replace the current vector.
2030 for ref in other:
2031 self._get_next().connect(ref)
2032 else:
2033 # scalar assignment to plain VectorPort is implicit append
2034 self._get_next().connect(other)
2035
2036 def clone(self, simobj, memo):
2037 if self in memo:
2038 return memo[self]
2039 newRef = copy.copy(self)
2040 memo[self] = newRef
2041 newRef.simobj = simobj
2042 assert(isSimObject(newRef.simobj))
2043 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
2044 return newRef
2045
2046 def unproxy(self, simobj):
2047 [el.unproxy(simobj) for el in self.elements]
2048
2049 def ccConnect(self):
2050 [el.ccConnect() for el in self.elements]
2051
2052# Port description object. Like a ParamDesc object, this represents a
2053# logical port in the SimObject class, not a particular port on a
2054# SimObject instance. The latter are represented by PortRef objects.
2055class Port(object):
2056 # Generate a PortRef for this port on the given SimObject with the
2057 # given name
2058 def makeRef(self, simobj):
2059 return PortRef(simobj, self.name, self.role)
2060
2061 # Connect an instance of this port (on the given SimObject with
2062 # the given name) with the port described by the supplied PortRef
2063 def connect(self, simobj, ref):
2064 self.makeRef(simobj).connect(ref)
2065
2066 # No need for any pre-declarations at the moment as we merely rely
2067 # on an unsigned int.
2068 def cxx_predecls(self, code):
2069 pass
2070
2071 def pybind_predecls(self, code):
2072 cls.cxx_predecls(self, code)
2073
2074 # Declare an unsigned int with the same name as the port, that
2075 # will eventually hold the number of connected ports (and thus the
2076 # number of elements for a VectorPort).
2077 def cxx_decl(self, code):
2078 code('unsigned int port_${{self.name}}_connection_count;')
2079
2080class MasterPort(Port):
2081 # MasterPort("description")
2082 def __init__(self, *args):
2083 if len(args) == 1:
2084 self.desc = args[0]
2085 self.role = 'MASTER'
2086 else:
2087 raise TypeError('wrong number of arguments')
2088
2089class SlavePort(Port):
2090 # SlavePort("description")
2091 def __init__(self, *args):
2092 if len(args) == 1:
2093 self.desc = args[0]
2094 self.role = 'SLAVE'
2095 else:
2096 raise TypeError('wrong number of arguments')
2097
2098# VectorPort description object. Like Port, but represents a vector
2099# of connections (e.g., as on a XBar).
2100class VectorPort(Port):
2101 def __init__(self, *args):
2102 self.isVec = True
2103
2104 def makeRef(self, simobj):
2105 return VectorPortRef(simobj, self.name, self.role)
2106
2107class VectorMasterPort(VectorPort):
2108 # VectorMasterPort("description")
2109 def __init__(self, *args):
2110 if len(args) == 1:
2111 self.desc = args[0]
2112 self.role = 'MASTER'
2113 VectorPort.__init__(self, *args)
2114 else:
2115 raise TypeError('wrong number of arguments')
2116
2117class VectorSlavePort(VectorPort):
2118 # VectorSlavePort("description")
2119 def __init__(self, *args):
2120 if len(args) == 1:
2121 self.desc = args[0]
2122 self.role = 'SLAVE'
2123 VectorPort.__init__(self, *args)
2124 else:
2125 raise TypeError('wrong number of arguments')
2126
2127# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2128# proxy objects (via set_param_desc()) so that proxy error messages
2129# make sense.
2130class PortParamDesc(object):
2131 __metaclass__ = Singleton
2132
2133 ptype_str = 'Port'
2134 ptype = Port
2135
2136baseEnums = allEnums.copy()
2137baseParams = allParams.copy()
2138
2139def clear():
2140 global allEnums, allParams
2141
2142 allEnums = baseEnums.copy()
2143 allParams = baseParams.copy()
2144
2145__all__ = ['Param', 'VectorParam',
2146 'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
2147 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2148 'Int32', 'UInt32', 'Int64', 'UInt64',
2149 'Counter', 'Addr', 'Tick', 'Percent',
2150 'TcpPort', 'UdpPort', 'EthernetAddr',
2151 'IpAddress', 'IpNetmask', 'IpWithPort',
2152 'MemorySize', 'MemorySize32',
2153 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2154 'NetworkBandwidth', 'MemoryBandwidth',
2155 'AddrRange',
2156 'MaxAddr', 'MaxTick', 'AllMemory',
2157 'Time',
2158 'NextEthernetAddr', 'NULL',
2159 'MasterPort', 'SlavePort',
2160 'VectorMasterPort', 'VectorSlavePort']
2161
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 def __index__(self):
610 return int(self.value)
611
612 @classmethod
613 def cxx_predecls(cls, code):
614 # most derived types require this, so we just do it here once
615 code('#include "base/types.hh"')
616
617 def getValue(self):
618 return long(self.value)
619
620class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
621class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
622
623class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
624class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
625class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
626class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
627class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
628class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
629class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
630class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
631
632class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
633class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
634class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
635class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
636
637class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
638
639class Cycles(CheckedInt):
640 cxx_type = 'Cycles'
641 size = 64
642 unsigned = True
643
644 def getValue(self):
645 from _m5.core import Cycles
646 return Cycles(self.value)
647
648 @classmethod
649 def cxx_ini_predecls(cls, code):
650 # Assume that base/str.hh will be included anyway
651 # code('#include "base/str.hh"')
652 pass
653
654 @classmethod
655 def cxx_ini_parse(cls, code, src, dest, ret):
656 code('uint64_t _temp;')
657 code('bool _ret = to_number(%s, _temp);' % src)
658 code('if (_ret)')
659 code(' %s = Cycles(_temp);' % dest)
660 code('%s _ret;' % ret)
661
662class Float(ParamValue, float):
663 cxx_type = 'double'
664 cmd_line_settable = True
665
666 def __init__(self, value):
667 if isinstance(value, (int, long, float, NumericParamValue, Float, str)):
668 self.value = float(value)
669 else:
670 raise TypeError("Can't convert object of type %s to Float" \
671 % type(value).__name__)
672
673 def __call__(self, value):
674 self.__init__(value)
675 return value
676
677 def getValue(self):
678 return float(self.value)
679
680 def config_value(self):
681 return self
682
683 @classmethod
684 def cxx_ini_predecls(cls, code):
685 code('#include <sstream>')
686
687 @classmethod
688 def cxx_ini_parse(self, code, src, dest, ret):
689 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
690
691class MemorySize(CheckedInt):
692 cxx_type = 'uint64_t'
693 ex_str = '512MB'
694 size = 64
695 unsigned = True
696 def __init__(self, value):
697 if isinstance(value, MemorySize):
698 self.value = value.value
699 else:
700 self.value = convert.toMemorySize(value)
701 self._check()
702
703class MemorySize32(CheckedInt):
704 cxx_type = 'uint32_t'
705 ex_str = '512MB'
706 size = 32
707 unsigned = True
708 def __init__(self, value):
709 if isinstance(value, MemorySize):
710 self.value = value.value
711 else:
712 self.value = convert.toMemorySize(value)
713 self._check()
714
715class Addr(CheckedInt):
716 cxx_type = 'Addr'
717 size = 64
718 unsigned = True
719 def __init__(self, value):
720 if isinstance(value, Addr):
721 self.value = value.value
722 else:
723 try:
724 # Often addresses are referred to with sizes. Ex: A device
725 # base address is at "512MB". Use toMemorySize() to convert
726 # these into addresses. If the address is not specified with a
727 # "size", an exception will occur and numeric translation will
728 # proceed below.
729 self.value = convert.toMemorySize(value)
730 except (TypeError, ValueError):
731 # Convert number to string and use long() to do automatic
732 # base conversion (requires base=0 for auto-conversion)
733 self.value = long(str(value), base=0)
734
735 self._check()
736 def __add__(self, other):
737 if isinstance(other, Addr):
738 return self.value + other.value
739 else:
740 return self.value + other
741 def pretty_print(self, value):
742 try:
743 val = convert.toMemorySize(value)
744 except TypeError:
745 val = long(value)
746 return "0x%x" % long(val)
747
748class AddrRange(ParamValue):
749 cxx_type = 'AddrRange'
750
751 def __init__(self, *args, **kwargs):
752 # Disable interleaving and hashing by default
753 self.intlvHighBit = 0
754 self.xorHighBit = 0
755 self.intlvBits = 0
756 self.intlvMatch = 0
757
758 def handle_kwargs(self, kwargs):
759 # An address range needs to have an upper limit, specified
760 # either explicitly with an end, or as an offset using the
761 # size keyword.
762 if 'end' in kwargs:
763 self.end = Addr(kwargs.pop('end'))
764 elif 'size' in kwargs:
765 self.end = self.start + Addr(kwargs.pop('size')) - 1
766 else:
767 raise TypeError("Either end or size must be specified")
768
769 # Now on to the optional bit
770 if 'intlvHighBit' in kwargs:
771 self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
772 if 'xorHighBit' in kwargs:
773 self.xorHighBit = int(kwargs.pop('xorHighBit'))
774 if 'intlvBits' in kwargs:
775 self.intlvBits = int(kwargs.pop('intlvBits'))
776 if 'intlvMatch' in kwargs:
777 self.intlvMatch = int(kwargs.pop('intlvMatch'))
778
779 if len(args) == 0:
780 self.start = Addr(kwargs.pop('start'))
781 handle_kwargs(self, kwargs)
782
783 elif len(args) == 1:
784 if kwargs:
785 self.start = Addr(args[0])
786 handle_kwargs(self, kwargs)
787 elif isinstance(args[0], (list, tuple)):
788 self.start = Addr(args[0][0])
789 self.end = Addr(args[0][1])
790 else:
791 self.start = Addr(0)
792 self.end = Addr(args[0]) - 1
793
794 elif len(args) == 2:
795 self.start = Addr(args[0])
796 self.end = Addr(args[1])
797 else:
798 raise TypeError("Too many arguments specified")
799
800 if kwargs:
801 raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
802
803 def __str__(self):
804 return '%s:%s:%s:%s:%s:%s' \
805 % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\
806 self.intlvBits, self.intlvMatch)
807
808 def size(self):
809 # Divide the size by the size of the interleaving slice
810 return (long(self.end) - long(self.start) + 1) >> self.intlvBits
811
812 @classmethod
813 def cxx_predecls(cls, code):
814 Addr.cxx_predecls(code)
815 code('#include "base/addr_range.hh"')
816
817 @classmethod
818 def pybind_predecls(cls, code):
819 Addr.pybind_predecls(code)
820 code('#include "base/addr_range.hh"')
821
822 @classmethod
823 def cxx_ini_predecls(cls, code):
824 code('#include <sstream>')
825
826 @classmethod
827 def cxx_ini_parse(cls, code, src, dest, ret):
828 code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;')
829 code('uint64_t _intlvBits = 0, _intlvMatch = 0;')
830 code('char _sep;')
831 code('std::istringstream _stream(${src});')
832 code('_stream >> _start;')
833 code('_stream.get(_sep);')
834 code('_stream >> _end;')
835 code('if (!_stream.fail() && !_stream.eof()) {')
836 code(' _stream.get(_sep);')
837 code(' _stream >> _intlvHighBit;')
838 code(' _stream.get(_sep);')
839 code(' _stream >> _xorHighBit;')
840 code(' _stream.get(_sep);')
841 code(' _stream >> _intlvBits;')
842 code(' _stream.get(_sep);')
843 code(' _stream >> _intlvMatch;')
844 code('}')
845 code('bool _ret = !_stream.fail() &&'
846 '_stream.eof() && _sep == \':\';')
847 code('if (_ret)')
848 code(' ${dest} = AddrRange(_start, _end, _intlvHighBit, \
849 _xorHighBit, _intlvBits, _intlvMatch);')
850 code('${ret} _ret;')
851
852 def getValue(self):
853 # Go from the Python class to the wrapped C++ class
854 from _m5.range import AddrRange
855
856 return AddrRange(long(self.start), long(self.end),
857 int(self.intlvHighBit), int(self.xorHighBit),
858 int(self.intlvBits), int(self.intlvMatch))
859
860# Boolean parameter type. Python doesn't let you subclass bool, since
861# it doesn't want to let you create multiple instances of True and
862# False. Thus this is a little more complicated than String.
863class Bool(ParamValue):
864 cxx_type = 'bool'
865 cmd_line_settable = True
866
867 def __init__(self, value):
868 try:
869 self.value = convert.toBool(value)
870 except TypeError:
871 self.value = bool(value)
872
873 def __call__(self, value):
874 self.__init__(value)
875 return value
876
877 def getValue(self):
878 return bool(self.value)
879
880 def __str__(self):
881 return str(self.value)
882
883 # implement truth value testing for Bool parameters so that these params
884 # evaluate correctly during the python configuration phase
885 def __bool__(self):
886 return bool(self.value)
887
888 # Python 2.7 uses __nonzero__ instead of __bool__
889 __nonzero__ = __bool__
890
891 def ini_str(self):
892 if self.value:
893 return 'true'
894 return 'false'
895
896 def config_value(self):
897 return self.value
898
899 @classmethod
900 def cxx_ini_predecls(cls, code):
901 # Assume that base/str.hh will be included anyway
902 # code('#include "base/str.hh"')
903 pass
904
905 @classmethod
906 def cxx_ini_parse(cls, code, src, dest, ret):
907 code('%s to_bool(%s, %s);' % (ret, src, dest))
908
909def IncEthernetAddr(addr, val = 1):
910 bytes = [ int(x, 16) for x in addr.split(':') ]
911 bytes[5] += val
912 for i in (5, 4, 3, 2, 1):
913 val,rem = divmod(bytes[i], 256)
914 bytes[i] = rem
915 if val == 0:
916 break
917 bytes[i - 1] += val
918 assert(bytes[0] <= 255)
919 return ':'.join(map(lambda x: '%02x' % x, bytes))
920
921_NextEthernetAddr = "00:90:00:00:00:01"
922def NextEthernetAddr():
923 global _NextEthernetAddr
924
925 value = _NextEthernetAddr
926 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
927 return value
928
929class EthernetAddr(ParamValue):
930 cxx_type = 'Net::EthAddr'
931 ex_str = "00:90:00:00:00:01"
932 cmd_line_settable = True
933
934 @classmethod
935 def cxx_predecls(cls, code):
936 code('#include "base/inet.hh"')
937
938 def __init__(self, value):
939 if value == NextEthernetAddr:
940 self.value = value
941 return
942
943 if not isinstance(value, str):
944 raise TypeError("expected an ethernet address and didn't get one")
945
946 bytes = value.split(':')
947 if len(bytes) != 6:
948 raise TypeError('invalid ethernet address %s' % value)
949
950 for byte in bytes:
951 if not 0 <= int(byte, base=16) <= 0xff:
952 raise TypeError('invalid ethernet address %s' % value)
953
954 self.value = value
955
956 def __call__(self, value):
957 self.__init__(value)
958 return value
959
960 def unproxy(self, base):
961 if self.value == NextEthernetAddr:
962 return EthernetAddr(self.value())
963 return self
964
965 def getValue(self):
966 from _m5.net import EthAddr
967 return EthAddr(self.value)
968
969 def __str__(self):
970 return self.value
971
972 def ini_str(self):
973 return self.value
974
975 @classmethod
976 def cxx_ini_parse(self, code, src, dest, ret):
977 code('%s = Net::EthAddr(%s);' % (dest, src))
978 code('%s true;' % ret)
979
980# When initializing an IpAddress, pass in an existing IpAddress, a string of
981# the form "a.b.c.d", or an integer representing an IP.
982class IpAddress(ParamValue):
983 cxx_type = 'Net::IpAddress'
984 ex_str = "127.0.0.1"
985 cmd_line_settable = True
986
987 @classmethod
988 def cxx_predecls(cls, code):
989 code('#include "base/inet.hh"')
990
991 def __init__(self, value):
992 if isinstance(value, IpAddress):
993 self.ip = value.ip
994 else:
995 try:
996 self.ip = convert.toIpAddress(value)
997 except TypeError:
998 self.ip = long(value)
999 self.verifyIp()
1000
1001 def __call__(self, value):
1002 self.__init__(value)
1003 return value
1004
1005 def __str__(self):
1006 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)]
1007 return '%d.%d.%d.%d' % tuple(tup)
1008
1009 def __eq__(self, other):
1010 if isinstance(other, IpAddress):
1011 return self.ip == other.ip
1012 elif isinstance(other, str):
1013 try:
1014 return self.ip == convert.toIpAddress(other)
1015 except:
1016 return False
1017 else:
1018 return self.ip == other
1019
1020 def __ne__(self, other):
1021 return not (self == other)
1022
1023 def verifyIp(self):
1024 if self.ip < 0 or self.ip >= (1 << 32):
1025 raise TypeError("invalid ip address %#08x" % self.ip)
1026
1027 def getValue(self):
1028 from _m5.net import IpAddress
1029 return IpAddress(self.ip)
1030
1031# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
1032# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
1033# positional or keyword arguments.
1034class IpNetmask(IpAddress):
1035 cxx_type = 'Net::IpNetmask'
1036 ex_str = "127.0.0.0/24"
1037 cmd_line_settable = True
1038
1039 @classmethod
1040 def cxx_predecls(cls, code):
1041 code('#include "base/inet.hh"')
1042
1043 def __init__(self, *args, **kwargs):
1044 def handle_kwarg(self, kwargs, key, elseVal = None):
1045 if key in kwargs:
1046 setattr(self, key, kwargs.pop(key))
1047 elif elseVal:
1048 setattr(self, key, elseVal)
1049 else:
1050 raise TypeError("No value set for %s" % key)
1051
1052 if len(args) == 0:
1053 handle_kwarg(self, kwargs, 'ip')
1054 handle_kwarg(self, kwargs, 'netmask')
1055
1056 elif len(args) == 1:
1057 if kwargs:
1058 if not 'ip' in kwargs and not 'netmask' in kwargs:
1059 raise TypeError("Invalid arguments")
1060 handle_kwarg(self, kwargs, 'ip', args[0])
1061 handle_kwarg(self, kwargs, 'netmask', args[0])
1062 elif isinstance(args[0], IpNetmask):
1063 self.ip = args[0].ip
1064 self.netmask = args[0].netmask
1065 else:
1066 (self.ip, self.netmask) = convert.toIpNetmask(args[0])
1067
1068 elif len(args) == 2:
1069 self.ip = args[0]
1070 self.netmask = args[1]
1071 else:
1072 raise TypeError("Too many arguments specified")
1073
1074 if kwargs:
1075 raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1076
1077 self.verify()
1078
1079 def __call__(self, value):
1080 self.__init__(value)
1081 return value
1082
1083 def __str__(self):
1084 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
1085
1086 def __eq__(self, other):
1087 if isinstance(other, IpNetmask):
1088 return self.ip == other.ip and self.netmask == other.netmask
1089 elif isinstance(other, str):
1090 try:
1091 return (self.ip, self.netmask) == convert.toIpNetmask(other)
1092 except:
1093 return False
1094 else:
1095 return False
1096
1097 def verify(self):
1098 self.verifyIp()
1099 if self.netmask < 0 or self.netmask > 32:
1100 raise TypeError("invalid netmask %d" % netmask)
1101
1102 def getValue(self):
1103 from _m5.net import IpNetmask
1104 return IpNetmask(self.ip, self.netmask)
1105
1106# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1107# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1108class IpWithPort(IpAddress):
1109 cxx_type = 'Net::IpWithPort'
1110 ex_str = "127.0.0.1:80"
1111 cmd_line_settable = True
1112
1113 @classmethod
1114 def cxx_predecls(cls, code):
1115 code('#include "base/inet.hh"')
1116
1117 def __init__(self, *args, **kwargs):
1118 def handle_kwarg(self, kwargs, key, elseVal = None):
1119 if key in kwargs:
1120 setattr(self, key, kwargs.pop(key))
1121 elif elseVal:
1122 setattr(self, key, elseVal)
1123 else:
1124 raise TypeError("No value set for %s" % key)
1125
1126 if len(args) == 0:
1127 handle_kwarg(self, kwargs, 'ip')
1128 handle_kwarg(self, kwargs, 'port')
1129
1130 elif len(args) == 1:
1131 if kwargs:
1132 if not 'ip' in kwargs and not 'port' in kwargs:
1133 raise TypeError("Invalid arguments")
1134 handle_kwarg(self, kwargs, 'ip', args[0])
1135 handle_kwarg(self, kwargs, 'port', args[0])
1136 elif isinstance(args[0], IpWithPort):
1137 self.ip = args[0].ip
1138 self.port = args[0].port
1139 else:
1140 (self.ip, self.port) = convert.toIpWithPort(args[0])
1141
1142 elif len(args) == 2:
1143 self.ip = args[0]
1144 self.port = args[1]
1145 else:
1146 raise TypeError("Too many arguments specified")
1147
1148 if kwargs:
1149 raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1150
1151 self.verify()
1152
1153 def __call__(self, value):
1154 self.__init__(value)
1155 return value
1156
1157 def __str__(self):
1158 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1159
1160 def __eq__(self, other):
1161 if isinstance(other, IpWithPort):
1162 return self.ip == other.ip and self.port == other.port
1163 elif isinstance(other, str):
1164 try:
1165 return (self.ip, self.port) == convert.toIpWithPort(other)
1166 except:
1167 return False
1168 else:
1169 return False
1170
1171 def verify(self):
1172 self.verifyIp()
1173 if self.port < 0 or self.port > 0xffff:
1174 raise TypeError("invalid port %d" % self.port)
1175
1176 def getValue(self):
1177 from _m5.net import IpWithPort
1178 return IpWithPort(self.ip, self.port)
1179
1180time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1181 "%a %b %d %H:%M:%S %Y",
1182 "%Y/%m/%d %H:%M:%S",
1183 "%Y/%m/%d %H:%M",
1184 "%Y/%m/%d",
1185 "%m/%d/%Y %H:%M:%S",
1186 "%m/%d/%Y %H:%M",
1187 "%m/%d/%Y",
1188 "%m/%d/%y %H:%M:%S",
1189 "%m/%d/%y %H:%M",
1190 "%m/%d/%y"]
1191
1192
1193def parse_time(value):
1194 from time import gmtime, strptime, struct_time, time
1195 from datetime import datetime, date
1196
1197 if isinstance(value, struct_time):
1198 return value
1199
1200 if isinstance(value, (int, long)):
1201 return gmtime(value)
1202
1203 if isinstance(value, (datetime, date)):
1204 return value.timetuple()
1205
1206 if isinstance(value, str):
1207 if value in ('Now', 'Today'):
1208 return time.gmtime(time.time())
1209
1210 for format in time_formats:
1211 try:
1212 return strptime(value, format)
1213 except ValueError:
1214 pass
1215
1216 raise ValueError("Could not parse '%s' as a time" % value)
1217
1218class Time(ParamValue):
1219 cxx_type = 'tm'
1220
1221 @classmethod
1222 def cxx_predecls(cls, code):
1223 code('#include <time.h>')
1224
1225 def __init__(self, value):
1226 self.value = parse_time(value)
1227
1228 def __call__(self, value):
1229 self.__init__(value)
1230 return value
1231
1232 def getValue(self):
1233 from _m5.core import tm
1234 import calendar
1235
1236 return tm.gmtime(calendar.timegm(self.value))
1237
1238 def __str__(self):
1239 return time.asctime(self.value)
1240
1241 def ini_str(self):
1242 return str(self)
1243
1244 def get_config_as_dict(self):
1245 assert false
1246 return str(self)
1247
1248 @classmethod
1249 def cxx_ini_predecls(cls, code):
1250 code('#include <time.h>')
1251
1252 @classmethod
1253 def cxx_ini_parse(cls, code, src, dest, ret):
1254 code('char *_parse_ret = strptime((${src}).c_str(),')
1255 code(' "%a %b %d %H:%M:%S %Y", &(${dest}));')
1256 code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1257
1258# Enumerated types are a little more complex. The user specifies the
1259# type as Enum(foo) where foo is either a list or dictionary of
1260# alternatives (typically strings, but not necessarily so). (In the
1261# long run, the integer value of the parameter will be the list index
1262# or the corresponding dictionary value. For now, since we only check
1263# that the alternative is valid and then spit it into a .ini file,
1264# there's not much point in using the dictionary.)
1265
1266# What Enum() must do is generate a new type encapsulating the
1267# provided list/dictionary so that specific values of the parameter
1268# can be instances of that type. We define two hidden internal
1269# classes (_ListEnum and _DictEnum) to serve as base classes, then
1270# derive the new type from the appropriate base class on the fly.
1271
1272allEnums = {}
1273# Metaclass for Enum types
1274class MetaEnum(MetaParamValue):
1275 def __new__(mcls, name, bases, dict):
1276 assert name not in allEnums
1277
1278 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1279 allEnums[name] = cls
1280 return cls
1281
1282 def __init__(cls, name, bases, init_dict):
1283 if 'map' in init_dict:
1284 if not isinstance(cls.map, dict):
1285 raise TypeError("Enum-derived class attribute 'map' " \
1286 "must be of type dict")
1287 # build list of value strings from map
1288 cls.vals = list(cls.map.keys())
1289 cls.vals.sort()
1290 elif 'vals' in init_dict:
1291 if not isinstance(cls.vals, list):
1292 raise TypeError("Enum-derived class attribute 'vals' " \
1293 "must be of type list")
1294 # build string->value map from vals sequence
1295 cls.map = {}
1296 for idx,val in enumerate(cls.vals):
1297 cls.map[val] = idx
1298 else:
1299 raise TypeError("Enum-derived class must define "\
1300 "attribute 'map' or 'vals'")
1301
1302 if cls.is_class:
1303 cls.cxx_type = '%s' % name
1304 else:
1305 cls.cxx_type = 'Enums::%s' % name
1306
1307 super(MetaEnum, cls).__init__(name, bases, init_dict)
1308
1309 # Generate C++ class declaration for this enum type.
1310 # Note that we wrap the enum in a class/struct to act as a namespace,
1311 # so that the enum strings can be brief w/o worrying about collisions.
1312 def cxx_decl(cls, code):
1313 wrapper_name = cls.wrapper_name
1314 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1315 name = cls.__name__ if cls.enum_name is None else cls.enum_name
1316 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1317
1318 code('''\
1319#ifndef $idem_macro
1320#define $idem_macro
1321
1322''')
1323 if cls.is_class:
1324 code('''\
1325enum class $name {
1326''')
1327 else:
1328 code('''\
1329$wrapper $wrapper_name {
1330 enum $name {
1331''')
1332 code.indent(1)
1333 code.indent(1)
1334 for val in cls.vals:
1335 code('$val = ${{cls.map[val]}},')
1336 code('Num_$name = ${{len(cls.vals)}}')
1337 code.dedent(1)
1338 code('};')
1339
1340 if cls.is_class:
1341 code('''\
1342extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
1343''')
1344 elif cls.wrapper_is_struct:
1345 code('static const char *${name}Strings[Num_${name}];')
1346 else:
1347 code('extern const char *${name}Strings[Num_${name}];')
1348
1349 if not cls.is_class:
1350 code.dedent(1)
1351 code('};')
1352
1353 code()
1354 code('#endif // $idem_macro')
1355
1356 def cxx_def(cls, code):
1357 wrapper_name = cls.wrapper_name
1358 file_name = cls.__name__
1359 name = cls.__name__ if cls.enum_name is None else cls.enum_name
1360
1361 code('#include "enums/$file_name.hh"')
1362 if cls.wrapper_is_struct:
1363 code('const char *${wrapper_name}::${name}Strings'
1364 '[Num_${name}] =')
1365 else:
1366 if cls.is_class:
1367 code('''\
1368const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
1369''')
1370 else:
1371 code('namespace Enums {')
1372 code.indent(1)
1373 code('const char *${name}Strings[Num_${name}] =')
1374
1375 code('{')
1376 code.indent(1)
1377 for val in cls.vals:
1378 code('"$val",')
1379 code.dedent(1)
1380 code('};')
1381
1382 if not cls.wrapper_is_struct and not cls.is_class:
1383 code.dedent(1)
1384 code('} // namespace $wrapper_name')
1385
1386
1387 def pybind_def(cls, code):
1388 name = cls.__name__
1389 enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
1390 wrapper_name = enum_name if cls.is_class else cls.wrapper_name
1391
1392 code('''#include "pybind11/pybind11.h"
1393#include "pybind11/stl.h"
1394
1395#include <sim/init.hh>
1396
1397namespace py = pybind11;
1398
1399static void
1400module_init(py::module &m_internal)
1401{
1402 py::module m = m_internal.def_submodule("enum_${name}");
1403
1404''')
1405 if cls.is_class:
1406 code('py::enum_<${enum_name}>(m, "enum_${name}")')
1407 else:
1408 code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
1409
1410 code.indent()
1411 code.indent()
1412 for val in cls.vals:
1413 code('.value("${val}", ${wrapper_name}::${val})')
1414 code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1415 code('.export_values()')
1416 code(';')
1417 code.dedent()
1418
1419 code('}')
1420 code.dedent()
1421 code()
1422 code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1423
1424
1425# Base class for enum types.
1426class Enum(ParamValue):
1427 __metaclass__ = MetaEnum
1428 vals = []
1429 cmd_line_settable = True
1430
1431 # The name of the wrapping namespace or struct
1432 wrapper_name = 'Enums'
1433
1434 # If true, the enum is wrapped in a struct rather than a namespace
1435 wrapper_is_struct = False
1436
1437 is_class = False
1438
1439 # If not None, use this as the enum name rather than this class name
1440 enum_name = None
1441
1442 def __init__(self, value):
1443 if value not in self.map:
1444 raise TypeError("Enum param got bad value '%s' (not in %s)" \
1445 % (value, self.vals))
1446 self.value = value
1447
1448 def __call__(self, value):
1449 self.__init__(value)
1450 return value
1451
1452 @classmethod
1453 def cxx_predecls(cls, code):
1454 code('#include "enums/$0.hh"', cls.__name__)
1455
1456 @classmethod
1457 def cxx_ini_parse(cls, code, src, dest, ret):
1458 code('if (false) {')
1459 for elem_name in cls.map.keys():
1460 code('} else if (%s == "%s") {' % (src, elem_name))
1461 code.indent()
1462 code('%s = Enums::%s;' % (dest, elem_name))
1463 code('%s true;' % ret)
1464 code.dedent()
1465 code('} else {')
1466 code(' %s false;' % ret)
1467 code('}')
1468
1469 def getValue(self):
1470 import m5.internal.params
1471 e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
1472 return e(self.map[self.value])
1473
1474 def __str__(self):
1475 return self.value
1476
1477# This param will generate a scoped c++ enum and its python bindings.
1478class ScopedEnum(Enum):
1479 __metaclass__ = MetaEnum
1480 vals = []
1481 cmd_line_settable = True
1482
1483 # The name of the wrapping namespace or struct
1484 wrapper_name = None
1485
1486 # If true, the enum is wrapped in a struct rather than a namespace
1487 wrapper_is_struct = False
1488
1489 # If true, the generated enum is a scoped enum
1490 is_class = True
1491
1492 # If not None, use this as the enum name rather than this class name
1493 enum_name = None
1494
1495# how big does a rounding error need to be before we warn about it?
1496frequency_tolerance = 0.001 # 0.1%
1497
1498class TickParamValue(NumericParamValue):
1499 cxx_type = 'Tick'
1500 ex_str = "1MHz"
1501 cmd_line_settable = True
1502
1503 @classmethod
1504 def cxx_predecls(cls, code):
1505 code('#include "base/types.hh"')
1506
1507 def __call__(self, value):
1508 self.__init__(value)
1509 return value
1510
1511 def getValue(self):
1512 return long(self.value)
1513
1514 @classmethod
1515 def cxx_ini_predecls(cls, code):
1516 code('#include <sstream>')
1517
1518 # Ticks are expressed in seconds in JSON files and in plain
1519 # Ticks in .ini files. Switch based on a config flag
1520 @classmethod
1521 def cxx_ini_parse(self, code, src, dest, ret):
1522 code('${ret} to_number(${src}, ${dest});')
1523
1524class Latency(TickParamValue):
1525 ex_str = "100ns"
1526
1527 def __init__(self, value):
1528 if isinstance(value, (Latency, Clock)):
1529 self.ticks = value.ticks
1530 self.value = value.value
1531 elif isinstance(value, Frequency):
1532 self.ticks = value.ticks
1533 self.value = 1.0 / value.value
1534 elif value.endswith('t'):
1535 self.ticks = True
1536 self.value = int(value[:-1])
1537 else:
1538 self.ticks = False
1539 self.value = convert.toLatency(value)
1540
1541 def __call__(self, value):
1542 self.__init__(value)
1543 return value
1544
1545 def __getattr__(self, attr):
1546 if attr in ('latency', 'period'):
1547 return self
1548 if attr == 'frequency':
1549 return Frequency(self)
1550 raise AttributeError("Latency object has no attribute '%s'" % attr)
1551
1552 def getValue(self):
1553 if self.ticks or self.value == 0:
1554 value = self.value
1555 else:
1556 value = ticks.fromSeconds(self.value)
1557 return long(value)
1558
1559 def config_value(self):
1560 return self.getValue()
1561
1562 # convert latency to ticks
1563 def ini_str(self):
1564 return '%d' % self.getValue()
1565
1566class Frequency(TickParamValue):
1567 ex_str = "1GHz"
1568
1569 def __init__(self, value):
1570 if isinstance(value, (Latency, Clock)):
1571 if value.value == 0:
1572 self.value = 0
1573 else:
1574 self.value = 1.0 / value.value
1575 self.ticks = value.ticks
1576 elif isinstance(value, Frequency):
1577 self.value = value.value
1578 self.ticks = value.ticks
1579 else:
1580 self.ticks = False
1581 self.value = convert.toFrequency(value)
1582
1583 def __call__(self, value):
1584 self.__init__(value)
1585 return value
1586
1587 def __getattr__(self, attr):
1588 if attr == 'frequency':
1589 return self
1590 if attr in ('latency', 'period'):
1591 return Latency(self)
1592 raise AttributeError("Frequency object has no attribute '%s'" % attr)
1593
1594 # convert latency to ticks
1595 def getValue(self):
1596 if self.ticks or self.value == 0:
1597 value = self.value
1598 else:
1599 value = ticks.fromSeconds(1.0 / self.value)
1600 return long(value)
1601
1602 def config_value(self):
1603 return self.getValue()
1604
1605 def ini_str(self):
1606 return '%d' % self.getValue()
1607
1608# A generic Frequency and/or Latency value. Value is stored as a
1609# latency, just like Latency and Frequency.
1610class Clock(TickParamValue):
1611 def __init__(self, value):
1612 if isinstance(value, (Latency, Clock)):
1613 self.ticks = value.ticks
1614 self.value = value.value
1615 elif isinstance(value, Frequency):
1616 self.ticks = value.ticks
1617 self.value = 1.0 / value.value
1618 elif value.endswith('t'):
1619 self.ticks = True
1620 self.value = int(value[:-1])
1621 else:
1622 self.ticks = False
1623 self.value = convert.anyToLatency(value)
1624
1625 def __call__(self, value):
1626 self.__init__(value)
1627 return value
1628
1629 def __str__(self):
1630 return "%s" % Latency(self)
1631
1632 def __getattr__(self, attr):
1633 if attr == 'frequency':
1634 return Frequency(self)
1635 if attr in ('latency', 'period'):
1636 return Latency(self)
1637 raise AttributeError("Frequency object has no attribute '%s'" % attr)
1638
1639 def getValue(self):
1640 return self.period.getValue()
1641
1642 def config_value(self):
1643 return self.period.config_value()
1644
1645 def ini_str(self):
1646 return self.period.ini_str()
1647
1648class Voltage(Float):
1649 ex_str = "1V"
1650
1651 def __new__(cls, value):
1652 value = convert.toVoltage(value)
1653 return super(cls, Voltage).__new__(cls, value)
1654
1655 def __init__(self, value):
1656 value = convert.toVoltage(value)
1657 super(Voltage, self).__init__(value)
1658
1659class Current(Float):
1660 ex_str = "1mA"
1661
1662 def __new__(cls, value):
1663 value = convert.toCurrent(value)
1664 return super(cls, Current).__new__(cls, value)
1665
1666 def __init__(self, value):
1667 value = convert.toCurrent(value)
1668 super(Current, self).__init__(value)
1669
1670class Energy(Float):
1671 ex_str = "1pJ"
1672
1673 def __new__(cls, value):
1674 value = convert.toEnergy(value)
1675 return super(cls, Energy).__new__(cls, value)
1676
1677 def __init__(self, value):
1678 value = convert.toEnergy(value)
1679 super(Energy, self).__init__(value)
1680
1681class NetworkBandwidth(float,ParamValue):
1682 cxx_type = 'float'
1683 ex_str = "1Gbps"
1684 cmd_line_settable = True
1685
1686 def __new__(cls, value):
1687 # convert to bits per second
1688 val = convert.toNetworkBandwidth(value)
1689 return super(cls, NetworkBandwidth).__new__(cls, val)
1690
1691 def __str__(self):
1692 return str(self.val)
1693
1694 def __call__(self, value):
1695 val = convert.toNetworkBandwidth(value)
1696 self.__init__(val)
1697 return value
1698
1699 def getValue(self):
1700 # convert to seconds per byte
1701 value = 8.0 / float(self)
1702 # convert to ticks per byte
1703 value = ticks.fromSeconds(value)
1704 return float(value)
1705
1706 def ini_str(self):
1707 return '%f' % self.getValue()
1708
1709 def config_value(self):
1710 return '%f' % self.getValue()
1711
1712 @classmethod
1713 def cxx_ini_predecls(cls, code):
1714 code('#include <sstream>')
1715
1716 @classmethod
1717 def cxx_ini_parse(self, code, src, dest, ret):
1718 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1719
1720class MemoryBandwidth(float,ParamValue):
1721 cxx_type = 'float'
1722 ex_str = "1GB/s"
1723 cmd_line_settable = True
1724
1725 def __new__(cls, value):
1726 # convert to bytes per second
1727 val = convert.toMemoryBandwidth(value)
1728 return super(cls, MemoryBandwidth).__new__(cls, val)
1729
1730 def __call__(self, value):
1731 val = convert.toMemoryBandwidth(value)
1732 self.__init__(val)
1733 return value
1734
1735 def getValue(self):
1736 # convert to seconds per byte
1737 value = float(self)
1738 if value:
1739 value = 1.0 / float(self)
1740 # convert to ticks per byte
1741 value = ticks.fromSeconds(value)
1742 return float(value)
1743
1744 def ini_str(self):
1745 return '%f' % self.getValue()
1746
1747 def config_value(self):
1748 return '%f' % self.getValue()
1749
1750 @classmethod
1751 def cxx_ini_predecls(cls, code):
1752 code('#include <sstream>')
1753
1754 @classmethod
1755 def cxx_ini_parse(self, code, src, dest, ret):
1756 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1757
1758#
1759# "Constants"... handy aliases for various values.
1760#
1761
1762# Special class for NULL pointers. Note the special check in
1763# make_param_value() above that lets these be assigned where a
1764# SimObject is required.
1765# only one copy of a particular node
1766class NullSimObject(object):
1767 __metaclass__ = Singleton
1768 _name = 'Null'
1769
1770 def __call__(cls):
1771 return cls
1772
1773 def _instantiate(self, parent = None, path = ''):
1774 pass
1775
1776 def ini_str(self):
1777 return 'Null'
1778
1779 def unproxy(self, base):
1780 return self
1781
1782 def set_path(self, parent, name):
1783 pass
1784
1785 def set_parent(self, parent, name):
1786 pass
1787
1788 def clear_parent(self, old_parent):
1789 pass
1790
1791 def descendants(self):
1792 return
1793 yield None
1794
1795 def get_config_as_dict(self):
1796 return {}
1797
1798 def __str__(self):
1799 return self._name
1800
1801 def config_value(self):
1802 return None
1803
1804 def getValue(self):
1805 return None
1806
1807# The only instance you'll ever need...
1808NULL = NullSimObject()
1809
1810def isNullPointer(value):
1811 return isinstance(value, NullSimObject)
1812
1813# Some memory range specifications use this as a default upper bound.
1814MaxAddr = Addr.max
1815MaxTick = Tick.max
1816AllMemory = AddrRange(0, MaxAddr)
1817
1818
1819#####################################################################
1820#
1821# Port objects
1822#
1823# Ports are used to interconnect objects in the memory system.
1824#
1825#####################################################################
1826
1827# Port reference: encapsulates a reference to a particular port on a
1828# particular SimObject.
1829class PortRef(object):
1830 def __init__(self, simobj, name, role):
1831 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1832 self.simobj = simobj
1833 self.name = name
1834 self.role = role
1835 self.peer = None # not associated with another port yet
1836 self.ccConnected = False # C++ port connection done?
1837 self.index = -1 # always -1 for non-vector ports
1838
1839 def __str__(self):
1840 return '%s.%s' % (self.simobj, self.name)
1841
1842 def __len__(self):
1843 # Return the number of connected ports, i.e. 0 is we have no
1844 # peer and 1 if we do.
1845 return int(self.peer != None)
1846
1847 # for config.ini, print peer's name (not ours)
1848 def ini_str(self):
1849 return str(self.peer)
1850
1851 # for config.json
1852 def get_config_as_dict(self):
1853 return {'role' : self.role, 'peer' : str(self.peer)}
1854
1855 def __getattr__(self, attr):
1856 if attr == 'peerObj':
1857 # shorthand for proxies
1858 return self.peer.simobj
1859 raise AttributeError("'%s' object has no attribute '%s'" % \
1860 (self.__class__.__name__, attr))
1861
1862 # Full connection is symmetric (both ways). Called via
1863 # SimObject.__setattr__ as a result of a port assignment, e.g.,
1864 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1865 # e.g., "obj1.portA[3] = obj2.portB".
1866 def connect(self, other):
1867 if isinstance(other, VectorPortRef):
1868 # reference to plain VectorPort is implicit append
1869 other = other._get_next()
1870 if self.peer and not proxy.isproxy(self.peer):
1871 fatal("Port %s is already connected to %s, cannot connect %s\n",
1872 self, self.peer, other);
1873 self.peer = other
1874 if proxy.isproxy(other):
1875 other.set_param_desc(PortParamDesc())
1876 elif isinstance(other, PortRef):
1877 if other.peer is not self:
1878 other.connect(self)
1879 else:
1880 raise TypeError("assigning non-port reference '%s' to port '%s'" \
1881 % (other, self))
1882
1883 # Allow a master/slave port pair to be spliced between
1884 # a port and its connected peer. Useful operation for connecting
1885 # instrumentation structures into a system when it is necessary
1886 # to connect the instrumentation after the full system has been
1887 # constructed.
1888 def splice(self, new_master_peer, new_slave_peer):
1889 if not self.peer or proxy.isproxy(self.peer):
1890 fatal("Port %s not connected, cannot splice in new peers\n", self)
1891
1892 if not isinstance(new_master_peer, PortRef) or \
1893 not isinstance(new_slave_peer, PortRef):
1894 raise TypeError(
1895 "Splicing non-port references '%s','%s' to port '%s'" % \
1896 (new_master_peer, new_slave_peer, self))
1897
1898 old_peer = self.peer
1899 if self.role == 'SLAVE':
1900 self.peer = new_master_peer
1901 old_peer.peer = new_slave_peer
1902 new_master_peer.connect(self)
1903 new_slave_peer.connect(old_peer)
1904 elif self.role == 'MASTER':
1905 self.peer = new_slave_peer
1906 old_peer.peer = new_master_peer
1907 new_slave_peer.connect(self)
1908 new_master_peer.connect(old_peer)
1909 else:
1910 panic("Port %s has unknown role, "+\
1911 "cannot splice in new peers\n", self)
1912
1913 def clone(self, simobj, memo):
1914 if self in memo:
1915 return memo[self]
1916 newRef = copy.copy(self)
1917 memo[self] = newRef
1918 newRef.simobj = simobj
1919 assert(isSimObject(newRef.simobj))
1920 if self.peer and not proxy.isproxy(self.peer):
1921 peerObj = self.peer.simobj(_memo=memo)
1922 newRef.peer = self.peer.clone(peerObj, memo)
1923 assert(not isinstance(newRef.peer, VectorPortRef))
1924 return newRef
1925
1926 def unproxy(self, simobj):
1927 assert(simobj is self.simobj)
1928 if proxy.isproxy(self.peer):
1929 try:
1930 realPeer = self.peer.unproxy(self.simobj)
1931 except:
1932 print("Error in unproxying port '%s' of %s" %
1933 (self.name, self.simobj.path()))
1934 raise
1935 self.connect(realPeer)
1936
1937 # Call C++ to create corresponding port connection between C++ objects
1938 def ccConnect(self):
1939 from _m5.pyobject import connectPorts
1940
1941 if self.ccConnected: # already done this
1942 return
1943
1944 peer = self.peer
1945 if not self.peer: # nothing to connect to
1946 return
1947
1948 # check that we connect a master to a slave
1949 if self.role == peer.role:
1950 raise TypeError(
1951 "cannot connect '%s' and '%s' due to identical role '%s'" % \
1952 (peer, self, self.role))
1953
1954 if self.role == 'SLAVE':
1955 # do nothing and let the master take care of it
1956 return
1957
1958 try:
1959 # self is always the master and peer the slave
1960 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1961 peer.simobj.getCCObject(), peer.name, peer.index)
1962 except:
1963 print("Error connecting port %s.%s to %s.%s" %
1964 (self.simobj.path(), self.name,
1965 peer.simobj.path(), peer.name))
1966 raise
1967 self.ccConnected = True
1968 peer.ccConnected = True
1969
1970# A reference to an individual element of a VectorPort... much like a
1971# PortRef, but has an index.
1972class VectorPortElementRef(PortRef):
1973 def __init__(self, simobj, name, role, index):
1974 PortRef.__init__(self, simobj, name, role)
1975 self.index = index
1976
1977 def __str__(self):
1978 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1979
1980# A reference to a complete vector-valued port (not just a single element).
1981# Can be indexed to retrieve individual VectorPortElementRef instances.
1982class VectorPortRef(object):
1983 def __init__(self, simobj, name, role):
1984 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1985 self.simobj = simobj
1986 self.name = name
1987 self.role = role
1988 self.elements = []
1989
1990 def __str__(self):
1991 return '%s.%s[:]' % (self.simobj, self.name)
1992
1993 def __len__(self):
1994 # Return the number of connected peers, corresponding the the
1995 # length of the elements.
1996 return len(self.elements)
1997
1998 # for config.ini, print peer's name (not ours)
1999 def ini_str(self):
2000 return ' '.join([el.ini_str() for el in self.elements])
2001
2002 # for config.json
2003 def get_config_as_dict(self):
2004 return {'role' : self.role,
2005 'peer' : [el.ini_str() for el in self.elements]}
2006
2007 def __getitem__(self, key):
2008 if not isinstance(key, int):
2009 raise TypeError("VectorPort index must be integer")
2010 if key >= len(self.elements):
2011 # need to extend list
2012 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
2013 for i in range(len(self.elements), key+1)]
2014 self.elements.extend(ext)
2015 return self.elements[key]
2016
2017 def _get_next(self):
2018 return self[len(self.elements)]
2019
2020 def __setitem__(self, key, value):
2021 if not isinstance(key, int):
2022 raise TypeError("VectorPort index must be integer")
2023 self[key].connect(value)
2024
2025 def connect(self, other):
2026 if isinstance(other, (list, tuple)):
2027 # Assign list of port refs to vector port.
2028 # For now, append them... not sure if that's the right semantics
2029 # or if it should replace the current vector.
2030 for ref in other:
2031 self._get_next().connect(ref)
2032 else:
2033 # scalar assignment to plain VectorPort is implicit append
2034 self._get_next().connect(other)
2035
2036 def clone(self, simobj, memo):
2037 if self in memo:
2038 return memo[self]
2039 newRef = copy.copy(self)
2040 memo[self] = newRef
2041 newRef.simobj = simobj
2042 assert(isSimObject(newRef.simobj))
2043 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
2044 return newRef
2045
2046 def unproxy(self, simobj):
2047 [el.unproxy(simobj) for el in self.elements]
2048
2049 def ccConnect(self):
2050 [el.ccConnect() for el in self.elements]
2051
2052# Port description object. Like a ParamDesc object, this represents a
2053# logical port in the SimObject class, not a particular port on a
2054# SimObject instance. The latter are represented by PortRef objects.
2055class Port(object):
2056 # Generate a PortRef for this port on the given SimObject with the
2057 # given name
2058 def makeRef(self, simobj):
2059 return PortRef(simobj, self.name, self.role)
2060
2061 # Connect an instance of this port (on the given SimObject with
2062 # the given name) with the port described by the supplied PortRef
2063 def connect(self, simobj, ref):
2064 self.makeRef(simobj).connect(ref)
2065
2066 # No need for any pre-declarations at the moment as we merely rely
2067 # on an unsigned int.
2068 def cxx_predecls(self, code):
2069 pass
2070
2071 def pybind_predecls(self, code):
2072 cls.cxx_predecls(self, code)
2073
2074 # Declare an unsigned int with the same name as the port, that
2075 # will eventually hold the number of connected ports (and thus the
2076 # number of elements for a VectorPort).
2077 def cxx_decl(self, code):
2078 code('unsigned int port_${{self.name}}_connection_count;')
2079
2080class MasterPort(Port):
2081 # MasterPort("description")
2082 def __init__(self, *args):
2083 if len(args) == 1:
2084 self.desc = args[0]
2085 self.role = 'MASTER'
2086 else:
2087 raise TypeError('wrong number of arguments')
2088
2089class SlavePort(Port):
2090 # SlavePort("description")
2091 def __init__(self, *args):
2092 if len(args) == 1:
2093 self.desc = args[0]
2094 self.role = 'SLAVE'
2095 else:
2096 raise TypeError('wrong number of arguments')
2097
2098# VectorPort description object. Like Port, but represents a vector
2099# of connections (e.g., as on a XBar).
2100class VectorPort(Port):
2101 def __init__(self, *args):
2102 self.isVec = True
2103
2104 def makeRef(self, simobj):
2105 return VectorPortRef(simobj, self.name, self.role)
2106
2107class VectorMasterPort(VectorPort):
2108 # VectorMasterPort("description")
2109 def __init__(self, *args):
2110 if len(args) == 1:
2111 self.desc = args[0]
2112 self.role = 'MASTER'
2113 VectorPort.__init__(self, *args)
2114 else:
2115 raise TypeError('wrong number of arguments')
2116
2117class VectorSlavePort(VectorPort):
2118 # VectorSlavePort("description")
2119 def __init__(self, *args):
2120 if len(args) == 1:
2121 self.desc = args[0]
2122 self.role = 'SLAVE'
2123 VectorPort.__init__(self, *args)
2124 else:
2125 raise TypeError('wrong number of arguments')
2126
2127# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2128# proxy objects (via set_param_desc()) so that proxy error messages
2129# make sense.
2130class PortParamDesc(object):
2131 __metaclass__ = Singleton
2132
2133 ptype_str = 'Port'
2134 ptype = Port
2135
2136baseEnums = allEnums.copy()
2137baseParams = allParams.copy()
2138
2139def clear():
2140 global allEnums, allParams
2141
2142 allEnums = baseEnums.copy()
2143 allParams = baseParams.copy()
2144
2145__all__ = ['Param', 'VectorParam',
2146 'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
2147 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2148 'Int32', 'UInt32', 'Int64', 'UInt64',
2149 'Counter', 'Addr', 'Tick', 'Percent',
2150 'TcpPort', 'UdpPort', 'EthernetAddr',
2151 'IpAddress', 'IpNetmask', 'IpWithPort',
2152 'MemorySize', 'MemorySize32',
2153 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2154 'NetworkBandwidth', 'MemoryBandwidth',
2155 'AddrRange',
2156 'MaxAddr', 'MaxTick', 'AllMemory',
2157 'Time',
2158 'NextEthernetAddr', 'NULL',
2159 'MasterPort', 'SlavePort',
2160 'VectorMasterPort', 'VectorSlavePort']
2161
2162import SimObject
2162from . import SimObject