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