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