params.py (4123:9c80390ea1bb) params.py (4167:ce5d0f62f13b)
1# Copyright (c) 2004-2006 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Steve Reinhardt
28# Nathan Binkert
29
30#####################################################################
31#
32# Parameter description classes
33#
34# The _params dictionary in each class maps parameter names to either
35# a Param or a VectorParam object. These objects contain the
36# parameter description string, the parameter type, and the default
37# value (if any). The convert() method on these objects is used to
38# force whatever value is assigned to the parameter to the appropriate
39# type.
40#
41# Note that the default values are loaded into the class's attribute
42# space when the parameter dictionary is initialized (in
43# MetaSimObject._new_param()); after that point they aren't used.
44#
45#####################################################################
46
47import copy
48import datetime
49import inspect
50import sys
51import time
52
53import convert
1# Copyright (c) 2004-2006 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Steve Reinhardt
28# Nathan Binkert
29
30#####################################################################
31#
32# Parameter description classes
33#
34# The _params dictionary in each class maps parameter names to either
35# a Param or a VectorParam object. These objects contain the
36# parameter description string, the parameter type, and the default
37# value (if any). The convert() method on these objects is used to
38# force whatever value is assigned to the parameter to the appropriate
39# type.
40#
41# Note that the default values are loaded into the class's attribute
42# space when the parameter dictionary is initialized (in
43# MetaSimObject._new_param()); after that point they aren't used.
44#
45#####################################################################
46
47import copy
48import datetime
49import inspect
50import sys
51import time
52
53import convert
54import ticks
54from util import *
55
56# Dummy base class to identify types that are legitimate for SimObject
57# parameters.
58class ParamValue(object):
59
60 cxx_predecls = []
61 swig_predecls = []
62
63 # default for printing to .ini file is regular string conversion.
64 # will be overridden in some cases
65 def ini_str(self):
66 return str(self)
67
68 # allows us to blithely call unproxy() on things without checking
69 # if they're really proxies or not
70 def unproxy(self, base):
71 return self
72
73# Regular parameter description.
74class ParamDesc(object):
75 def __init__(self, ptype_str, ptype, *args, **kwargs):
76 self.ptype_str = ptype_str
77 # remember ptype only if it is provided
78 if ptype != None:
79 self.ptype = ptype
80
81 if args:
82 if len(args) == 1:
83 self.desc = args[0]
84 elif len(args) == 2:
85 self.default = args[0]
86 self.desc = args[1]
87 else:
88 raise TypeError, 'too many arguments'
89
90 if kwargs.has_key('desc'):
91 assert(not hasattr(self, 'desc'))
92 self.desc = kwargs['desc']
93 del kwargs['desc']
94
95 if kwargs.has_key('default'):
96 assert(not hasattr(self, 'default'))
97 self.default = kwargs['default']
98 del kwargs['default']
99
100 if kwargs:
101 raise TypeError, 'extra unknown kwargs %s' % kwargs
102
103 if not hasattr(self, 'desc'):
104 raise TypeError, 'desc attribute missing'
105
106 def __getattr__(self, attr):
107 if attr == 'ptype':
108 try:
109 ptype = eval(self.ptype_str, objects.__dict__)
110 if not isinstance(ptype, type):
111 raise NameError
112 self.ptype = ptype
113 return ptype
114 except NameError:
115 raise TypeError, \
116 "Param qualifier '%s' is not a type" % self.ptype_str
117 raise AttributeError, "'%s' object has no attribute '%s'" % \
118 (type(self).__name__, attr)
119
120 def convert(self, value):
121 if isinstance(value, proxy.BaseProxy):
122 value.set_param_desc(self)
123 return value
124 if not hasattr(self, 'ptype') and isNullPointer(value):
125 # deferred evaluation of SimObject; continue to defer if
126 # we're just assigning a null pointer
127 return value
128 if isinstance(value, self.ptype):
129 return value
130 if isNullPointer(value) and isSimObjectClass(self.ptype):
131 return value
132 return self.ptype(value)
133
134 def cxx_predecls(self):
135 return self.ptype.cxx_predecls
136
137 def swig_predecls(self):
138 return self.ptype.swig_predecls
139
140 def cxx_decl(self):
141 return '%s %s;' % (self.ptype.cxx_type, self.name)
142
143# Vector-valued parameter description. Just like ParamDesc, except
144# that the value is a vector (list) of the specified type instead of a
145# single value.
146
147class VectorParamValue(list):
148 def ini_str(self):
149 return ' '.join([v.ini_str() for v in self])
150
151 def unproxy(self, base):
152 return [v.unproxy(base) for v in self]
153
154class SimObjVector(VectorParamValue):
155 def print_ini(self):
156 for v in self:
157 v.print_ini()
158
159class VectorParamDesc(ParamDesc):
160 # Convert assigned value to appropriate type. If the RHS is not a
161 # list or tuple, it generates a single-element list.
162 def convert(self, value):
163 if isinstance(value, (list, tuple)):
164 # list: coerce each element into new list
165 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
166 if isSimObjectSequence(tmp_list):
167 return SimObjVector(tmp_list)
168 else:
169 return VectorParamValue(tmp_list)
170 else:
171 # singleton: leave it be (could coerce to a single-element
172 # list here, but for some historical reason we don't...
173 return ParamDesc.convert(self, value)
174
175 def cxx_predecls(self):
176 return ['#include <vector>'] + self.ptype.cxx_predecls
177
178 def swig_predecls(self):
179 return ['%include "std_vector.i"'] + self.ptype.swig_predecls
180
181 def cxx_decl(self):
182 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
183
184class ParamFactory(object):
185 def __init__(self, param_desc_class, ptype_str = None):
186 self.param_desc_class = param_desc_class
187 self.ptype_str = ptype_str
188
189 def __getattr__(self, attr):
190 if self.ptype_str:
191 attr = self.ptype_str + '.' + attr
192 return ParamFactory(self.param_desc_class, attr)
193
194 # E.g., Param.Int(5, "number of widgets")
195 def __call__(self, *args, **kwargs):
196 caller_frame = inspect.currentframe().f_back
197 ptype = None
198 try:
199 ptype = eval(self.ptype_str,
200 caller_frame.f_globals, caller_frame.f_locals)
201 if not isinstance(ptype, type):
202 raise TypeError, \
203 "Param qualifier is not a type: %s" % ptype
204 except NameError:
205 # if name isn't defined yet, assume it's a SimObject, and
206 # try to resolve it later
207 pass
208 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
209
210Param = ParamFactory(ParamDesc)
211VectorParam = ParamFactory(VectorParamDesc)
212
213#####################################################################
214#
215# Parameter Types
216#
217# Though native Python types could be used to specify parameter types
218# (the 'ptype' field of the Param and VectorParam classes), it's more
219# flexible to define our own set of types. This gives us more control
220# over how Python expressions are converted to values (via the
221# __init__() constructor) and how these values are printed out (via
222# the __str__() conversion method).
223#
224#####################################################################
225
226# String-valued parameter. Just mixin the ParamValue class with the
227# built-in str class.
228class String(ParamValue,str):
229 cxx_type = 'std::string'
230 cxx_predecls = ['#include <string>']
231 swig_predecls = ['%include "std_string.i"\n' +
232 '%apply const std::string& {std::string *};']
233 pass
234
235# superclass for "numeric" parameter values, to emulate math
236# operations in a type-safe way. e.g., a Latency times an int returns
237# a new Latency object.
238class NumericParamValue(ParamValue):
239 def __str__(self):
240 return str(self.value)
241
242 def __float__(self):
243 return float(self.value)
244
245 def __long__(self):
246 return long(self.value)
247
248 def __int__(self):
249 return int(self.value)
250
251 # hook for bounds checking
252 def _check(self):
253 return
254
255 def __mul__(self, other):
256 newobj = self.__class__(self)
257 newobj.value *= other
258 newobj._check()
259 return newobj
260
261 __rmul__ = __mul__
262
263 def __div__(self, other):
264 newobj = self.__class__(self)
265 newobj.value /= other
266 newobj._check()
267 return newobj
268
269 def __sub__(self, other):
270 newobj = self.__class__(self)
271 newobj.value -= other
272 newobj._check()
273 return newobj
274
275# Metaclass for bounds-checked integer parameters. See CheckedInt.
276class CheckedIntType(type):
277 def __init__(cls, name, bases, dict):
278 super(CheckedIntType, cls).__init__(name, bases, dict)
279
280 # CheckedInt is an abstract base class, so we actually don't
281 # want to do any processing on it... the rest of this code is
282 # just for classes that derive from CheckedInt.
283 if name == 'CheckedInt':
284 return
285
286 if not cls.cxx_predecls:
287 # most derived types require this, so we just do it here once
288 cls.cxx_predecls = ['#include "sim/host.hh"']
289
290 if not cls.swig_predecls:
291 # most derived types require this, so we just do it here once
292 cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
293 '%import "sim/host.hh"']
294
295 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
296 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
297 panic("CheckedInt subclass %s must define either\n" \
298 " 'min' and 'max' or 'size' and 'unsigned'\n" \
299 % name);
300 if cls.unsigned:
301 cls.min = 0
302 cls.max = 2 ** cls.size - 1
303 else:
304 cls.min = -(2 ** (cls.size - 1))
305 cls.max = (2 ** (cls.size - 1)) - 1
306
307# Abstract superclass for bounds-checked integer parameters. This
308# class is subclassed to generate parameter classes with specific
309# bounds. Initialization of the min and max bounds is done in the
310# metaclass CheckedIntType.__init__.
311class CheckedInt(NumericParamValue):
312 __metaclass__ = CheckedIntType
313
314 def _check(self):
315 if not self.min <= self.value <= self.max:
316 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
317 (self.min, self.value, self.max)
318
319 def __init__(self, value):
320 if isinstance(value, str):
321 self.value = convert.toInteger(value)
322 elif isinstance(value, (int, long, float, NumericParamValue)):
323 self.value = long(value)
324 else:
325 raise TypeError, "Can't convert object of type %s to CheckedInt" \
326 % type(value).__name__
327 self._check()
328
329class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
330class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
331
332class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
333class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
334class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
335class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
336class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
337class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
338class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
339class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
340
341class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
342class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
343class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
344class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
345
346class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
347
348class Float(ParamValue, float):
349 pass
350
351class MemorySize(CheckedInt):
352 cxx_type = 'uint64_t'
353 size = 64
354 unsigned = True
355 def __init__(self, value):
356 if isinstance(value, MemorySize):
357 self.value = value.value
358 else:
359 self.value = convert.toMemorySize(value)
360 self._check()
361
362class MemorySize32(CheckedInt):
363 size = 32
364 unsigned = True
365 def __init__(self, value):
366 if isinstance(value, MemorySize):
367 self.value = value.value
368 else:
369 self.value = convert.toMemorySize(value)
370 self._check()
371
372class Addr(CheckedInt):
373 cxx_type = 'Addr'
374 cxx_predecls = ['#include "targetarch/isa_traits.hh"']
375 size = 64
376 unsigned = True
377 def __init__(self, value):
378 if isinstance(value, Addr):
379 self.value = value.value
380 else:
381 try:
382 self.value = convert.toMemorySize(value)
383 except TypeError:
384 self.value = long(value)
385 self._check()
386 def __add__(self, other):
387 if isinstance(other, Addr):
388 return self.value + other.value
389 else:
390 return self.value + other
391
392
393class MetaRange(type):
394 def __init__(cls, name, bases, dict):
395 super(MetaRange, cls).__init__(name, bases, dict)
396 if name == 'Range':
397 return
398 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
399 cls.cxx_predecls = \
400 ['#include "base/range.hh"'] + cls.type.cxx_predecls
401
402class Range(ParamValue):
403 __metaclass__ = MetaRange
404 type = Int # default; can be overridden in subclasses
405 def __init__(self, *args, **kwargs):
406 def handle_kwargs(self, kwargs):
407 if 'end' in kwargs:
408 self.second = self.type(kwargs.pop('end'))
409 elif 'size' in kwargs:
410 self.second = self.first + self.type(kwargs.pop('size')) - 1
411 else:
412 raise TypeError, "Either end or size must be specified"
413
414 if len(args) == 0:
415 self.first = self.type(kwargs.pop('start'))
416 handle_kwargs(self, kwargs)
417
418 elif len(args) == 1:
419 if kwargs:
420 self.first = self.type(args[0])
421 handle_kwargs(self, kwargs)
422 elif isinstance(args[0], Range):
423 self.first = self.type(args[0].first)
424 self.second = self.type(args[0].second)
425 else:
426 self.first = self.type(0)
427 self.second = self.type(args[0]) - 1
428
429 elif len(args) == 2:
430 self.first = self.type(args[0])
431 self.second = self.type(args[1])
432 else:
433 raise TypeError, "Too many arguments specified"
434
435 if kwargs:
436 raise TypeError, "too many keywords: %s" % kwargs.keys()
437
438 def __str__(self):
439 return '%s:%s' % (self.first, self.second)
440
441class AddrRange(Range):
442 type = Addr
443
444class TickRange(Range):
445 type = Tick
446
447# Boolean parameter type. Python doesn't let you subclass bool, since
448# it doesn't want to let you create multiple instances of True and
449# False. Thus this is a little more complicated than String.
450class Bool(ParamValue):
451 cxx_type = 'bool'
452 def __init__(self, value):
453 try:
454 self.value = convert.toBool(value)
455 except TypeError:
456 self.value = bool(value)
457
458 def __str__(self):
459 return str(self.value)
460
461 def ini_str(self):
462 if self.value:
463 return 'true'
464 return 'false'
465
466def IncEthernetAddr(addr, val = 1):
467 bytes = map(lambda x: int(x, 16), addr.split(':'))
468 bytes[5] += val
469 for i in (5, 4, 3, 2, 1):
470 val,rem = divmod(bytes[i], 256)
471 bytes[i] = rem
472 if val == 0:
473 break
474 bytes[i - 1] += val
475 assert(bytes[0] <= 255)
476 return ':'.join(map(lambda x: '%02x' % x, bytes))
477
478class NextEthernetAddr(object):
479 addr = "00:90:00:00:00:01"
480
481 def __init__(self, inc = 1):
482 self.value = NextEthernetAddr.addr
483 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
484
485class EthernetAddr(ParamValue):
486 cxx_type = 'Net::EthAddr'
487 cxx_predecls = ['#include "base/inet.hh"']
488 swig_predecls = ['class Net::EthAddr;']
489 def __init__(self, value):
490 if value == NextEthernetAddr:
491 self.value = value
492 return
493
494 if not isinstance(value, str):
495 raise TypeError, "expected an ethernet address and didn't get one"
496
497 bytes = value.split(':')
498 if len(bytes) != 6:
499 raise TypeError, 'invalid ethernet address %s' % value
500
501 for byte in bytes:
502 if not 0 <= int(byte) <= 256:
503 raise TypeError, 'invalid ethernet address %s' % value
504
505 self.value = value
506
507 def unproxy(self, base):
508 if self.value == NextEthernetAddr:
509 self.addr = self.value().value
510 return self
511
512 def __str__(self):
513 if self.value == NextEthernetAddr:
514 if hasattr(self, 'addr'):
515 return self.addr
516 else:
517 return "NextEthernetAddr (unresolved)"
518 else:
519 return self.value
520
521time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
522 "%a %b %d %H:%M:%S %Z %Y",
523 "%Y/%m/%d %H:%M:%S",
524 "%Y/%m/%d %H:%M",
525 "%Y/%m/%d",
526 "%m/%d/%Y %H:%M:%S",
527 "%m/%d/%Y %H:%M",
528 "%m/%d/%Y",
529 "%m/%d/%y %H:%M:%S",
530 "%m/%d/%y %H:%M",
531 "%m/%d/%y"]
532
533
534def parse_time(value):
535 from time import gmtime, strptime, struct_time, time
536 from datetime import datetime, date
537
538 if isinstance(value, struct_time):
539 return value
540
541 if isinstance(value, (int, long)):
542 return gmtime(value)
543
544 if isinstance(value, (datetime, date)):
545 return value.timetuple()
546
547 if isinstance(value, str):
548 if value in ('Now', 'Today'):
549 return time.gmtime(time.time())
550
551 for format in time_formats:
552 try:
553 return strptime(value, format)
554 except ValueError:
555 pass
556
557 raise ValueError, "Could not parse '%s' as a time" % value
558
559class Time(ParamValue):
560 cxx_type = 'time_t'
561 def __init__(self, value):
562 self.value = parse_time(value)
563
564 def __str__(self):
565 tm = self.value
566 return ' '.join([ str(tm[i]) for i in xrange(8)])
567
568 def ini_str(self):
569 return str(self)
570
571# Enumerated types are a little more complex. The user specifies the
572# type as Enum(foo) where foo is either a list or dictionary of
573# alternatives (typically strings, but not necessarily so). (In the
574# long run, the integer value of the parameter will be the list index
575# or the corresponding dictionary value. For now, since we only check
576# that the alternative is valid and then spit it into a .ini file,
577# there's not much point in using the dictionary.)
578
579# What Enum() must do is generate a new type encapsulating the
580# provided list/dictionary so that specific values of the parameter
581# can be instances of that type. We define two hidden internal
582# classes (_ListEnum and _DictEnum) to serve as base classes, then
583# derive the new type from the appropriate base class on the fly.
584
585
586# Metaclass for Enum types
587class MetaEnum(type):
588 def __init__(cls, name, bases, init_dict):
589 if init_dict.has_key('map'):
590 if not isinstance(cls.map, dict):
591 raise TypeError, "Enum-derived class attribute 'map' " \
592 "must be of type dict"
593 # build list of value strings from map
594 cls.vals = cls.map.keys()
595 cls.vals.sort()
596 elif init_dict.has_key('vals'):
597 if not isinstance(cls.vals, list):
598 raise TypeError, "Enum-derived class attribute 'vals' " \
599 "must be of type list"
600 # build string->value map from vals sequence
601 cls.map = {}
602 for idx,val in enumerate(cls.vals):
603 cls.map[val] = idx
604 else:
605 raise TypeError, "Enum-derived class must define "\
606 "attribute 'map' or 'vals'"
607
608 cls.cxx_type = name + '::Enum'
609
610 super(MetaEnum, cls).__init__(name, bases, init_dict)
611
612 # Generate C++ class declaration for this enum type.
613 # Note that we wrap the enum in a class/struct to act as a namespace,
614 # so that the enum strings can be brief w/o worrying about collisions.
615 def cxx_decl(cls):
616 s = 'struct %s {\n enum Enum {\n ' % cls.__name__
617 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
618 s += '\n };\n};\n'
619 return s
620
621# Base class for enum types.
622class Enum(ParamValue):
623 __metaclass__ = MetaEnum
624 vals = []
625
626 def __init__(self, value):
627 if value not in self.map:
628 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
629 % (value, self.vals)
630 self.value = value
631
632 def __str__(self):
633 return self.value
634
55from util import *
56
57# Dummy base class to identify types that are legitimate for SimObject
58# parameters.
59class ParamValue(object):
60
61 cxx_predecls = []
62 swig_predecls = []
63
64 # default for printing to .ini file is regular string conversion.
65 # will be overridden in some cases
66 def ini_str(self):
67 return str(self)
68
69 # allows us to blithely call unproxy() on things without checking
70 # if they're really proxies or not
71 def unproxy(self, base):
72 return self
73
74# Regular parameter description.
75class ParamDesc(object):
76 def __init__(self, ptype_str, ptype, *args, **kwargs):
77 self.ptype_str = ptype_str
78 # remember ptype only if it is provided
79 if ptype != None:
80 self.ptype = ptype
81
82 if args:
83 if len(args) == 1:
84 self.desc = args[0]
85 elif len(args) == 2:
86 self.default = args[0]
87 self.desc = args[1]
88 else:
89 raise TypeError, 'too many arguments'
90
91 if kwargs.has_key('desc'):
92 assert(not hasattr(self, 'desc'))
93 self.desc = kwargs['desc']
94 del kwargs['desc']
95
96 if kwargs.has_key('default'):
97 assert(not hasattr(self, 'default'))
98 self.default = kwargs['default']
99 del kwargs['default']
100
101 if kwargs:
102 raise TypeError, 'extra unknown kwargs %s' % kwargs
103
104 if not hasattr(self, 'desc'):
105 raise TypeError, 'desc attribute missing'
106
107 def __getattr__(self, attr):
108 if attr == 'ptype':
109 try:
110 ptype = eval(self.ptype_str, objects.__dict__)
111 if not isinstance(ptype, type):
112 raise NameError
113 self.ptype = ptype
114 return ptype
115 except NameError:
116 raise TypeError, \
117 "Param qualifier '%s' is not a type" % self.ptype_str
118 raise AttributeError, "'%s' object has no attribute '%s'" % \
119 (type(self).__name__, attr)
120
121 def convert(self, value):
122 if isinstance(value, proxy.BaseProxy):
123 value.set_param_desc(self)
124 return value
125 if not hasattr(self, 'ptype') and isNullPointer(value):
126 # deferred evaluation of SimObject; continue to defer if
127 # we're just assigning a null pointer
128 return value
129 if isinstance(value, self.ptype):
130 return value
131 if isNullPointer(value) and isSimObjectClass(self.ptype):
132 return value
133 return self.ptype(value)
134
135 def cxx_predecls(self):
136 return self.ptype.cxx_predecls
137
138 def swig_predecls(self):
139 return self.ptype.swig_predecls
140
141 def cxx_decl(self):
142 return '%s %s;' % (self.ptype.cxx_type, self.name)
143
144# Vector-valued parameter description. Just like ParamDesc, except
145# that the value is a vector (list) of the specified type instead of a
146# single value.
147
148class VectorParamValue(list):
149 def ini_str(self):
150 return ' '.join([v.ini_str() for v in self])
151
152 def unproxy(self, base):
153 return [v.unproxy(base) for v in self]
154
155class SimObjVector(VectorParamValue):
156 def print_ini(self):
157 for v in self:
158 v.print_ini()
159
160class VectorParamDesc(ParamDesc):
161 # Convert assigned value to appropriate type. If the RHS is not a
162 # list or tuple, it generates a single-element list.
163 def convert(self, value):
164 if isinstance(value, (list, tuple)):
165 # list: coerce each element into new list
166 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
167 if isSimObjectSequence(tmp_list):
168 return SimObjVector(tmp_list)
169 else:
170 return VectorParamValue(tmp_list)
171 else:
172 # singleton: leave it be (could coerce to a single-element
173 # list here, but for some historical reason we don't...
174 return ParamDesc.convert(self, value)
175
176 def cxx_predecls(self):
177 return ['#include <vector>'] + self.ptype.cxx_predecls
178
179 def swig_predecls(self):
180 return ['%include "std_vector.i"'] + self.ptype.swig_predecls
181
182 def cxx_decl(self):
183 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
184
185class ParamFactory(object):
186 def __init__(self, param_desc_class, ptype_str = None):
187 self.param_desc_class = param_desc_class
188 self.ptype_str = ptype_str
189
190 def __getattr__(self, attr):
191 if self.ptype_str:
192 attr = self.ptype_str + '.' + attr
193 return ParamFactory(self.param_desc_class, attr)
194
195 # E.g., Param.Int(5, "number of widgets")
196 def __call__(self, *args, **kwargs):
197 caller_frame = inspect.currentframe().f_back
198 ptype = None
199 try:
200 ptype = eval(self.ptype_str,
201 caller_frame.f_globals, caller_frame.f_locals)
202 if not isinstance(ptype, type):
203 raise TypeError, \
204 "Param qualifier is not a type: %s" % ptype
205 except NameError:
206 # if name isn't defined yet, assume it's a SimObject, and
207 # try to resolve it later
208 pass
209 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
210
211Param = ParamFactory(ParamDesc)
212VectorParam = ParamFactory(VectorParamDesc)
213
214#####################################################################
215#
216# Parameter Types
217#
218# Though native Python types could be used to specify parameter types
219# (the 'ptype' field of the Param and VectorParam classes), it's more
220# flexible to define our own set of types. This gives us more control
221# over how Python expressions are converted to values (via the
222# __init__() constructor) and how these values are printed out (via
223# the __str__() conversion method).
224#
225#####################################################################
226
227# String-valued parameter. Just mixin the ParamValue class with the
228# built-in str class.
229class String(ParamValue,str):
230 cxx_type = 'std::string'
231 cxx_predecls = ['#include <string>']
232 swig_predecls = ['%include "std_string.i"\n' +
233 '%apply const std::string& {std::string *};']
234 pass
235
236# superclass for "numeric" parameter values, to emulate math
237# operations in a type-safe way. e.g., a Latency times an int returns
238# a new Latency object.
239class NumericParamValue(ParamValue):
240 def __str__(self):
241 return str(self.value)
242
243 def __float__(self):
244 return float(self.value)
245
246 def __long__(self):
247 return long(self.value)
248
249 def __int__(self):
250 return int(self.value)
251
252 # hook for bounds checking
253 def _check(self):
254 return
255
256 def __mul__(self, other):
257 newobj = self.__class__(self)
258 newobj.value *= other
259 newobj._check()
260 return newobj
261
262 __rmul__ = __mul__
263
264 def __div__(self, other):
265 newobj = self.__class__(self)
266 newobj.value /= other
267 newobj._check()
268 return newobj
269
270 def __sub__(self, other):
271 newobj = self.__class__(self)
272 newobj.value -= other
273 newobj._check()
274 return newobj
275
276# Metaclass for bounds-checked integer parameters. See CheckedInt.
277class CheckedIntType(type):
278 def __init__(cls, name, bases, dict):
279 super(CheckedIntType, cls).__init__(name, bases, dict)
280
281 # CheckedInt is an abstract base class, so we actually don't
282 # want to do any processing on it... the rest of this code is
283 # just for classes that derive from CheckedInt.
284 if name == 'CheckedInt':
285 return
286
287 if not cls.cxx_predecls:
288 # most derived types require this, so we just do it here once
289 cls.cxx_predecls = ['#include "sim/host.hh"']
290
291 if not cls.swig_predecls:
292 # most derived types require this, so we just do it here once
293 cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
294 '%import "sim/host.hh"']
295
296 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
297 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
298 panic("CheckedInt subclass %s must define either\n" \
299 " 'min' and 'max' or 'size' and 'unsigned'\n" \
300 % name);
301 if cls.unsigned:
302 cls.min = 0
303 cls.max = 2 ** cls.size - 1
304 else:
305 cls.min = -(2 ** (cls.size - 1))
306 cls.max = (2 ** (cls.size - 1)) - 1
307
308# Abstract superclass for bounds-checked integer parameters. This
309# class is subclassed to generate parameter classes with specific
310# bounds. Initialization of the min and max bounds is done in the
311# metaclass CheckedIntType.__init__.
312class CheckedInt(NumericParamValue):
313 __metaclass__ = CheckedIntType
314
315 def _check(self):
316 if not self.min <= self.value <= self.max:
317 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
318 (self.min, self.value, self.max)
319
320 def __init__(self, value):
321 if isinstance(value, str):
322 self.value = convert.toInteger(value)
323 elif isinstance(value, (int, long, float, NumericParamValue)):
324 self.value = long(value)
325 else:
326 raise TypeError, "Can't convert object of type %s to CheckedInt" \
327 % type(value).__name__
328 self._check()
329
330class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
331class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
332
333class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
334class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
335class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
336class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
337class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
338class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
339class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
340class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
341
342class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
343class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
344class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
345class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
346
347class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
348
349class Float(ParamValue, float):
350 pass
351
352class MemorySize(CheckedInt):
353 cxx_type = 'uint64_t'
354 size = 64
355 unsigned = True
356 def __init__(self, value):
357 if isinstance(value, MemorySize):
358 self.value = value.value
359 else:
360 self.value = convert.toMemorySize(value)
361 self._check()
362
363class MemorySize32(CheckedInt):
364 size = 32
365 unsigned = True
366 def __init__(self, value):
367 if isinstance(value, MemorySize):
368 self.value = value.value
369 else:
370 self.value = convert.toMemorySize(value)
371 self._check()
372
373class Addr(CheckedInt):
374 cxx_type = 'Addr'
375 cxx_predecls = ['#include "targetarch/isa_traits.hh"']
376 size = 64
377 unsigned = True
378 def __init__(self, value):
379 if isinstance(value, Addr):
380 self.value = value.value
381 else:
382 try:
383 self.value = convert.toMemorySize(value)
384 except TypeError:
385 self.value = long(value)
386 self._check()
387 def __add__(self, other):
388 if isinstance(other, Addr):
389 return self.value + other.value
390 else:
391 return self.value + other
392
393
394class MetaRange(type):
395 def __init__(cls, name, bases, dict):
396 super(MetaRange, cls).__init__(name, bases, dict)
397 if name == 'Range':
398 return
399 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
400 cls.cxx_predecls = \
401 ['#include "base/range.hh"'] + cls.type.cxx_predecls
402
403class Range(ParamValue):
404 __metaclass__ = MetaRange
405 type = Int # default; can be overridden in subclasses
406 def __init__(self, *args, **kwargs):
407 def handle_kwargs(self, kwargs):
408 if 'end' in kwargs:
409 self.second = self.type(kwargs.pop('end'))
410 elif 'size' in kwargs:
411 self.second = self.first + self.type(kwargs.pop('size')) - 1
412 else:
413 raise TypeError, "Either end or size must be specified"
414
415 if len(args) == 0:
416 self.first = self.type(kwargs.pop('start'))
417 handle_kwargs(self, kwargs)
418
419 elif len(args) == 1:
420 if kwargs:
421 self.first = self.type(args[0])
422 handle_kwargs(self, kwargs)
423 elif isinstance(args[0], Range):
424 self.first = self.type(args[0].first)
425 self.second = self.type(args[0].second)
426 else:
427 self.first = self.type(0)
428 self.second = self.type(args[0]) - 1
429
430 elif len(args) == 2:
431 self.first = self.type(args[0])
432 self.second = self.type(args[1])
433 else:
434 raise TypeError, "Too many arguments specified"
435
436 if kwargs:
437 raise TypeError, "too many keywords: %s" % kwargs.keys()
438
439 def __str__(self):
440 return '%s:%s' % (self.first, self.second)
441
442class AddrRange(Range):
443 type = Addr
444
445class TickRange(Range):
446 type = Tick
447
448# Boolean parameter type. Python doesn't let you subclass bool, since
449# it doesn't want to let you create multiple instances of True and
450# False. Thus this is a little more complicated than String.
451class Bool(ParamValue):
452 cxx_type = 'bool'
453 def __init__(self, value):
454 try:
455 self.value = convert.toBool(value)
456 except TypeError:
457 self.value = bool(value)
458
459 def __str__(self):
460 return str(self.value)
461
462 def ini_str(self):
463 if self.value:
464 return 'true'
465 return 'false'
466
467def IncEthernetAddr(addr, val = 1):
468 bytes = map(lambda x: int(x, 16), addr.split(':'))
469 bytes[5] += val
470 for i in (5, 4, 3, 2, 1):
471 val,rem = divmod(bytes[i], 256)
472 bytes[i] = rem
473 if val == 0:
474 break
475 bytes[i - 1] += val
476 assert(bytes[0] <= 255)
477 return ':'.join(map(lambda x: '%02x' % x, bytes))
478
479class NextEthernetAddr(object):
480 addr = "00:90:00:00:00:01"
481
482 def __init__(self, inc = 1):
483 self.value = NextEthernetAddr.addr
484 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
485
486class EthernetAddr(ParamValue):
487 cxx_type = 'Net::EthAddr'
488 cxx_predecls = ['#include "base/inet.hh"']
489 swig_predecls = ['class Net::EthAddr;']
490 def __init__(self, value):
491 if value == NextEthernetAddr:
492 self.value = value
493 return
494
495 if not isinstance(value, str):
496 raise TypeError, "expected an ethernet address and didn't get one"
497
498 bytes = value.split(':')
499 if len(bytes) != 6:
500 raise TypeError, 'invalid ethernet address %s' % value
501
502 for byte in bytes:
503 if not 0 <= int(byte) <= 256:
504 raise TypeError, 'invalid ethernet address %s' % value
505
506 self.value = value
507
508 def unproxy(self, base):
509 if self.value == NextEthernetAddr:
510 self.addr = self.value().value
511 return self
512
513 def __str__(self):
514 if self.value == NextEthernetAddr:
515 if hasattr(self, 'addr'):
516 return self.addr
517 else:
518 return "NextEthernetAddr (unresolved)"
519 else:
520 return self.value
521
522time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
523 "%a %b %d %H:%M:%S %Z %Y",
524 "%Y/%m/%d %H:%M:%S",
525 "%Y/%m/%d %H:%M",
526 "%Y/%m/%d",
527 "%m/%d/%Y %H:%M:%S",
528 "%m/%d/%Y %H:%M",
529 "%m/%d/%Y",
530 "%m/%d/%y %H:%M:%S",
531 "%m/%d/%y %H:%M",
532 "%m/%d/%y"]
533
534
535def parse_time(value):
536 from time import gmtime, strptime, struct_time, time
537 from datetime import datetime, date
538
539 if isinstance(value, struct_time):
540 return value
541
542 if isinstance(value, (int, long)):
543 return gmtime(value)
544
545 if isinstance(value, (datetime, date)):
546 return value.timetuple()
547
548 if isinstance(value, str):
549 if value in ('Now', 'Today'):
550 return time.gmtime(time.time())
551
552 for format in time_formats:
553 try:
554 return strptime(value, format)
555 except ValueError:
556 pass
557
558 raise ValueError, "Could not parse '%s' as a time" % value
559
560class Time(ParamValue):
561 cxx_type = 'time_t'
562 def __init__(self, value):
563 self.value = parse_time(value)
564
565 def __str__(self):
566 tm = self.value
567 return ' '.join([ str(tm[i]) for i in xrange(8)])
568
569 def ini_str(self):
570 return str(self)
571
572# Enumerated types are a little more complex. The user specifies the
573# type as Enum(foo) where foo is either a list or dictionary of
574# alternatives (typically strings, but not necessarily so). (In the
575# long run, the integer value of the parameter will be the list index
576# or the corresponding dictionary value. For now, since we only check
577# that the alternative is valid and then spit it into a .ini file,
578# there's not much point in using the dictionary.)
579
580# What Enum() must do is generate a new type encapsulating the
581# provided list/dictionary so that specific values of the parameter
582# can be instances of that type. We define two hidden internal
583# classes (_ListEnum and _DictEnum) to serve as base classes, then
584# derive the new type from the appropriate base class on the fly.
585
586
587# Metaclass for Enum types
588class MetaEnum(type):
589 def __init__(cls, name, bases, init_dict):
590 if init_dict.has_key('map'):
591 if not isinstance(cls.map, dict):
592 raise TypeError, "Enum-derived class attribute 'map' " \
593 "must be of type dict"
594 # build list of value strings from map
595 cls.vals = cls.map.keys()
596 cls.vals.sort()
597 elif init_dict.has_key('vals'):
598 if not isinstance(cls.vals, list):
599 raise TypeError, "Enum-derived class attribute 'vals' " \
600 "must be of type list"
601 # build string->value map from vals sequence
602 cls.map = {}
603 for idx,val in enumerate(cls.vals):
604 cls.map[val] = idx
605 else:
606 raise TypeError, "Enum-derived class must define "\
607 "attribute 'map' or 'vals'"
608
609 cls.cxx_type = name + '::Enum'
610
611 super(MetaEnum, cls).__init__(name, bases, init_dict)
612
613 # Generate C++ class declaration for this enum type.
614 # Note that we wrap the enum in a class/struct to act as a namespace,
615 # so that the enum strings can be brief w/o worrying about collisions.
616 def cxx_decl(cls):
617 s = 'struct %s {\n enum Enum {\n ' % cls.__name__
618 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
619 s += '\n };\n};\n'
620 return s
621
622# Base class for enum types.
623class Enum(ParamValue):
624 __metaclass__ = MetaEnum
625 vals = []
626
627 def __init__(self, value):
628 if value not in self.map:
629 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
630 % (value, self.vals)
631 self.value = value
632
633 def __str__(self):
634 return self.value
635
635ticks_per_sec = None
636
637# how big does a rounding error need to be before we warn about it?
638frequency_tolerance = 0.001 # 0.1%
639
636# how big does a rounding error need to be before we warn about it?
637frequency_tolerance = 0.001 # 0.1%
638
640# convert a floting-point # of ticks to integer, and warn if rounding
641# discards too much precision
642def tick_check(float_ticks):
643 if float_ticks == 0:
644 return 0
645 int_ticks = int(round(float_ticks))
646 err = (float_ticks - int_ticks) / float_ticks
647 if err > frequency_tolerance:
648 print >> sys.stderr, "Warning: rounding error > tolerance"
649 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
650 #raise ValueError
651 return int_ticks
652
653def getLatency(value):
654 if isinstance(value, Latency) or isinstance(value, Clock):
655 return value.value
656 elif isinstance(value, Frequency) or isinstance(value, RootClock):
657 return 1 / value.value
658 elif isinstance(value, str):
659 try:
660 return convert.toLatency(value)
661 except ValueError:
662 try:
663 return 1 / convert.toFrequency(value)
664 except ValueError:
665 pass # fall through
666 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
667
668
669class Latency(NumericParamValue):
639class TickParamValue(NumericParamValue):
670 cxx_type = 'Tick'
671 cxx_predecls = ['#include "sim/host.hh"']
672 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
673 '%import "sim/host.hh"']
640 cxx_type = 'Tick'
641 cxx_predecls = ['#include "sim/host.hh"']
642 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
643 '%import "sim/host.hh"']
644
645class Latency(TickParamValue):
674 def __init__(self, value):
646 def __init__(self, value):
675 self.value = getLatency(value)
647 if isinstance(value, (Latency, Clock)):
648 self.ticks = value.ticks
649 self.value = value.value
650 elif isinstance(value, Frequency):
651 self.ticks = value.ticks
652 self.value = 1.0 / value.value
653 elif value.endswith('t'):
654 self.ticks = True
655 self.value = int(value[:-1])
656 else:
657 self.ticks = False
658 self.value = convert.toLatency(value)
676
677 def __getattr__(self, attr):
678 if attr in ('latency', 'period'):
679 return self
680 if attr == 'frequency':
681 return Frequency(self)
682 raise AttributeError, "Latency object has no attribute '%s'" % attr
683
684 # convert latency to ticks
685 def ini_str(self):
659
660 def __getattr__(self, attr):
661 if attr in ('latency', 'period'):
662 return self
663 if attr == 'frequency':
664 return Frequency(self)
665 raise AttributeError, "Latency object has no attribute '%s'" % attr
666
667 # convert latency to ticks
668 def ini_str(self):
686 return str(tick_check(self.value * ticks_per_sec))
669 if self.ticks or self.value == 0:
670 return '%d' % self.value
671 else:
672 return '%d' % (ticks.fromSeconds(self.value))
687
673
688class Frequency(NumericParamValue):
689 cxx_type = 'Tick'
690 cxx_predecls = ['#include "sim/host.hh"']
691 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
692 '%import "sim/host.hh"']
674class Frequency(TickParamValue):
693 def __init__(self, value):
675 def __init__(self, value):
694 self.value = 1 / getLatency(value)
676 if isinstance(value, (Latency, Clock)):
677 if value.value == 0:
678 self.value = 0
679 else:
680 self.value = 1.0 / value.value
681 self.ticks = value.ticks
682 elif isinstance(value, Frequency):
683 self.value = value.value
684 self.ticks = value.ticks
685 else:
686 self.ticks = False
687 self.value = convert.toFrequency(value)
695
696 def __getattr__(self, attr):
697 if attr == 'frequency':
698 return self
699 if attr in ('latency', 'period'):
700 return Latency(self)
701 raise AttributeError, "Frequency object has no attribute '%s'" % attr
702
688
689 def __getattr__(self, attr):
690 if attr == 'frequency':
691 return self
692 if attr in ('latency', 'period'):
693 return Latency(self)
694 raise AttributeError, "Frequency object has no attribute '%s'" % attr
695
703 # convert frequency to ticks per period
696 # convert latency to ticks
704 def ini_str(self):
697 def ini_str(self):
705 return self.period.ini_str()
698 if self.ticks or self.value == 0:
699 return '%d' % self.value
700 else:
701 return '%d' % (ticks.fromSeconds(1.0 / self.value))
706
702
707# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
708# We can't inherit from Frequency because we don't want it to be directly
709# assignable to a regular Frequency parameter.
710class RootClock(ParamValue):
711 cxx_type = 'Tick'
712 cxx_predecls = ['#include "sim/host.hh"']
713 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
714 '%import "sim/host.hh"']
715 def __init__(self, value):
716 self.value = 1 / getLatency(value)
717
718 def __getattr__(self, attr):
719 if attr == 'frequency':
720 return Frequency(self)
721 if attr in ('latency', 'period'):
722 return Latency(self)
723 raise AttributeError, "Frequency object has no attribute '%s'" % attr
724
725 def ini_str(self):
726 return str(tick_check(self.value))
727
728# A generic frequency and/or Latency value. Value is stored as a latency,
729# but to avoid ambiguity this object does not support numeric ops (* or /).
730# An explicit conversion to a Latency or Frequency must be made first.
731class Clock(ParamValue):
732 cxx_type = 'Tick'
733 cxx_predecls = ['#include "sim/host.hh"']
734 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
735 '%import "sim/host.hh"']
736 def __init__(self, value):
703# A generic frequency and/or Latency value. Value is stored as a latency,
704# but to avoid ambiguity this object does not support numeric ops (* or /).
705# An explicit conversion to a Latency or Frequency must be made first.
706class Clock(ParamValue):
707 cxx_type = 'Tick'
708 cxx_predecls = ['#include "sim/host.hh"']
709 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
710 '%import "sim/host.hh"']
711 def __init__(self, value):
737 self.value = getLatency(value)
712 if isinstance(value, (Latency, Clock)):
713 self.ticks = value.ticks
714 self.value = value.value
715 elif isinstance(value, Frequency):
716 self.ticks = value.ticks
717 self.value = 1.0 / value.value
718 elif value.endswith('t'):
719 self.ticks = True
720 self.value = int(value[:-1])
721 else:
722 self.ticks = False
723 self.value = convert.anyToLatency(value)
738
739 def __getattr__(self, attr):
740 if attr == 'frequency':
741 return Frequency(self)
742 if attr in ('latency', 'period'):
743 return Latency(self)
744 raise AttributeError, "Frequency object has no attribute '%s'" % attr
745
746 def ini_str(self):
747 return self.period.ini_str()
748
749class NetworkBandwidth(float,ParamValue):
750 cxx_type = 'float'
751 def __new__(cls, value):
724
725 def __getattr__(self, attr):
726 if attr == 'frequency':
727 return Frequency(self)
728 if attr in ('latency', 'period'):
729 return Latency(self)
730 raise AttributeError, "Frequency object has no attribute '%s'" % attr
731
732 def ini_str(self):
733 return self.period.ini_str()
734
735class NetworkBandwidth(float,ParamValue):
736 cxx_type = 'float'
737 def __new__(cls, value):
752 val = convert.toNetworkBandwidth(value) / 8.0
738 # convert to bits per second
739 val = convert.toNetworkBandwidth(value)
753 return super(cls, NetworkBandwidth).__new__(cls, val)
754
755 def __str__(self):
756 return str(self.val)
757
758 def ini_str(self):
740 return super(cls, NetworkBandwidth).__new__(cls, val)
741
742 def __str__(self):
743 return str(self.val)
744
745 def ini_str(self):
759 return '%f' % (ticks_per_sec / float(self))
746 # convert to seconds per byte
747 value = 8.0 / float(self)
748 # convert to ticks per byte
749 return '%f' % (ticks.fromSeconds(value))
760
761class MemoryBandwidth(float,ParamValue):
762 cxx_type = 'float'
763 def __new__(self, value):
750
751class MemoryBandwidth(float,ParamValue):
752 cxx_type = 'float'
753 def __new__(self, value):
754 # we want the number of ticks per byte of data
764 val = convert.toMemoryBandwidth(value)
765 return super(cls, MemoryBandwidth).__new__(cls, val)
766
767 def __str__(self):
768 return str(self.val)
769
770 def ini_str(self):
755 val = convert.toMemoryBandwidth(value)
756 return super(cls, MemoryBandwidth).__new__(cls, val)
757
758 def __str__(self):
759 return str(self.val)
760
761 def ini_str(self):
771 return '%f' % (ticks_per_sec / float(self))
762 # convert to seconds per byte
763 value = 1.0 / float(self)
764 # convert to ticks per byte
765 return '%f' % (ticks.fromSeconds(value))
772
773#
774# "Constants"... handy aliases for various values.
775#
776
777# Special class for NULL pointers. Note the special check in
778# make_param_value() above that lets these be assigned where a
779# SimObject is required.
780# only one copy of a particular node
781class NullSimObject(object):
782 __metaclass__ = Singleton
783
784 def __call__(cls):
785 return cls
786
787 def _instantiate(self, parent = None, path = ''):
788 pass
789
790 def ini_str(self):
791 return 'Null'
792
793 def unproxy(self, base):
794 return self
795
796 def set_path(self, parent, name):
797 pass
798 def __str__(self):
799 return 'Null'
800
801# The only instance you'll ever need...
802NULL = NullSimObject()
803
804def isNullPointer(value):
805 return isinstance(value, NullSimObject)
806
807# Some memory range specifications use this as a default upper bound.
808MaxAddr = Addr.max
809MaxTick = Tick.max
810AllMemory = AddrRange(0, MaxAddr)
811
812
813#####################################################################
814#
815# Port objects
816#
817# Ports are used to interconnect objects in the memory system.
818#
819#####################################################################
820
821# Port reference: encapsulates a reference to a particular port on a
822# particular SimObject.
823class PortRef(object):
824 def __init__(self, simobj, name):
825 assert(isSimObject(simobj) or isSimObjectClass(simobj))
826 self.simobj = simobj
827 self.name = name
828 self.peer = None # not associated with another port yet
829 self.ccConnected = False # C++ port connection done?
830 self.index = -1 # always -1 for non-vector ports
831
832 def __str__(self):
833 return '%s.%s' % (self.simobj, self.name)
834
835 # for config.ini, print peer's name (not ours)
836 def ini_str(self):
837 return str(self.peer)
838
839 def __getattr__(self, attr):
840 if attr == 'peerObj':
841 # shorthand for proxies
842 return self.peer.simobj
843 raise AttributeError, "'%s' object has no attribute '%s'" % \
844 (self.__class__.__name__, attr)
845
846 # Full connection is symmetric (both ways). Called via
847 # SimObject.__setattr__ as a result of a port assignment, e.g.,
848 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
849 # e.g., "obj1.portA[3] = obj2.portB".
850 def connect(self, other):
851 if isinstance(other, VectorPortRef):
852 # reference to plain VectorPort is implicit append
853 other = other._get_next()
854 if self.peer and not proxy.isproxy(self.peer):
855 print "warning: overwriting port", self, \
856 "value", self.peer, "with", other
857 self.peer = other
858 if proxy.isproxy(other):
859 other.set_param_desc(PortParamDesc())
860 elif isinstance(other, PortRef):
861 if other.peer is not self:
862 other.connect(self)
863 else:
864 raise TypeError, \
865 "assigning non-port reference '%s' to port '%s'" \
866 % (other, self)
867
868 def clone(self, simobj, memo):
869 if memo.has_key(self):
870 return memo[self]
871 newRef = copy.copy(self)
872 memo[self] = newRef
873 newRef.simobj = simobj
874 assert(isSimObject(newRef.simobj))
875 if self.peer and not proxy.isproxy(self.peer):
876 peerObj = self.peer.simobj(_memo=memo)
877 newRef.peer = self.peer.clone(peerObj, memo)
878 assert(not isinstance(newRef.peer, VectorPortRef))
879 return newRef
880
881 def unproxy(self, simobj):
882 assert(simobj is self.simobj)
883 if proxy.isproxy(self.peer):
884 try:
885 realPeer = self.peer.unproxy(self.simobj)
886 except:
887 print "Error in unproxying port '%s' of %s" % \
888 (self.name, self.simobj.path())
889 raise
890 self.connect(realPeer)
891
892 # Call C++ to create corresponding port connection between C++ objects
893 def ccConnect(self):
894 if self.ccConnected: # already done this
895 return
896 peer = self.peer
897 internal.sim_object.connectPorts(self.simobj.getCCObject(), self.name,
898 self.index, peer.simobj.getCCObject(), peer.name, peer.index)
899 self.ccConnected = True
900 peer.ccConnected = True
901
902# A reference to an individual element of a VectorPort... much like a
903# PortRef, but has an index.
904class VectorPortElementRef(PortRef):
905 def __init__(self, simobj, name, index):
906 PortRef.__init__(self, simobj, name)
907 self.index = index
908
909 def __str__(self):
910 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
911
912# A reference to a complete vector-valued port (not just a single element).
913# Can be indexed to retrieve individual VectorPortElementRef instances.
914class VectorPortRef(object):
915 def __init__(self, simobj, name):
916 assert(isSimObject(simobj) or isSimObjectClass(simobj))
917 self.simobj = simobj
918 self.name = name
919 self.elements = []
920
921 def __str__(self):
922 return '%s.%s[:]' % (self.simobj, self.name)
923
924 # for config.ini, print peer's name (not ours)
925 def ini_str(self):
926 return ' '.join([el.ini_str() for el in self.elements])
927
928 def __getitem__(self, key):
929 if not isinstance(key, int):
930 raise TypeError, "VectorPort index must be integer"
931 if key >= len(self.elements):
932 # need to extend list
933 ext = [VectorPortElementRef(self.simobj, self.name, i)
934 for i in range(len(self.elements), key+1)]
935 self.elements.extend(ext)
936 return self.elements[key]
937
938 def _get_next(self):
939 return self[len(self.elements)]
940
941 def __setitem__(self, key, value):
942 if not isinstance(key, int):
943 raise TypeError, "VectorPort index must be integer"
944 self[key].connect(value)
945
946 def connect(self, other):
947 if isinstance(other, (list, tuple)):
948 # Assign list of port refs to vector port.
949 # For now, append them... not sure if that's the right semantics
950 # or if it should replace the current vector.
951 for ref in other:
952 self._get_next().connect(ref)
953 else:
954 # scalar assignment to plain VectorPort is implicit append
955 self._get_next().connect(other)
956
957 def clone(self, simobj, memo):
958 if memo.has_key(self):
959 return memo[self]
960 newRef = copy.copy(self)
961 memo[self] = newRef
962 newRef.simobj = simobj
963 assert(isSimObject(newRef.simobj))
964 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
965 return newRef
966
967 def unproxy(self, simobj):
968 [el.unproxy(simobj) for el in self.elements]
969
970 def ccConnect(self):
971 [el.ccConnect() for el in self.elements]
972
973# Port description object. Like a ParamDesc object, this represents a
974# logical port in the SimObject class, not a particular port on a
975# SimObject instance. The latter are represented by PortRef objects.
976class Port(object):
977 # Port("description") or Port(default, "description")
978 def __init__(self, *args):
979 if len(args) == 1:
980 self.desc = args[0]
981 elif len(args) == 2:
982 self.default = args[0]
983 self.desc = args[1]
984 else:
985 raise TypeError, 'wrong number of arguments'
986 # self.name is set by SimObject class on assignment
987 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
988
989 # Generate a PortRef for this port on the given SimObject with the
990 # given name
991 def makeRef(self, simobj):
992 return PortRef(simobj, self.name)
993
994 # Connect an instance of this port (on the given SimObject with
995 # the given name) with the port described by the supplied PortRef
996 def connect(self, simobj, ref):
997 self.makeRef(simobj).connect(ref)
998
999# VectorPort description object. Like Port, but represents a vector
1000# of connections (e.g., as on a Bus).
1001class VectorPort(Port):
1002 def __init__(self, *args):
1003 Port.__init__(self, *args)
1004 self.isVec = True
1005
1006 def makeRef(self, simobj):
1007 return VectorPortRef(simobj, self.name)
1008
1009# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1010# proxy objects (via set_param_desc()) so that proxy error messages
1011# make sense.
1012class PortParamDesc(object):
1013 __metaclass__ = Singleton
1014
1015 ptype_str = 'Port'
1016 ptype = Port
1017
1018
1019__all__ = ['Param', 'VectorParam',
1020 'Enum', 'Bool', 'String', 'Float',
1021 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1022 'Int32', 'UInt32', 'Int64', 'UInt64',
1023 'Counter', 'Addr', 'Tick', 'Percent',
1024 'TcpPort', 'UdpPort', 'EthernetAddr',
1025 'MemorySize', 'MemorySize32',
766
767#
768# "Constants"... handy aliases for various values.
769#
770
771# Special class for NULL pointers. Note the special check in
772# make_param_value() above that lets these be assigned where a
773# SimObject is required.
774# only one copy of a particular node
775class NullSimObject(object):
776 __metaclass__ = Singleton
777
778 def __call__(cls):
779 return cls
780
781 def _instantiate(self, parent = None, path = ''):
782 pass
783
784 def ini_str(self):
785 return 'Null'
786
787 def unproxy(self, base):
788 return self
789
790 def set_path(self, parent, name):
791 pass
792 def __str__(self):
793 return 'Null'
794
795# The only instance you'll ever need...
796NULL = NullSimObject()
797
798def isNullPointer(value):
799 return isinstance(value, NullSimObject)
800
801# Some memory range specifications use this as a default upper bound.
802MaxAddr = Addr.max
803MaxTick = Tick.max
804AllMemory = AddrRange(0, MaxAddr)
805
806
807#####################################################################
808#
809# Port objects
810#
811# Ports are used to interconnect objects in the memory system.
812#
813#####################################################################
814
815# Port reference: encapsulates a reference to a particular port on a
816# particular SimObject.
817class PortRef(object):
818 def __init__(self, simobj, name):
819 assert(isSimObject(simobj) or isSimObjectClass(simobj))
820 self.simobj = simobj
821 self.name = name
822 self.peer = None # not associated with another port yet
823 self.ccConnected = False # C++ port connection done?
824 self.index = -1 # always -1 for non-vector ports
825
826 def __str__(self):
827 return '%s.%s' % (self.simobj, self.name)
828
829 # for config.ini, print peer's name (not ours)
830 def ini_str(self):
831 return str(self.peer)
832
833 def __getattr__(self, attr):
834 if attr == 'peerObj':
835 # shorthand for proxies
836 return self.peer.simobj
837 raise AttributeError, "'%s' object has no attribute '%s'" % \
838 (self.__class__.__name__, attr)
839
840 # Full connection is symmetric (both ways). Called via
841 # SimObject.__setattr__ as a result of a port assignment, e.g.,
842 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
843 # e.g., "obj1.portA[3] = obj2.portB".
844 def connect(self, other):
845 if isinstance(other, VectorPortRef):
846 # reference to plain VectorPort is implicit append
847 other = other._get_next()
848 if self.peer and not proxy.isproxy(self.peer):
849 print "warning: overwriting port", self, \
850 "value", self.peer, "with", other
851 self.peer = other
852 if proxy.isproxy(other):
853 other.set_param_desc(PortParamDesc())
854 elif isinstance(other, PortRef):
855 if other.peer is not self:
856 other.connect(self)
857 else:
858 raise TypeError, \
859 "assigning non-port reference '%s' to port '%s'" \
860 % (other, self)
861
862 def clone(self, simobj, memo):
863 if memo.has_key(self):
864 return memo[self]
865 newRef = copy.copy(self)
866 memo[self] = newRef
867 newRef.simobj = simobj
868 assert(isSimObject(newRef.simobj))
869 if self.peer and not proxy.isproxy(self.peer):
870 peerObj = self.peer.simobj(_memo=memo)
871 newRef.peer = self.peer.clone(peerObj, memo)
872 assert(not isinstance(newRef.peer, VectorPortRef))
873 return newRef
874
875 def unproxy(self, simobj):
876 assert(simobj is self.simobj)
877 if proxy.isproxy(self.peer):
878 try:
879 realPeer = self.peer.unproxy(self.simobj)
880 except:
881 print "Error in unproxying port '%s' of %s" % \
882 (self.name, self.simobj.path())
883 raise
884 self.connect(realPeer)
885
886 # Call C++ to create corresponding port connection between C++ objects
887 def ccConnect(self):
888 if self.ccConnected: # already done this
889 return
890 peer = self.peer
891 internal.sim_object.connectPorts(self.simobj.getCCObject(), self.name,
892 self.index, peer.simobj.getCCObject(), peer.name, peer.index)
893 self.ccConnected = True
894 peer.ccConnected = True
895
896# A reference to an individual element of a VectorPort... much like a
897# PortRef, but has an index.
898class VectorPortElementRef(PortRef):
899 def __init__(self, simobj, name, index):
900 PortRef.__init__(self, simobj, name)
901 self.index = index
902
903 def __str__(self):
904 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
905
906# A reference to a complete vector-valued port (not just a single element).
907# Can be indexed to retrieve individual VectorPortElementRef instances.
908class VectorPortRef(object):
909 def __init__(self, simobj, name):
910 assert(isSimObject(simobj) or isSimObjectClass(simobj))
911 self.simobj = simobj
912 self.name = name
913 self.elements = []
914
915 def __str__(self):
916 return '%s.%s[:]' % (self.simobj, self.name)
917
918 # for config.ini, print peer's name (not ours)
919 def ini_str(self):
920 return ' '.join([el.ini_str() for el in self.elements])
921
922 def __getitem__(self, key):
923 if not isinstance(key, int):
924 raise TypeError, "VectorPort index must be integer"
925 if key >= len(self.elements):
926 # need to extend list
927 ext = [VectorPortElementRef(self.simobj, self.name, i)
928 for i in range(len(self.elements), key+1)]
929 self.elements.extend(ext)
930 return self.elements[key]
931
932 def _get_next(self):
933 return self[len(self.elements)]
934
935 def __setitem__(self, key, value):
936 if not isinstance(key, int):
937 raise TypeError, "VectorPort index must be integer"
938 self[key].connect(value)
939
940 def connect(self, other):
941 if isinstance(other, (list, tuple)):
942 # Assign list of port refs to vector port.
943 # For now, append them... not sure if that's the right semantics
944 # or if it should replace the current vector.
945 for ref in other:
946 self._get_next().connect(ref)
947 else:
948 # scalar assignment to plain VectorPort is implicit append
949 self._get_next().connect(other)
950
951 def clone(self, simobj, memo):
952 if memo.has_key(self):
953 return memo[self]
954 newRef = copy.copy(self)
955 memo[self] = newRef
956 newRef.simobj = simobj
957 assert(isSimObject(newRef.simobj))
958 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
959 return newRef
960
961 def unproxy(self, simobj):
962 [el.unproxy(simobj) for el in self.elements]
963
964 def ccConnect(self):
965 [el.ccConnect() for el in self.elements]
966
967# Port description object. Like a ParamDesc object, this represents a
968# logical port in the SimObject class, not a particular port on a
969# SimObject instance. The latter are represented by PortRef objects.
970class Port(object):
971 # Port("description") or Port(default, "description")
972 def __init__(self, *args):
973 if len(args) == 1:
974 self.desc = args[0]
975 elif len(args) == 2:
976 self.default = args[0]
977 self.desc = args[1]
978 else:
979 raise TypeError, 'wrong number of arguments'
980 # self.name is set by SimObject class on assignment
981 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
982
983 # Generate a PortRef for this port on the given SimObject with the
984 # given name
985 def makeRef(self, simobj):
986 return PortRef(simobj, self.name)
987
988 # Connect an instance of this port (on the given SimObject with
989 # the given name) with the port described by the supplied PortRef
990 def connect(self, simobj, ref):
991 self.makeRef(simobj).connect(ref)
992
993# VectorPort description object. Like Port, but represents a vector
994# of connections (e.g., as on a Bus).
995class VectorPort(Port):
996 def __init__(self, *args):
997 Port.__init__(self, *args)
998 self.isVec = True
999
1000 def makeRef(self, simobj):
1001 return VectorPortRef(simobj, self.name)
1002
1003# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1004# proxy objects (via set_param_desc()) so that proxy error messages
1005# make sense.
1006class PortParamDesc(object):
1007 __metaclass__ = Singleton
1008
1009 ptype_str = 'Port'
1010 ptype = Port
1011
1012
1013__all__ = ['Param', 'VectorParam',
1014 'Enum', 'Bool', 'String', 'Float',
1015 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1016 'Int32', 'UInt32', 'Int64', 'UInt64',
1017 'Counter', 'Addr', 'Tick', 'Percent',
1018 'TcpPort', 'UdpPort', 'EthernetAddr',
1019 'MemorySize', 'MemorySize32',
1026 'Latency', 'Frequency', 'RootClock', 'Clock',
1020 'Latency', 'Frequency', 'Clock',
1027 'NetworkBandwidth', 'MemoryBandwidth',
1028 'Range', 'AddrRange', 'TickRange',
1029 'MaxAddr', 'MaxTick', 'AllMemory',
1030 'Time',
1031 'NextEthernetAddr', 'NULL',
1032 'Port', 'VectorPort']
1033
1034# see comment on imports at end of __init__.py.
1035from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
1036import proxy
1037import objects
1038import internal
1021 'NetworkBandwidth', 'MemoryBandwidth',
1022 'Range', 'AddrRange', 'TickRange',
1023 'MaxAddr', 'MaxTick', 'AllMemory',
1024 'Time',
1025 'NextEthernetAddr', 'NULL',
1026 'Port', 'VectorPort']
1027
1028# see comment on imports at end of __init__.py.
1029from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
1030import proxy
1031import objects
1032import internal