0a563b8f26292b3a9c28e3f118e11c23c3d74c33
1 # Copyright (c) 2012-2014, 2017, 2018 ARM Limited
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.
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.
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.
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.
40 # Authors: Steve Reinhardt
45 #####################################################################
47 # Parameter description classes
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
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.
60 #####################################################################
62 from __future__
import print_function
75 def isSimObject(*args
, **kwargs
):
76 return SimObject
.isSimObject(*args
, **kwargs
)
78 def isSimObjectSequence(*args
, **kwargs
):
79 return SimObject
.isSimObjectSequence(*args
, **kwargs
)
81 def isSimObjectClass(*args
, **kwargs
):
82 return SimObject
.isSimObjectClass(*args
, **kwargs
)
86 class MetaParamValue(type):
87 def __new__(mcls
, name
, bases
, dct
):
88 cls
= super(MetaParamValue
, mcls
).__new
__(mcls
, name
, bases
, dct
)
89 assert name
not in allParams
94 # Dummy base class to identify types that are legitimate for SimObject
96 class ParamValue(object):
97 __metaclass__
= MetaParamValue
98 cmd_line_settable
= False
100 # Generate the code needed as a prerequisite for declaring a C++
101 # object of this type. Typically generates one or more #include
102 # statements. Used when declaring parameters of this type.
104 def cxx_predecls(cls
, code
):
108 def pybind_predecls(cls
, code
):
109 cls
.cxx_predecls(code
)
111 # default for printing to .ini file is regular string conversion.
112 # will be overridden in some cases
116 # default for printing to .json file is regular string conversion.
117 # will be overridden in some cases, mostly to use native Python
118 # types where there are similar JSON types
119 def config_value(self
):
122 # Prerequisites for .ini parsing with cxx_ini_parse
124 def cxx_ini_predecls(cls
, code
):
127 # parse a .ini file entry for this param from string expression
128 # src into lvalue dest (of the param's C++ type)
130 def cxx_ini_parse(cls
, code
, src
, dest
, ret
):
131 code('// Unhandled param type: %s' % cls
.__name
__)
132 code('%s false;' % ret
)
134 # allows us to blithely call unproxy() on things without checking
135 # if they're really proxies or not
136 def unproxy(self
, base
):
139 # Produce a human readable version of the stored value
140 def pretty_print(self
, value
):
143 # Regular parameter description.
144 class ParamDesc(object):
145 def __init__(self
, ptype_str
, ptype
, *args
, **kwargs
):
146 self
.ptype_str
= ptype_str
147 # remember ptype only if it is provided
155 self
.default
= args
[0]
158 raise TypeError('too many arguments')
160 if kwargs
.has_key('desc'):
161 assert(not hasattr(self
, 'desc'))
162 self
.desc
= kwargs
['desc']
165 if kwargs
.has_key('default'):
166 assert(not hasattr(self
, 'default'))
167 self
.default
= kwargs
['default']
168 del kwargs
['default']
171 raise TypeError('extra unknown kwargs %s' % kwargs
)
173 if not hasattr(self
, 'desc'):
174 raise TypeError('desc attribute missing')
176 def __getattr__(self
, attr
):
178 ptype
= SimObject
.allClasses
[self
.ptype_str
]
179 assert isSimObjectClass(ptype
)
183 raise AttributeError("'%s' object has no attribute '%s'" % \
184 (type(self
).__name
__, attr
))
186 def example_str(self
):
187 if hasattr(self
.ptype
, "ex_str"):
188 return self
.ptype
.ex_str
190 return self
.ptype_str
192 # Is the param available to be exposed on the command line
193 def isCmdLineSettable(self
):
194 if hasattr(self
.ptype
, "cmd_line_settable"):
195 return self
.ptype
.cmd_line_settable
199 def convert(self
, value
):
200 if isinstance(value
, proxy
.BaseProxy
):
201 value
.set_param_desc(self
)
203 if not hasattr(self
, 'ptype') and isNullPointer(value
):
204 # deferred evaluation of SimObject; continue to defer if
205 # we're just assigning a null pointer
207 if isinstance(value
, self
.ptype
):
209 if isNullPointer(value
) and isSimObjectClass(self
.ptype
):
211 return self
.ptype(value
)
213 def pretty_print(self
, value
):
214 if isinstance(value
, proxy
.BaseProxy
):
216 if isNullPointer(value
):
218 return self
.ptype(value
).pretty_print(value
)
220 def cxx_predecls(self
, code
):
221 code('#include <cstddef>')
222 self
.ptype
.cxx_predecls(code
)
224 def pybind_predecls(self
, code
):
225 self
.ptype
.pybind_predecls(code
)
227 def cxx_decl(self
, code
):
228 code('${{self.ptype.cxx_type}} ${{self.name}};')
230 # Vector-valued parameter description. Just like ParamDesc, except
231 # that the value is a vector (list) of the specified type instead of a
234 class VectorParamValue(list):
235 __metaclass__
= MetaParamValue
236 def __setattr__(self
, attr
, value
):
237 raise AttributeError("Not allowed to set %s on '%s'" % \
238 (attr
, type(self
).__name
__))
240 def config_value(self
):
241 return [v
.config_value() for v
in self
]
244 return ' '.join([v
.ini_str() for v
in self
])
247 return [ v
.getValue() for v
in self
]
249 def unproxy(self
, base
):
250 if len(self
) == 1 and isinstance(self
[0], proxy
.BaseProxy
):
251 # The value is a proxy (e.g. Parent.any, Parent.all or
252 # Parent.x) therefore try resolve it
253 return self
[0].unproxy(base
)
255 return [v
.unproxy(base
) for v
in self
]
257 class SimObjectVector(VectorParamValue
):
258 # support clone operation
259 def __call__(self
, **kwargs
):
260 return SimObjectVector([v(**kwargs
) for v
in self
])
262 def clear_parent(self
, old_parent
):
264 v
.clear_parent(old_parent
)
266 def set_parent(self
, parent
, name
):
268 self
[0].set_parent(parent
, name
)
270 width
= int(math
.ceil(math
.log(len(self
))/math
.log(10)))
271 for i
,v
in enumerate(self
):
272 v
.set_parent(parent
, "%s%0*d" % (name
, width
, i
))
274 def has_parent(self
):
275 return any([e
.has_parent() for e
in self
if not isNullPointer(e
)])
277 # return 'cpu0 cpu1' etc. for print_ini()
279 return ' '.join([v
._name
for v
in self
])
281 # By iterating through the constituent members of the vector here
282 # we can nicely handle iterating over all a SimObject's children
283 # without having to provide lots of special functions on
284 # SimObjectVector directly.
285 def descendants(self
):
287 for obj
in v
.descendants():
290 def get_config_as_dict(self
):
293 a
.append(v
.get_config_as_dict())
296 # If we are replacing an item in the vector, make sure to set the
297 # parent reference of the new SimObject to be the same as the parent
298 # of the SimObject being replaced. Useful to have if we created
299 # a SimObjectVector of temporary objects that will be modified later in
300 # configuration scripts.
301 def __setitem__(self
, key
, value
):
303 if value
.has_parent():
304 warn("SimObject %s already has a parent" % value
.get_name() +\
305 " that is being overwritten by a SimObjectVector")
306 value
.set_parent(val
.get_parent(), val
._name
)
307 super(SimObjectVector
, self
).__setitem
__(key
, value
)
309 # Enumerate the params of each member of the SimObject vector. Creates
310 # strings that will allow indexing into the vector by the python code and
311 # allow it to be specified on the command line.
312 def enumerateParams(self
, flags_dict
= {},
315 if hasattr(self
, "_paramEnumed"):
316 print("Cycle detected enumerating params at %s?!" % (cmd_line_str
))
320 # Each entry in the SimObjectVector should be an
321 # instance of a SimObject
322 flags_dict
= vals
.enumerateParams(flags_dict
,
323 cmd_line_str
+ "%d." % x
,
324 access_str
+ "[%d]." % x
)
329 class VectorParamDesc(ParamDesc
):
330 # Convert assigned value to appropriate type. If the RHS is not a
331 # list or tuple, it generates a single-element list.
332 def convert(self
, value
):
333 if isinstance(value
, (list, tuple)):
334 # list: coerce each element into new list
335 tmp_list
= [ ParamDesc
.convert(self
, v
) for v
in value
]
336 elif isinstance(value
, str):
337 # If input is a csv string
338 tmp_list
= [ ParamDesc
.convert(self
, v
) \
339 for v
in value
.strip('[').strip(']').split(',') ]
341 # singleton: coerce to a single-element list
342 tmp_list
= [ ParamDesc
.convert(self
, value
) ]
344 if isSimObjectSequence(tmp_list
):
345 return SimObjectVector(tmp_list
)
347 return VectorParamValue(tmp_list
)
349 # Produce a human readable example string that describes
350 # how to set this vector parameter in the absence of a default
352 def example_str(self
):
353 s
= super(VectorParamDesc
, self
).example_str()
354 help_str
= "[" + s
+ "," + s
+ ", ...]"
357 # Produce a human readable representation of the value of this vector param.
358 def pretty_print(self
, value
):
359 if isinstance(value
, (list, tuple)):
360 tmp_list
= [ ParamDesc
.pretty_print(self
, v
) for v
in value
]
361 elif isinstance(value
, str):
362 tmp_list
= [ ParamDesc
.pretty_print(self
, v
) for v
in value
.split(',') ]
364 tmp_list
= [ ParamDesc
.pretty_print(self
, value
) ]
368 # This is a helper function for the new config system
369 def __call__(self
, value
):
370 if isinstance(value
, (list, tuple)):
371 # list: coerce each element into new list
372 tmp_list
= [ ParamDesc
.convert(self
, v
) for v
in value
]
373 elif isinstance(value
, str):
374 # If input is a csv string
375 tmp_list
= [ ParamDesc
.convert(self
, v
) \
376 for v
in value
.strip('[').strip(']').split(',') ]
378 # singleton: coerce to a single-element list
379 tmp_list
= [ ParamDesc
.convert(self
, value
) ]
381 return VectorParamValue(tmp_list
)
383 def cxx_predecls(self
, code
):
384 code('#include <vector>')
385 self
.ptype
.cxx_predecls(code
)
387 def pybind_predecls(self
, code
):
388 code('#include <vector>')
389 self
.ptype
.pybind_predecls(code
)
391 def cxx_decl(self
, code
):
392 code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
394 class ParamFactory(object):
395 def __init__(self
, param_desc_class
, ptype_str
= None):
396 self
.param_desc_class
= param_desc_class
397 self
.ptype_str
= ptype_str
399 def __getattr__(self
, attr
):
401 attr
= self
.ptype_str
+ '.' + attr
402 return ParamFactory(self
.param_desc_class
, attr
)
404 # E.g., Param.Int(5, "number of widgets")
405 def __call__(self
, *args
, **kwargs
):
408 ptype
= allParams
[self
.ptype_str
]
410 # if name isn't defined yet, assume it's a SimObject, and
411 # try to resolve it later
413 return self
.param_desc_class(self
.ptype_str
, ptype
, *args
, **kwargs
)
415 Param
= ParamFactory(ParamDesc
)
416 VectorParam
= ParamFactory(VectorParamDesc
)
418 #####################################################################
422 # Though native Python types could be used to specify parameter types
423 # (the 'ptype' field of the Param and VectorParam classes), it's more
424 # flexible to define our own set of types. This gives us more control
425 # over how Python expressions are converted to values (via the
426 # __init__() constructor) and how these values are printed out (via
427 # the __str__() conversion method).
429 #####################################################################
431 # String-valued parameter. Just mixin the ParamValue class with the
432 # built-in str class.
433 class String(ParamValue
,str):
434 cxx_type
= 'std::string'
435 cmd_line_settable
= True
438 def cxx_predecls(self
, code
):
439 code('#include <string>')
441 def __call__(self
, value
):
446 def cxx_ini_parse(self
, code
, src
, dest
, ret
):
447 code('%s = %s;' % (dest
, src
))
448 code('%s true;' % ret
)
453 # superclass for "numeric" parameter values, to emulate math
454 # operations in a type-safe way. e.g., a Latency times an int returns
455 # a new Latency object.
456 class NumericParamValue(ParamValue
):
458 return str(self
.value
)
461 return float(self
.value
)
464 return long(self
.value
)
467 return int(self
.value
)
469 # hook for bounds checking
473 def __mul__(self
, other
):
474 newobj
= self
.__class
__(self
)
475 newobj
.value
*= other
481 def __div__(self
, other
):
482 newobj
= self
.__class
__(self
)
483 newobj
.value
/= other
487 def __sub__(self
, other
):
488 newobj
= self
.__class
__(self
)
489 newobj
.value
-= other
493 def config_value(self
):
497 def cxx_ini_predecls(cls
, code
):
498 # Assume that base/str.hh will be included anyway
499 # code('#include "base/str.hh"')
502 # The default for parsing PODs from an .ini entry is to extract from an
503 # istringstream and let overloading choose the right type according to
506 def cxx_ini_parse(self
, code
, src
, dest
, ret
):
507 code('%s to_number(%s, %s);' % (ret
, src
, dest
))
509 # Metaclass for bounds-checked integer parameters. See CheckedInt.
510 class CheckedIntType(MetaParamValue
):
511 def __init__(cls
, name
, bases
, dict):
512 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
514 # CheckedInt is an abstract base class, so we actually don't
515 # want to do any processing on it... the rest of this code is
516 # just for classes that derive from CheckedInt.
517 if name
== 'CheckedInt':
520 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
521 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
522 panic("CheckedInt subclass %s must define either\n" \
523 " 'min' and 'max' or 'size' and 'unsigned'\n",
527 cls
.max = 2 ** cls
.size
- 1
529 cls
.min = -(2 ** (cls
.size
- 1))
530 cls
.max = (2 ** (cls
.size
- 1)) - 1
532 # Abstract superclass for bounds-checked integer parameters. This
533 # class is subclassed to generate parameter classes with specific
534 # bounds. Initialization of the min and max bounds is done in the
535 # metaclass CheckedIntType.__init__.
536 class CheckedInt(NumericParamValue
):
537 __metaclass__
= CheckedIntType
538 cmd_line_settable
= True
541 if not self
.min <= self
.value
<= self
.max:
542 raise TypeError('Integer param out of bounds %d < %d < %d' % \
543 (self
.min, self
.value
, self
.max))
545 def __init__(self
, value
):
546 if isinstance(value
, str):
547 self
.value
= convert
.toInteger(value
)
548 elif isinstance(value
, (int, long, float, NumericParamValue
)):
549 self
.value
= long(value
)
551 raise TypeError("Can't convert object of type %s to CheckedInt" \
552 % type(value
).__name
__)
555 def __call__(self
, value
):
560 def cxx_predecls(cls
, code
):
561 # most derived types require this, so we just do it here once
562 code('#include "base/types.hh"')
565 return long(self
.value
)
567 class Int(CheckedInt
): cxx_type
= 'int'; size
= 32; unsigned
= False
568 class Unsigned(CheckedInt
): cxx_type
= 'unsigned'; size
= 32; unsigned
= True
570 class Int8(CheckedInt
): cxx_type
= 'int8_t'; size
= 8; unsigned
= False
571 class UInt8(CheckedInt
): cxx_type
= 'uint8_t'; size
= 8; unsigned
= True
572 class Int16(CheckedInt
): cxx_type
= 'int16_t'; size
= 16; unsigned
= False
573 class UInt16(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
574 class Int32(CheckedInt
): cxx_type
= 'int32_t'; size
= 32; unsigned
= False
575 class UInt32(CheckedInt
): cxx_type
= 'uint32_t'; size
= 32; unsigned
= True
576 class Int64(CheckedInt
): cxx_type
= 'int64_t'; size
= 64; unsigned
= False
577 class UInt64(CheckedInt
): cxx_type
= 'uint64_t'; size
= 64; unsigned
= True
579 class Counter(CheckedInt
): cxx_type
= 'Counter'; size
= 64; unsigned
= True
580 class Tick(CheckedInt
): cxx_type
= 'Tick'; size
= 64; unsigned
= True
581 class TcpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
582 class UdpPort(CheckedInt
): cxx_type
= 'uint16_t'; size
= 16; unsigned
= True
584 class Percent(CheckedInt
): cxx_type
= 'int'; min = 0; max = 100
586 class Cycles(CheckedInt
):
592 from _m5
.core
import Cycles
593 return Cycles(self
.value
)
596 def cxx_ini_predecls(cls
, code
):
597 # Assume that base/str.hh will be included anyway
598 # code('#include "base/str.hh"')
602 def cxx_ini_parse(cls
, code
, src
, dest
, ret
):
603 code('uint64_t _temp;')
604 code('bool _ret = to_number(%s, _temp);' % src
)
606 code(' %s = Cycles(_temp);' % dest
)
607 code('%s _ret;' % ret
)
609 class Float(ParamValue
, float):
611 cmd_line_settable
= True
613 def __init__(self
, value
):
614 if isinstance(value
, (int, long, float, NumericParamValue
, Float
, str)):
615 self
.value
= float(value
)
617 raise TypeError("Can't convert object of type %s to Float" \
618 % type(value
).__name
__)
620 def __call__(self
, value
):
625 return float(self
.value
)
627 def config_value(self
):
631 def cxx_ini_predecls(cls
, code
):
632 code('#include <sstream>')
635 def cxx_ini_parse(self
, code
, src
, dest
, ret
):
636 code('%s (std::istringstream(%s) >> %s).eof();' % (ret
, src
, dest
))
638 class MemorySize(CheckedInt
):
639 cxx_type
= 'uint64_t'
643 def __init__(self
, value
):
644 if isinstance(value
, MemorySize
):
645 self
.value
= value
.value
647 self
.value
= convert
.toMemorySize(value
)
650 class MemorySize32(CheckedInt
):
651 cxx_type
= 'uint32_t'
655 def __init__(self
, value
):
656 if isinstance(value
, MemorySize
):
657 self
.value
= value
.value
659 self
.value
= convert
.toMemorySize(value
)
662 class Addr(CheckedInt
):
666 def __init__(self
, value
):
667 if isinstance(value
, Addr
):
668 self
.value
= value
.value
671 # Often addresses are referred to with sizes. Ex: A device
672 # base address is at "512MB". Use toMemorySize() to convert
673 # these into addresses. If the address is not specified with a
674 # "size", an exception will occur and numeric translation will
676 self
.value
= convert
.toMemorySize(value
)
677 except (TypeError, ValueError):
678 # Convert number to string and use long() to do automatic
679 # base conversion (requires base=0 for auto-conversion)
680 self
.value
= long(str(value
), base
=0)
683 def __add__(self
, other
):
684 if isinstance(other
, Addr
):
685 return self
.value
+ other
.value
687 return self
.value
+ other
688 def pretty_print(self
, value
):
690 val
= convert
.toMemorySize(value
)
693 return "0x%x" % long(val
)
695 class AddrRange(ParamValue
):
696 cxx_type
= 'AddrRange'
698 def __init__(self
, *args
, **kwargs
):
699 # Disable interleaving and hashing by default
700 self
.intlvHighBit
= 0
705 def handle_kwargs(self
, kwargs
):
706 # An address range needs to have an upper limit, specified
707 # either explicitly with an end, or as an offset using the
710 self
.end
= Addr(kwargs
.pop('end'))
711 elif 'size' in kwargs
:
712 self
.end
= self
.start
+ Addr(kwargs
.pop('size')) - 1
714 raise TypeError("Either end or size must be specified")
716 # Now on to the optional bit
717 if 'intlvHighBit' in kwargs
:
718 self
.intlvHighBit
= int(kwargs
.pop('intlvHighBit'))
719 if 'xorHighBit' in kwargs
:
720 self
.xorHighBit
= int(kwargs
.pop('xorHighBit'))
721 if 'intlvBits' in kwargs
:
722 self
.intlvBits
= int(kwargs
.pop('intlvBits'))
723 if 'intlvMatch' in kwargs
:
724 self
.intlvMatch
= int(kwargs
.pop('intlvMatch'))
727 self
.start
= Addr(kwargs
.pop('start'))
728 handle_kwargs(self
, kwargs
)
732 self
.start
= Addr(args
[0])
733 handle_kwargs(self
, kwargs
)
734 elif isinstance(args
[0], (list, tuple)):
735 self
.start
= Addr(args
[0][0])
736 self
.end
= Addr(args
[0][1])
739 self
.end
= Addr(args
[0]) - 1
742 self
.start
= Addr(args
[0])
743 self
.end
= Addr(args
[1])
745 raise TypeError("Too many arguments specified")
748 raise TypeError("Too many keywords: %s" % list(kwargs
.keys()))
751 return '%s:%s:%s:%s:%s:%s' \
752 % (self
.start
, self
.end
, self
.intlvHighBit
, self
.xorHighBit
,\
753 self
.intlvBits
, self
.intlvMatch
)
756 # Divide the size by the size of the interleaving slice
757 return (long(self
.end
) - long(self
.start
) + 1) >> self
.intlvBits
760 def cxx_predecls(cls
, code
):
761 Addr
.cxx_predecls(code
)
762 code('#include "base/addr_range.hh"')
765 def pybind_predecls(cls
, code
):
766 Addr
.pybind_predecls(code
)
767 code('#include "base/addr_range.hh"')
770 def cxx_ini_predecls(cls
, code
):
771 code('#include <sstream>')
774 def cxx_ini_parse(cls
, code
, src
, dest
, ret
):
775 code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;')
776 code('uint64_t _intlvBits = 0, _intlvMatch = 0;')
778 code('std::istringstream _stream(${src});')
779 code('_stream >> _start;')
780 code('_stream.get(_sep);')
781 code('_stream >> _end;')
782 code('if (!_stream.fail() && !_stream.eof()) {')
783 code(' _stream.get(_sep);')
784 code(' _stream >> _intlvHighBit;')
785 code(' _stream.get(_sep);')
786 code(' _stream >> _xorHighBit;')
787 code(' _stream.get(_sep);')
788 code(' _stream >> _intlvBits;')
789 code(' _stream.get(_sep);')
790 code(' _stream >> _intlvMatch;')
792 code('bool _ret = !_stream.fail() &&'
793 '_stream.eof() && _sep == \':\';')
795 code(' ${dest} = AddrRange(_start, _end, _intlvHighBit, \
796 _xorHighBit, _intlvBits, _intlvMatch);')
800 # Go from the Python class to the wrapped C++ class
801 from _m5
.range import AddrRange
803 return AddrRange(long(self
.start
), long(self
.end
),
804 int(self
.intlvHighBit
), int(self
.xorHighBit
),
805 int(self
.intlvBits
), int(self
.intlvMatch
))
807 # Boolean parameter type. Python doesn't let you subclass bool, since
808 # it doesn't want to let you create multiple instances of True and
809 # False. Thus this is a little more complicated than String.
810 class Bool(ParamValue
):
812 cmd_line_settable
= True
814 def __init__(self
, value
):
816 self
.value
= convert
.toBool(value
)
818 self
.value
= bool(value
)
820 def __call__(self
, value
):
825 return bool(self
.value
)
828 return str(self
.value
)
830 # implement truth value testing for Bool parameters so that these params
831 # evaluate correctly during the python configuration phase
832 def __nonzero__(self
):
833 return bool(self
.value
)
840 def config_value(self
):
844 def cxx_ini_predecls(cls
, code
):
845 # Assume that base/str.hh will be included anyway
846 # code('#include "base/str.hh"')
850 def cxx_ini_parse(cls
, code
, src
, dest
, ret
):
851 code('%s to_bool(%s, %s);' % (ret
, src
, dest
))
853 def IncEthernetAddr(addr
, val
= 1):
854 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
856 for i
in (5, 4, 3, 2, 1):
857 val
,rem
= divmod(bytes
[i
], 256)
862 assert(bytes
[0] <= 255)
863 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
865 _NextEthernetAddr
= "00:90:00:00:00:01"
866 def NextEthernetAddr():
867 global _NextEthernetAddr
869 value
= _NextEthernetAddr
870 _NextEthernetAddr
= IncEthernetAddr(_NextEthernetAddr
, 1)
873 class EthernetAddr(ParamValue
):
874 cxx_type
= 'Net::EthAddr'
875 ex_str
= "00:90:00:00:00:01"
876 cmd_line_settable
= True
879 def cxx_predecls(cls
, code
):
880 code('#include "base/inet.hh"')
882 def __init__(self
, value
):
883 if value
== NextEthernetAddr
:
887 if not isinstance(value
, str):
888 raise TypeError("expected an ethernet address and didn't get one")
890 bytes
= value
.split(':')
892 raise TypeError('invalid ethernet address %s' % value
)
895 if not 0 <= int(byte
, base
=16) <= 0xff:
896 raise TypeError('invalid ethernet address %s' % value
)
900 def __call__(self
, value
):
904 def unproxy(self
, base
):
905 if self
.value
== NextEthernetAddr
:
906 return EthernetAddr(self
.value())
910 from _m5
.net
import EthAddr
911 return EthAddr(self
.value
)
920 def cxx_ini_parse(self
, code
, src
, dest
, ret
):
921 code('%s = Net::EthAddr(%s);' % (dest
, src
))
922 code('%s true;' % ret
)
924 # When initializing an IpAddress, pass in an existing IpAddress, a string of
925 # the form "a.b.c.d", or an integer representing an IP.
926 class IpAddress(ParamValue
):
927 cxx_type
= 'Net::IpAddress'
929 cmd_line_settable
= True
932 def cxx_predecls(cls
, code
):
933 code('#include "base/inet.hh"')
935 def __init__(self
, value
):
936 if isinstance(value
, IpAddress
):
940 self
.ip
= convert
.toIpAddress(value
)
942 self
.ip
= long(value
)
945 def __call__(self
, value
):
950 tup
= [(self
.ip
>> i
) & 0xff for i
in (24, 16, 8, 0)]
951 return '%d.%d.%d.%d' % tuple(tup
)
953 def __eq__(self
, other
):
954 if isinstance(other
, IpAddress
):
955 return self
.ip
== other
.ip
956 elif isinstance(other
, str):
958 return self
.ip
== convert
.toIpAddress(other
)
962 return self
.ip
== other
964 def __ne__(self
, other
):
965 return not (self
== other
)
968 if self
.ip
< 0 or self
.ip
>= (1 << 32):
969 raise TypeError("invalid ip address %#08x" % self
.ip
)
972 from _m5
.net
import IpAddress
973 return IpAddress(self
.ip
)
975 # When initializing an IpNetmask, pass in an existing IpNetmask, a string of
976 # the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
977 # positional or keyword arguments.
978 class IpNetmask(IpAddress
):
979 cxx_type
= 'Net::IpNetmask'
980 ex_str
= "127.0.0.0/24"
981 cmd_line_settable
= True
984 def cxx_predecls(cls
, code
):
985 code('#include "base/inet.hh"')
987 def __init__(self
, *args
, **kwargs
):
988 def handle_kwarg(self
, kwargs
, key
, elseVal
= None):
990 setattr(self
, key
, kwargs
.pop(key
))
992 setattr(self
, key
, elseVal
)
994 raise TypeError("No value set for %s" % key
)
997 handle_kwarg(self
, kwargs
, 'ip')
998 handle_kwarg(self
, kwargs
, 'netmask')
1000 elif len(args
) == 1:
1002 if not 'ip' in kwargs
and not 'netmask' in kwargs
:
1003 raise TypeError("Invalid arguments")
1004 handle_kwarg(self
, kwargs
, 'ip', args
[0])
1005 handle_kwarg(self
, kwargs
, 'netmask', args
[0])
1006 elif isinstance(args
[0], IpNetmask
):
1007 self
.ip
= args
[0].ip
1008 self
.netmask
= args
[0].netmask
1010 (self
.ip
, self
.netmask
) = convert
.toIpNetmask(args
[0])
1012 elif len(args
) == 2:
1014 self
.netmask
= args
[1]
1016 raise TypeError("Too many arguments specified")
1019 raise TypeError("Too many keywords: %s" % list(kwargs
.keys()))
1023 def __call__(self
, value
):
1024 self
.__init
__(value
)
1028 return "%s/%d" % (super(IpNetmask
, self
).__str
__(), self
.netmask
)
1030 def __eq__(self
, other
):
1031 if isinstance(other
, IpNetmask
):
1032 return self
.ip
== other
.ip
and self
.netmask
== other
.netmask
1033 elif isinstance(other
, str):
1035 return (self
.ip
, self
.netmask
) == convert
.toIpNetmask(other
)
1043 if self
.netmask
< 0 or self
.netmask
> 32:
1044 raise TypeError("invalid netmask %d" % netmask
)
1047 from _m5
.net
import IpNetmask
1048 return IpNetmask(self
.ip
, self
.netmask
)
1050 # When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1051 # the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1052 class IpWithPort(IpAddress
):
1053 cxx_type
= 'Net::IpWithPort'
1054 ex_str
= "127.0.0.1:80"
1055 cmd_line_settable
= True
1058 def cxx_predecls(cls
, code
):
1059 code('#include "base/inet.hh"')
1061 def __init__(self
, *args
, **kwargs
):
1062 def handle_kwarg(self
, kwargs
, key
, elseVal
= None):
1064 setattr(self
, key
, kwargs
.pop(key
))
1066 setattr(self
, key
, elseVal
)
1068 raise TypeError("No value set for %s" % key
)
1071 handle_kwarg(self
, kwargs
, 'ip')
1072 handle_kwarg(self
, kwargs
, 'port')
1074 elif len(args
) == 1:
1076 if not 'ip' in kwargs
and not 'port' in kwargs
:
1077 raise TypeError("Invalid arguments")
1078 handle_kwarg(self
, kwargs
, 'ip', args
[0])
1079 handle_kwarg(self
, kwargs
, 'port', args
[0])
1080 elif isinstance(args
[0], IpWithPort
):
1081 self
.ip
= args
[0].ip
1082 self
.port
= args
[0].port
1084 (self
.ip
, self
.port
) = convert
.toIpWithPort(args
[0])
1086 elif len(args
) == 2:
1090 raise TypeError("Too many arguments specified")
1093 raise TypeError("Too many keywords: %s" % list(kwargs
.keys()))
1097 def __call__(self
, value
):
1098 self
.__init
__(value
)
1102 return "%s:%d" % (super(IpWithPort
, self
).__str
__(), self
.port
)
1104 def __eq__(self
, other
):
1105 if isinstance(other
, IpWithPort
):
1106 return self
.ip
== other
.ip
and self
.port
== other
.port
1107 elif isinstance(other
, str):
1109 return (self
.ip
, self
.port
) == convert
.toIpWithPort(other
)
1117 if self
.port
< 0 or self
.port
> 0xffff:
1118 raise TypeError("invalid port %d" % self
.port
)
1121 from _m5
.net
import IpWithPort
1122 return IpWithPort(self
.ip
, self
.port
)
1124 time_formats
= [ "%a %b %d %H:%M:%S %Z %Y",
1125 "%a %b %d %H:%M:%S %Y",
1126 "%Y/%m/%d %H:%M:%S",
1129 "%m/%d/%Y %H:%M:%S",
1132 "%m/%d/%y %H:%M:%S",
1137 def parse_time(value
):
1138 from time
import gmtime
, strptime
, struct_time
, time
1139 from datetime
import datetime
, date
1141 if isinstance(value
, struct_time
):
1144 if isinstance(value
, (int, long)):
1145 return gmtime(value
)
1147 if isinstance(value
, (datetime
, date
)):
1148 return value
.timetuple()
1150 if isinstance(value
, str):
1151 if value
in ('Now', 'Today'):
1152 return time
.gmtime(time
.time())
1154 for format
in time_formats
:
1156 return strptime(value
, format
)
1160 raise ValueError("Could not parse '%s' as a time" % value
)
1162 class Time(ParamValue
):
1166 def cxx_predecls(cls
, code
):
1167 code('#include <time.h>')
1169 def __init__(self
, value
):
1170 self
.value
= parse_time(value
)
1172 def __call__(self
, value
):
1173 self
.__init
__(value
)
1177 from _m5
.core
import tm
1180 return tm
.gmtime(calendar
.timegm(self
.value
))
1183 return time
.asctime(self
.value
)
1188 def get_config_as_dict(self
):
1193 def cxx_ini_predecls(cls
, code
):
1194 code('#include <time.h>')
1197 def cxx_ini_parse(cls
, code
, src
, dest
, ret
):
1198 code('char *_parse_ret = strptime((${src}).c_str(),')
1199 code(' "%a %b %d %H:%M:%S %Y", &(${dest}));')
1200 code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1202 # Enumerated types are a little more complex. The user specifies the
1203 # type as Enum(foo) where foo is either a list or dictionary of
1204 # alternatives (typically strings, but not necessarily so). (In the
1205 # long run, the integer value of the parameter will be the list index
1206 # or the corresponding dictionary value. For now, since we only check
1207 # that the alternative is valid and then spit it into a .ini file,
1208 # there's not much point in using the dictionary.)
1210 # What Enum() must do is generate a new type encapsulating the
1211 # provided list/dictionary so that specific values of the parameter
1212 # can be instances of that type. We define two hidden internal
1213 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1214 # derive the new type from the appropriate base class on the fly.
1217 # Metaclass for Enum types
1218 class MetaEnum(MetaParamValue
):
1219 def __new__(mcls
, name
, bases
, dict):
1220 assert name
not in allEnums
1222 cls
= super(MetaEnum
, mcls
).__new
__(mcls
, name
, bases
, dict)
1223 allEnums
[name
] = cls
1226 def __init__(cls
, name
, bases
, init_dict
):
1227 if init_dict
.has_key('map'):
1228 if not isinstance(cls
.map, dict):
1229 raise TypeError("Enum-derived class attribute 'map' " \
1230 "must be of type dict")
1231 # build list of value strings from map
1232 cls
.vals
= cls
.map.keys()
1234 elif init_dict
.has_key('vals'):
1235 if not isinstance(cls
.vals
, list):
1236 raise TypeError("Enum-derived class attribute 'vals' " \
1237 "must be of type list")
1238 # build string->value map from vals sequence
1240 for idx
,val
in enumerate(cls
.vals
):
1243 raise TypeError("Enum-derived class must define "\
1244 "attribute 'map' or 'vals'")
1247 cls
.cxx_type
= '%s' % name
1249 cls
.cxx_type
= 'Enums::%s' % name
1251 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
1253 # Generate C++ class declaration for this enum type.
1254 # Note that we wrap the enum in a class/struct to act as a namespace,
1255 # so that the enum strings can be brief w/o worrying about collisions.
1256 def cxx_decl(cls
, code
):
1257 wrapper_name
= cls
.wrapper_name
1258 wrapper
= 'struct' if cls
.wrapper_is_struct
else 'namespace'
1259 name
= cls
.__name
__ if cls
.enum_name
is None else cls
.enum_name
1260 idem_macro
= '__ENUM__%s__%s__' % (wrapper_name
, name
)
1273 $wrapper $wrapper_name {
1278 for val
in cls
.vals
:
1279 code('$val = ${{cls.map[val]}},')
1280 code('Num_$name = ${{len(cls.vals)}}')
1286 extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
1288 elif cls
.wrapper_is_struct
:
1289 code('static const char *${name}Strings[Num_${name}];')
1291 code('extern const char *${name}Strings[Num_${name}];')
1293 if not cls
.is_class
:
1298 code('#endif // $idem_macro')
1300 def cxx_def(cls
, code
):
1301 wrapper_name
= cls
.wrapper_name
1302 file_name
= cls
.__name
__
1303 name
= cls
.__name
__ if cls
.enum_name
is None else cls
.enum_name
1305 code('#include "enums/$file_name.hh"')
1306 if cls
.wrapper_is_struct
:
1307 code('const char *${wrapper_name}::${name}Strings'
1312 const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
1315 code('namespace Enums {')
1317 code('const char *${name}Strings[Num_${name}] =')
1321 for val
in cls
.vals
:
1326 if not cls
.wrapper_is_struct
and not cls
.is_class
:
1328 code('} // namespace $wrapper_name')
1331 def pybind_def(cls
, code
):
1333 enum_name
= cls
.__name
__ if cls
.enum_name
is None else cls
.enum_name
1334 wrapper_name
= enum_name
if cls
.is_class
else cls
.wrapper_name
1336 code('''#include "pybind11/pybind11.h"
1337 #include "pybind11/stl.h"
1339 #include <sim/init.hh>
1341 namespace py = pybind11;
1344 module_init(py::module &m_internal)
1346 py::module m = m_internal.def_submodule("enum_${name}");
1350 code('py::enum_<${enum_name}>(m, "enum_${name}")')
1352 code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
1356 for val
in cls
.vals
:
1357 code('.value("${val}", ${wrapper_name}::${val})')
1358 code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1359 code('.export_values()')
1366 code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1369 # Base class for enum types.
1370 class Enum(ParamValue
):
1371 __metaclass__
= MetaEnum
1373 cmd_line_settable
= True
1375 # The name of the wrapping namespace or struct
1376 wrapper_name
= 'Enums'
1378 # If true, the enum is wrapped in a struct rather than a namespace
1379 wrapper_is_struct
= False
1383 # If not None, use this as the enum name rather than this class name
1386 def __init__(self
, value
):
1387 if value
not in self
.map:
1388 raise TypeError("Enum param got bad value '%s' (not in %s)" \
1389 % (value
, self
.vals
))
1392 def __call__(self
, value
):
1393 self
.__init
__(value
)
1397 def cxx_predecls(cls
, code
):
1398 code('#include "enums/$0.hh"', cls
.__name
__)
1401 def cxx_ini_parse(cls
, code
, src
, dest
, ret
):
1402 code('if (false) {')
1403 for elem_name
in cls
.map.iterkeys():
1404 code('} else if (%s == "%s") {' % (src
, elem_name
))
1406 code('%s = Enums::%s;' % (dest
, elem_name
))
1407 code('%s true;' % ret
)
1410 code(' %s false;' % ret
)
1414 import m5
.internal
.params
1415 e
= getattr(m5
.internal
.params
, "enum_%s" % self
.__class
__.__name
__)
1416 return e(self
.map[self
.value
])
1421 # This param will generate a scoped c++ enum and its python bindings.
1422 class ScopedEnum(Enum
):
1423 __metaclass__
= MetaEnum
1425 cmd_line_settable
= True
1427 # The name of the wrapping namespace or struct
1430 # If true, the enum is wrapped in a struct rather than a namespace
1431 wrapper_is_struct
= False
1433 # If true, the generated enum is a scoped enum
1436 # If not None, use this as the enum name rather than this class name
1439 # how big does a rounding error need to be before we warn about it?
1440 frequency_tolerance
= 0.001 # 0.1%
1442 class TickParamValue(NumericParamValue
):
1445 cmd_line_settable
= True
1448 def cxx_predecls(cls
, code
):
1449 code('#include "base/types.hh"')
1451 def __call__(self
, value
):
1452 self
.__init
__(value
)
1456 return long(self
.value
)
1459 def cxx_ini_predecls(cls
, code
):
1460 code('#include <sstream>')
1462 # Ticks are expressed in seconds in JSON files and in plain
1463 # Ticks in .ini files. Switch based on a config flag
1465 def cxx_ini_parse(self
, code
, src
, dest
, ret
):
1466 code('${ret} to_number(${src}, ${dest});')
1468 class Latency(TickParamValue
):
1471 def __init__(self
, value
):
1472 if isinstance(value
, (Latency
, Clock
)):
1473 self
.ticks
= value
.ticks
1474 self
.value
= value
.value
1475 elif isinstance(value
, Frequency
):
1476 self
.ticks
= value
.ticks
1477 self
.value
= 1.0 / value
.value
1478 elif value
.endswith('t'):
1480 self
.value
= int(value
[:-1])
1483 self
.value
= convert
.toLatency(value
)
1485 def __call__(self
, value
):
1486 self
.__init
__(value
)
1489 def __getattr__(self
, attr
):
1490 if attr
in ('latency', 'period'):
1492 if attr
== 'frequency':
1493 return Frequency(self
)
1494 raise AttributeError("Latency object has no attribute '%s'" % attr
)
1497 if self
.ticks
or self
.value
== 0:
1500 value
= ticks
.fromSeconds(self
.value
)
1503 def config_value(self
):
1504 return self
.getValue()
1506 # convert latency to ticks
1508 return '%d' % self
.getValue()
1510 class Frequency(TickParamValue
):
1513 def __init__(self
, value
):
1514 if isinstance(value
, (Latency
, Clock
)):
1515 if value
.value
== 0:
1518 self
.value
= 1.0 / value
.value
1519 self
.ticks
= value
.ticks
1520 elif isinstance(value
, Frequency
):
1521 self
.value
= value
.value
1522 self
.ticks
= value
.ticks
1525 self
.value
= convert
.toFrequency(value
)
1527 def __call__(self
, value
):
1528 self
.__init
__(value
)
1531 def __getattr__(self
, attr
):
1532 if attr
== 'frequency':
1534 if attr
in ('latency', 'period'):
1535 return Latency(self
)
1536 raise AttributeError("Frequency object has no attribute '%s'" % attr
)
1538 # convert latency to ticks
1540 if self
.ticks
or self
.value
== 0:
1543 value
= ticks
.fromSeconds(1.0 / self
.value
)
1546 def config_value(self
):
1547 return self
.getValue()
1550 return '%d' % self
.getValue()
1552 # A generic Frequency and/or Latency value. Value is stored as a
1553 # latency, just like Latency and Frequency.
1554 class Clock(TickParamValue
):
1555 def __init__(self
, value
):
1556 if isinstance(value
, (Latency
, Clock
)):
1557 self
.ticks
= value
.ticks
1558 self
.value
= value
.value
1559 elif isinstance(value
, Frequency
):
1560 self
.ticks
= value
.ticks
1561 self
.value
= 1.0 / value
.value
1562 elif value
.endswith('t'):
1564 self
.value
= int(value
[:-1])
1567 self
.value
= convert
.anyToLatency(value
)
1569 def __call__(self
, value
):
1570 self
.__init
__(value
)
1574 return "%s" % Latency(self
)
1576 def __getattr__(self
, attr
):
1577 if attr
== 'frequency':
1578 return Frequency(self
)
1579 if attr
in ('latency', 'period'):
1580 return Latency(self
)
1581 raise AttributeError("Frequency object has no attribute '%s'" % attr
)
1584 return self
.period
.getValue()
1586 def config_value(self
):
1587 return self
.period
.config_value()
1590 return self
.period
.ini_str()
1592 class Voltage(Float
):
1595 def __new__(cls
, value
):
1596 value
= convert
.toVoltage(value
)
1597 return super(cls
, Voltage
).__new
__(cls
, value
)
1599 def __init__(self
, value
):
1600 value
= convert
.toVoltage(value
)
1601 super(Voltage
, self
).__init
__(value
)
1603 class Current(Float
):
1606 def __new__(cls
, value
):
1607 value
= convert
.toCurrent(value
)
1608 return super(cls
, Current
).__new
__(cls
, value
)
1610 def __init__(self
, value
):
1611 value
= convert
.toCurrent(value
)
1612 super(Current
, self
).__init
__(value
)
1614 class Energy(Float
):
1617 def __new__(cls
, value
):
1618 value
= convert
.toEnergy(value
)
1619 return super(cls
, Energy
).__new
__(cls
, value
)
1621 def __init__(self
, value
):
1622 value
= convert
.toEnergy(value
)
1623 super(Energy
, self
).__init
__(value
)
1625 class NetworkBandwidth(float,ParamValue
):
1628 cmd_line_settable
= True
1630 def __new__(cls
, value
):
1631 # convert to bits per second
1632 val
= convert
.toNetworkBandwidth(value
)
1633 return super(cls
, NetworkBandwidth
).__new
__(cls
, val
)
1636 return str(self
.val
)
1638 def __call__(self
, value
):
1639 val
= convert
.toNetworkBandwidth(value
)
1644 # convert to seconds per byte
1645 value
= 8.0 / float(self
)
1646 # convert to ticks per byte
1647 value
= ticks
.fromSeconds(value
)
1651 return '%f' % self
.getValue()
1653 def config_value(self
):
1654 return '%f' % self
.getValue()
1657 def cxx_ini_predecls(cls
, code
):
1658 code('#include <sstream>')
1661 def cxx_ini_parse(self
, code
, src
, dest
, ret
):
1662 code('%s (std::istringstream(%s) >> %s).eof();' % (ret
, src
, dest
))
1664 class MemoryBandwidth(float,ParamValue
):
1667 cmd_line_settable
= True
1669 def __new__(cls
, value
):
1670 # convert to bytes per second
1671 val
= convert
.toMemoryBandwidth(value
)
1672 return super(cls
, MemoryBandwidth
).__new
__(cls
, val
)
1674 def __call__(self
, value
):
1675 val
= convert
.toMemoryBandwidth(value
)
1680 # convert to seconds per byte
1683 value
= 1.0 / float(self
)
1684 # convert to ticks per byte
1685 value
= ticks
.fromSeconds(value
)
1689 return '%f' % self
.getValue()
1691 def config_value(self
):
1692 return '%f' % self
.getValue()
1695 def cxx_ini_predecls(cls
, code
):
1696 code('#include <sstream>')
1699 def cxx_ini_parse(self
, code
, src
, dest
, ret
):
1700 code('%s (std::istringstream(%s) >> %s).eof();' % (ret
, src
, dest
))
1703 # "Constants"... handy aliases for various values.
1706 # Special class for NULL pointers. Note the special check in
1707 # make_param_value() above that lets these be assigned where a
1708 # SimObject is required.
1709 # only one copy of a particular node
1710 class NullSimObject(object):
1711 __metaclass__
= Singleton
1717 def _instantiate(self
, parent
= None, path
= ''):
1723 def unproxy(self
, base
):
1726 def set_path(self
, parent
, name
):
1729 def set_parent(self
, parent
, name
):
1732 def clear_parent(self
, old_parent
):
1735 def descendants(self
):
1739 def get_config_as_dict(self
):
1745 def config_value(self
):
1751 # The only instance you'll ever need...
1752 NULL
= NullSimObject()
1754 def isNullPointer(value
):
1755 return isinstance(value
, NullSimObject
)
1757 # Some memory range specifications use this as a default upper bound.
1760 AllMemory
= AddrRange(0, MaxAddr
)
1763 #####################################################################
1767 # Ports are used to interconnect objects in the memory system.
1769 #####################################################################
1771 # Port reference: encapsulates a reference to a particular port on a
1772 # particular SimObject.
1773 class PortRef(object):
1774 def __init__(self
, simobj
, name
, role
):
1775 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
1776 self
.simobj
= simobj
1779 self
.peer
= None # not associated with another port yet
1780 self
.ccConnected
= False # C++ port connection done?
1781 self
.index
= -1 # always -1 for non-vector ports
1784 return '%s.%s' % (self
.simobj
, self
.name
)
1787 # Return the number of connected ports, i.e. 0 is we have no
1788 # peer and 1 if we do.
1789 return int(self
.peer
!= None)
1791 # for config.ini, print peer's name (not ours)
1793 return str(self
.peer
)
1796 def get_config_as_dict(self
):
1797 return {'role' : self
.role
, 'peer' : str(self
.peer
)}
1799 def __getattr__(self
, attr
):
1800 if attr
== 'peerObj':
1801 # shorthand for proxies
1802 return self
.peer
.simobj
1803 raise AttributeError("'%s' object has no attribute '%s'" % \
1804 (self
.__class
__.__name
__, attr
))
1806 # Full connection is symmetric (both ways). Called via
1807 # SimObject.__setattr__ as a result of a port assignment, e.g.,
1808 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1809 # e.g., "obj1.portA[3] = obj2.portB".
1810 def connect(self
, other
):
1811 if isinstance(other
, VectorPortRef
):
1812 # reference to plain VectorPort is implicit append
1813 other
= other
._get
_next
()
1814 if self
.peer
and not proxy
.isproxy(self
.peer
):
1815 fatal("Port %s is already connected to %s, cannot connect %s\n",
1816 self
, self
.peer
, other
);
1818 if proxy
.isproxy(other
):
1819 other
.set_param_desc(PortParamDesc())
1820 elif isinstance(other
, PortRef
):
1821 if other
.peer
is not self
:
1824 raise TypeError("assigning non-port reference '%s' to port '%s'" \
1827 # Allow a master/slave port pair to be spliced between
1828 # a port and its connected peer. Useful operation for connecting
1829 # instrumentation structures into a system when it is necessary
1830 # to connect the instrumentation after the full system has been
1832 def splice(self
, new_master_peer
, new_slave_peer
):
1833 if not self
.peer
or proxy
.isproxy(self
.peer
):
1834 fatal("Port %s not connected, cannot splice in new peers\n", self
)
1836 if not isinstance(new_master_peer
, PortRef
) or \
1837 not isinstance(new_slave_peer
, PortRef
):
1839 "Splicing non-port references '%s','%s' to port '%s'" % \
1840 (new_master_peer
, new_slave_peer
, self
))
1842 old_peer
= self
.peer
1843 if self
.role
== 'SLAVE':
1844 self
.peer
= new_master_peer
1845 old_peer
.peer
= new_slave_peer
1846 new_master_peer
.connect(self
)
1847 new_slave_peer
.connect(old_peer
)
1848 elif self
.role
== 'MASTER':
1849 self
.peer
= new_slave_peer
1850 old_peer
.peer
= new_master_peer
1851 new_slave_peer
.connect(self
)
1852 new_master_peer
.connect(old_peer
)
1854 panic("Port %s has unknown role, "+\
1855 "cannot splice in new peers\n", self
)
1857 def clone(self
, simobj
, memo
):
1858 if memo
.has_key(self
):
1860 newRef
= copy
.copy(self
)
1862 newRef
.simobj
= simobj
1863 assert(isSimObject(newRef
.simobj
))
1864 if self
.peer
and not proxy
.isproxy(self
.peer
):
1865 peerObj
= self
.peer
.simobj(_memo
=memo
)
1866 newRef
.peer
= self
.peer
.clone(peerObj
, memo
)
1867 assert(not isinstance(newRef
.peer
, VectorPortRef
))
1870 def unproxy(self
, simobj
):
1871 assert(simobj
is self
.simobj
)
1872 if proxy
.isproxy(self
.peer
):
1874 realPeer
= self
.peer
.unproxy(self
.simobj
)
1876 print("Error in unproxying port '%s' of %s" %
1877 (self
.name
, self
.simobj
.path()))
1879 self
.connect(realPeer
)
1881 # Call C++ to create corresponding port connection between C++ objects
1882 def ccConnect(self
):
1883 from _m5
.pyobject
import connectPorts
1885 if self
.ccConnected
: # already done this
1889 if not self
.peer
: # nothing to connect to
1892 # check that we connect a master to a slave
1893 if self
.role
== peer
.role
:
1895 "cannot connect '%s' and '%s' due to identical role '%s'" % \
1896 (peer
, self
, self
.role
))
1898 if self
.role
== 'SLAVE':
1899 # do nothing and let the master take care of it
1903 # self is always the master and peer the slave
1904 connectPorts(self
.simobj
.getCCObject(), self
.name
, self
.index
,
1905 peer
.simobj
.getCCObject(), peer
.name
, peer
.index
)
1907 print("Error connecting port %s.%s to %s.%s" %
1908 (self
.simobj
.path(), self
.name
,
1909 peer
.simobj
.path(), peer
.name
))
1911 self
.ccConnected
= True
1912 peer
.ccConnected
= True
1914 # A reference to an individual element of a VectorPort... much like a
1915 # PortRef, but has an index.
1916 class VectorPortElementRef(PortRef
):
1917 def __init__(self
, simobj
, name
, role
, index
):
1918 PortRef
.__init
__(self
, simobj
, name
, role
)
1922 return '%s.%s[%d]' % (self
.simobj
, self
.name
, self
.index
)
1924 # A reference to a complete vector-valued port (not just a single element).
1925 # Can be indexed to retrieve individual VectorPortElementRef instances.
1926 class VectorPortRef(object):
1927 def __init__(self
, simobj
, name
, role
):
1928 assert(isSimObject(simobj
) or isSimObjectClass(simobj
))
1929 self
.simobj
= simobj
1935 return '%s.%s[:]' % (self
.simobj
, self
.name
)
1938 # Return the number of connected peers, corresponding the the
1939 # length of the elements.
1940 return len(self
.elements
)
1942 # for config.ini, print peer's name (not ours)
1944 return ' '.join([el
.ini_str() for el
in self
.elements
])
1947 def get_config_as_dict(self
):
1948 return {'role' : self
.role
,
1949 'peer' : [el
.ini_str() for el
in self
.elements
]}
1951 def __getitem__(self
, key
):
1952 if not isinstance(key
, int):
1953 raise TypeError("VectorPort index must be integer")
1954 if key
>= len(self
.elements
):
1955 # need to extend list
1956 ext
= [VectorPortElementRef(self
.simobj
, self
.name
, self
.role
, i
)
1957 for i
in range(len(self
.elements
), key
+1)]
1958 self
.elements
.extend(ext
)
1959 return self
.elements
[key
]
1961 def _get_next(self
):
1962 return self
[len(self
.elements
)]
1964 def __setitem__(self
, key
, value
):
1965 if not isinstance(key
, int):
1966 raise TypeError("VectorPort index must be integer")
1967 self
[key
].connect(value
)
1969 def connect(self
, other
):
1970 if isinstance(other
, (list, tuple)):
1971 # Assign list of port refs to vector port.
1972 # For now, append them... not sure if that's the right semantics
1973 # or if it should replace the current vector.
1975 self
._get
_next
().connect(ref
)
1977 # scalar assignment to plain VectorPort is implicit append
1978 self
._get
_next
().connect(other
)
1980 def clone(self
, simobj
, memo
):
1981 if memo
.has_key(self
):
1983 newRef
= copy
.copy(self
)
1985 newRef
.simobj
= simobj
1986 assert(isSimObject(newRef
.simobj
))
1987 newRef
.elements
= [el
.clone(simobj
, memo
) for el
in self
.elements
]
1990 def unproxy(self
, simobj
):
1991 [el
.unproxy(simobj
) for el
in self
.elements
]
1993 def ccConnect(self
):
1994 [el
.ccConnect() for el
in self
.elements
]
1996 # Port description object. Like a ParamDesc object, this represents a
1997 # logical port in the SimObject class, not a particular port on a
1998 # SimObject instance. The latter are represented by PortRef objects.
2000 # Generate a PortRef for this port on the given SimObject with the
2002 def makeRef(self
, simobj
):
2003 return PortRef(simobj
, self
.name
, self
.role
)
2005 # Connect an instance of this port (on the given SimObject with
2006 # the given name) with the port described by the supplied PortRef
2007 def connect(self
, simobj
, ref
):
2008 self
.makeRef(simobj
).connect(ref
)
2010 # No need for any pre-declarations at the moment as we merely rely
2011 # on an unsigned int.
2012 def cxx_predecls(self
, code
):
2015 def pybind_predecls(self
, code
):
2016 cls
.cxx_predecls(self
, code
)
2018 # Declare an unsigned int with the same name as the port, that
2019 # will eventually hold the number of connected ports (and thus the
2020 # number of elements for a VectorPort).
2021 def cxx_decl(self
, code
):
2022 code('unsigned int port_${{self.name}}_connection_count;')
2024 class MasterPort(Port
):
2025 # MasterPort("description")
2026 def __init__(self
, *args
):
2029 self
.role
= 'MASTER'
2031 raise TypeError('wrong number of arguments')
2033 class SlavePort(Port
):
2034 # SlavePort("description")
2035 def __init__(self
, *args
):
2040 raise TypeError('wrong number of arguments')
2042 # VectorPort description object. Like Port, but represents a vector
2043 # of connections (e.g., as on a XBar).
2044 class VectorPort(Port
):
2045 def __init__(self
, *args
):
2048 def makeRef(self
, simobj
):
2049 return VectorPortRef(simobj
, self
.name
, self
.role
)
2051 class VectorMasterPort(VectorPort
):
2052 # VectorMasterPort("description")
2053 def __init__(self
, *args
):
2056 self
.role
= 'MASTER'
2057 VectorPort
.__init
__(self
, *args
)
2059 raise TypeError('wrong number of arguments')
2061 class VectorSlavePort(VectorPort
):
2062 # VectorSlavePort("description")
2063 def __init__(self
, *args
):
2067 VectorPort
.__init
__(self
, *args
)
2069 raise TypeError('wrong number of arguments')
2071 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2072 # proxy objects (via set_param_desc()) so that proxy error messages
2074 class PortParamDesc(object):
2075 __metaclass__
= Singleton
2080 baseEnums
= allEnums
.copy()
2081 baseParams
= allParams
.copy()
2084 global allEnums
, allParams
2086 allEnums
= baseEnums
.copy()
2087 allParams
= baseParams
.copy()
2089 __all__
= ['Param', 'VectorParam',
2090 'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
2091 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2092 'Int32', 'UInt32', 'Int64', 'UInt64',
2093 'Counter', 'Addr', 'Tick', 'Percent',
2094 'TcpPort', 'UdpPort', 'EthernetAddr',
2095 'IpAddress', 'IpNetmask', 'IpWithPort',
2096 'MemorySize', 'MemorySize32',
2097 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2098 'NetworkBandwidth', 'MemoryBandwidth',
2100 'MaxAddr', 'MaxTick', 'AllMemory',
2102 'NextEthernetAddr', 'NULL',
2103 'MasterPort', 'SlavePort',
2104 'VectorMasterPort', 'VectorSlavePort']