params.py (7526:4bb5f5207617) params.py (7528:6efc3672733b)
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 re
50import sys
51import time
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 re
50import sys
51import time
52import math
52
53import proxy
54import ticks
55from util import *
56
57def isSimObject(*args, **kwargs):
58 return SimObject.isSimObject(*args, **kwargs)
59
60def isSimObjectSequence(*args, **kwargs):
61 return SimObject.isSimObjectSequence(*args, **kwargs)
62
63def isSimObjectClass(*args, **kwargs):
64 return SimObject.isSimObjectClass(*args, **kwargs)
65
66allParams = {}
67
68class MetaParamValue(type):
69 def __new__(mcls, name, bases, dct):
70 cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
71 assert name not in allParams
72 allParams[name] = cls
73 return cls
74
75
76# Dummy base class to identify types that are legitimate for SimObject
77# parameters.
78class ParamValue(object):
79 __metaclass__ = MetaParamValue
80
81 cxx_predecls = []
82 swig_predecls = []
83
84 # default for printing to .ini file is regular string conversion.
85 # will be overridden in some cases
86 def ini_str(self):
87 return str(self)
88
89 # allows us to blithely call unproxy() on things without checking
90 # if they're really proxies or not
91 def unproxy(self, base):
92 return self
93
94# Regular parameter description.
95class ParamDesc(object):
96 file_ext = 'ptype'
97
98 def __init__(self, ptype_str, ptype, *args, **kwargs):
99 self.ptype_str = ptype_str
100 # remember ptype only if it is provided
101 if ptype != None:
102 self.ptype = ptype
103
104 if args:
105 if len(args) == 1:
106 self.desc = args[0]
107 elif len(args) == 2:
108 self.default = args[0]
109 self.desc = args[1]
110 else:
111 raise TypeError, 'too many arguments'
112
113 if kwargs.has_key('desc'):
114 assert(not hasattr(self, 'desc'))
115 self.desc = kwargs['desc']
116 del kwargs['desc']
117
118 if kwargs.has_key('default'):
119 assert(not hasattr(self, 'default'))
120 self.default = kwargs['default']
121 del kwargs['default']
122
123 if kwargs:
124 raise TypeError, 'extra unknown kwargs %s' % kwargs
125
126 if not hasattr(self, 'desc'):
127 raise TypeError, 'desc attribute missing'
128
129 def __getattr__(self, attr):
130 if attr == 'ptype':
131 ptype = SimObject.allClasses[self.ptype_str]
132 assert isSimObjectClass(ptype)
133 self.ptype = ptype
134 return ptype
135
136 raise AttributeError, "'%s' object has no attribute '%s'" % \
137 (type(self).__name__, attr)
138
139 def convert(self, value):
140 if isinstance(value, proxy.BaseProxy):
141 value.set_param_desc(self)
142 return value
143 if not hasattr(self, 'ptype') and isNullPointer(value):
144 # deferred evaluation of SimObject; continue to defer if
145 # we're just assigning a null pointer
146 return value
147 if isinstance(value, self.ptype):
148 return value
149 if isNullPointer(value) and isSimObjectClass(self.ptype):
150 return value
151 return self.ptype(value)
152
153 def cxx_predecls(self):
154 return self.ptype.cxx_predecls
155
156 def swig_predecls(self):
157 return self.ptype.swig_predecls
158
159 def cxx_decl(self):
160 return '%s %s;' % (self.ptype.cxx_type, self.name)
161
162# Vector-valued parameter description. Just like ParamDesc, except
163# that the value is a vector (list) of the specified type instead of a
164# single value.
165
166class VectorParamValue(list):
167 __metaclass__ = MetaParamValue
168 def __setattr__(self, attr, value):
169 raise AttributeError, \
170 "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
171
172 def ini_str(self):
173 return ' '.join([v.ini_str() for v in self])
174
175 def getValue(self):
176 return [ v.getValue() for v in self ]
177
178 def unproxy(self, base):
179 return [v.unproxy(base) for v in self]
180
53
54import proxy
55import ticks
56from util import *
57
58def isSimObject(*args, **kwargs):
59 return SimObject.isSimObject(*args, **kwargs)
60
61def isSimObjectSequence(*args, **kwargs):
62 return SimObject.isSimObjectSequence(*args, **kwargs)
63
64def isSimObjectClass(*args, **kwargs):
65 return SimObject.isSimObjectClass(*args, **kwargs)
66
67allParams = {}
68
69class MetaParamValue(type):
70 def __new__(mcls, name, bases, dct):
71 cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
72 assert name not in allParams
73 allParams[name] = cls
74 return cls
75
76
77# Dummy base class to identify types that are legitimate for SimObject
78# parameters.
79class ParamValue(object):
80 __metaclass__ = MetaParamValue
81
82 cxx_predecls = []
83 swig_predecls = []
84
85 # default for printing to .ini file is regular string conversion.
86 # will be overridden in some cases
87 def ini_str(self):
88 return str(self)
89
90 # allows us to blithely call unproxy() on things without checking
91 # if they're really proxies or not
92 def unproxy(self, base):
93 return self
94
95# Regular parameter description.
96class ParamDesc(object):
97 file_ext = 'ptype'
98
99 def __init__(self, ptype_str, ptype, *args, **kwargs):
100 self.ptype_str = ptype_str
101 # remember ptype only if it is provided
102 if ptype != None:
103 self.ptype = ptype
104
105 if args:
106 if len(args) == 1:
107 self.desc = args[0]
108 elif len(args) == 2:
109 self.default = args[0]
110 self.desc = args[1]
111 else:
112 raise TypeError, 'too many arguments'
113
114 if kwargs.has_key('desc'):
115 assert(not hasattr(self, 'desc'))
116 self.desc = kwargs['desc']
117 del kwargs['desc']
118
119 if kwargs.has_key('default'):
120 assert(not hasattr(self, 'default'))
121 self.default = kwargs['default']
122 del kwargs['default']
123
124 if kwargs:
125 raise TypeError, 'extra unknown kwargs %s' % kwargs
126
127 if not hasattr(self, 'desc'):
128 raise TypeError, 'desc attribute missing'
129
130 def __getattr__(self, attr):
131 if attr == 'ptype':
132 ptype = SimObject.allClasses[self.ptype_str]
133 assert isSimObjectClass(ptype)
134 self.ptype = ptype
135 return ptype
136
137 raise AttributeError, "'%s' object has no attribute '%s'" % \
138 (type(self).__name__, attr)
139
140 def convert(self, value):
141 if isinstance(value, proxy.BaseProxy):
142 value.set_param_desc(self)
143 return value
144 if not hasattr(self, 'ptype') and isNullPointer(value):
145 # deferred evaluation of SimObject; continue to defer if
146 # we're just assigning a null pointer
147 return value
148 if isinstance(value, self.ptype):
149 return value
150 if isNullPointer(value) and isSimObjectClass(self.ptype):
151 return value
152 return self.ptype(value)
153
154 def cxx_predecls(self):
155 return self.ptype.cxx_predecls
156
157 def swig_predecls(self):
158 return self.ptype.swig_predecls
159
160 def cxx_decl(self):
161 return '%s %s;' % (self.ptype.cxx_type, self.name)
162
163# Vector-valued parameter description. Just like ParamDesc, except
164# that the value is a vector (list) of the specified type instead of a
165# single value.
166
167class VectorParamValue(list):
168 __metaclass__ = MetaParamValue
169 def __setattr__(self, attr, value):
170 raise AttributeError, \
171 "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
172
173 def ini_str(self):
174 return ' '.join([v.ini_str() for v in self])
175
176 def getValue(self):
177 return [ v.getValue() for v in self ]
178
179 def unproxy(self, base):
180 return [v.unproxy(base) for v in self]
181
181class SimObjVector(VectorParamValue):
182 def print_ini(self, ini_file):
182class SimObjectVector(VectorParamValue):
183 # support clone operation
184 def __call__(self, **kwargs):
185 return SimObjectVector([v(**kwargs) for v in self])
186
187 def clear_parent(self, old_parent):
183 for v in self:
188 for v in self:
184 v.print_ini(ini_file)
189 v.clear_parent(old_parent)
185
190
191 def set_parent(self, parent, name):
192 if len(self) == 1:
193 self[0].set_parent(parent, name)
194 else:
195 width = int(math.ceil(math.log(len(self))/math.log(10)))
196 for i,v in enumerate(self):
197 v.set_parent(parent, "%s%0*d" % (name, width, i))
198
199 def get_parent(self):
200 parent_set = set(v._parent for v in self)
201 if len(parent_set) != 1:
202 raise RuntimeError, \
203 "SimObjectVector elements have inconsistent parent value."
204 return parent_set.pop()
205
206 # return 'cpu0 cpu1' etc. for print_ini()
207 def get_name(self):
208 return ' '.join([v._name for v in self])
209
210 # By iterating through the constituent members of the vector here
211 # we can nicely handle iterating over all a SimObject's children
212 # without having to provide lots of special functions on
213 # SimObjectVector directly.
214 def descendants(self):
215 for v in self:
216 for obj in v.descendants():
217 yield obj
218
186class VectorParamDesc(ParamDesc):
187 file_ext = 'vptype'
188
189 # Convert assigned value to appropriate type. If the RHS is not a
190 # list or tuple, it generates a single-element list.
191 def convert(self, value):
192 if isinstance(value, (list, tuple)):
193 # list: coerce each element into new list
194 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
195 else:
196 # singleton: coerce to a single-element list
197 tmp_list = [ ParamDesc.convert(self, value) ]
198
199 if isSimObjectSequence(tmp_list):
219class VectorParamDesc(ParamDesc):
220 file_ext = 'vptype'
221
222 # Convert assigned value to appropriate type. If the RHS is not a
223 # list or tuple, it generates a single-element list.
224 def convert(self, value):
225 if isinstance(value, (list, tuple)):
226 # list: coerce each element into new list
227 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
228 else:
229 # singleton: coerce to a single-element list
230 tmp_list = [ ParamDesc.convert(self, value) ]
231
232 if isSimObjectSequence(tmp_list):
200 return SimObjVector(tmp_list)
233 return SimObjectVector(tmp_list)
201 else:
202 return VectorParamValue(tmp_list)
203
204 def swig_predecls(self):
205 return ['%%include "%s_vptype.i"' % self.ptype_str]
206
207 def swig_decl(self):
208 cxx_type = re.sub('std::', '', self.ptype.cxx_type)
209 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
210 (self.ptype_str, cxx_type)
211 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
212
213 def cxx_predecls(self):
214 return ['#include <vector>'] + self.ptype.cxx_predecls
215
216 def cxx_decl(self):
217 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
218
219class ParamFactory(object):
220 def __init__(self, param_desc_class, ptype_str = None):
221 self.param_desc_class = param_desc_class
222 self.ptype_str = ptype_str
223
224 def __getattr__(self, attr):
225 if self.ptype_str:
226 attr = self.ptype_str + '.' + attr
227 return ParamFactory(self.param_desc_class, attr)
228
229 # E.g., Param.Int(5, "number of widgets")
230 def __call__(self, *args, **kwargs):
231 ptype = None
232 try:
233 ptype = allParams[self.ptype_str]
234 except KeyError:
235 # if name isn't defined yet, assume it's a SimObject, and
236 # try to resolve it later
237 pass
238 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
239
240Param = ParamFactory(ParamDesc)
241VectorParam = ParamFactory(VectorParamDesc)
242
243#####################################################################
244#
245# Parameter Types
246#
247# Though native Python types could be used to specify parameter types
248# (the 'ptype' field of the Param and VectorParam classes), it's more
249# flexible to define our own set of types. This gives us more control
250# over how Python expressions are converted to values (via the
251# __init__() constructor) and how these values are printed out (via
252# the __str__() conversion method).
253#
254#####################################################################
255
256# String-valued parameter. Just mixin the ParamValue class with the
257# built-in str class.
258class String(ParamValue,str):
259 cxx_type = 'std::string'
260 cxx_predecls = ['#include <string>']
261 swig_predecls = ['%include "std_string.i"\n' +
262 '%apply const std::string& {std::string *};']
263 swig_predecls = ['%include "std_string.i"' ]
264
265 def getValue(self):
266 return self
267
268# superclass for "numeric" parameter values, to emulate math
269# operations in a type-safe way. e.g., a Latency times an int returns
270# a new Latency object.
271class NumericParamValue(ParamValue):
272 def __str__(self):
273 return str(self.value)
274
275 def __float__(self):
276 return float(self.value)
277
278 def __long__(self):
279 return long(self.value)
280
281 def __int__(self):
282 return int(self.value)
283
284 # hook for bounds checking
285 def _check(self):
286 return
287
288 def __mul__(self, other):
289 newobj = self.__class__(self)
290 newobj.value *= other
291 newobj._check()
292 return newobj
293
294 __rmul__ = __mul__
295
296 def __div__(self, other):
297 newobj = self.__class__(self)
298 newobj.value /= other
299 newobj._check()
300 return newobj
301
302 def __sub__(self, other):
303 newobj = self.__class__(self)
304 newobj.value -= other
305 newobj._check()
306 return newobj
307
308# Metaclass for bounds-checked integer parameters. See CheckedInt.
309class CheckedIntType(MetaParamValue):
310 def __init__(cls, name, bases, dict):
311 super(CheckedIntType, cls).__init__(name, bases, dict)
312
313 # CheckedInt is an abstract base class, so we actually don't
314 # want to do any processing on it... the rest of this code is
315 # just for classes that derive from CheckedInt.
316 if name == 'CheckedInt':
317 return
318
319 if not cls.cxx_predecls:
320 # most derived types require this, so we just do it here once
321 cls.cxx_predecls = ['#include "base/types.hh"']
322
323 if not cls.swig_predecls:
324 # most derived types require this, so we just do it here once
325 cls.swig_predecls = ['%import "stdint.i"\n' +
326 '%import "base/types.hh"']
327
328 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
329 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
330 panic("CheckedInt subclass %s must define either\n" \
331 " 'min' and 'max' or 'size' and 'unsigned'\n",
332 name);
333 if cls.unsigned:
334 cls.min = 0
335 cls.max = 2 ** cls.size - 1
336 else:
337 cls.min = -(2 ** (cls.size - 1))
338 cls.max = (2 ** (cls.size - 1)) - 1
339
340# Abstract superclass for bounds-checked integer parameters. This
341# class is subclassed to generate parameter classes with specific
342# bounds. Initialization of the min and max bounds is done in the
343# metaclass CheckedIntType.__init__.
344class CheckedInt(NumericParamValue):
345 __metaclass__ = CheckedIntType
346
347 def _check(self):
348 if not self.min <= self.value <= self.max:
349 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
350 (self.min, self.value, self.max)
351
352 def __init__(self, value):
353 if isinstance(value, str):
354 self.value = convert.toInteger(value)
355 elif isinstance(value, (int, long, float, NumericParamValue)):
356 self.value = long(value)
357 else:
358 raise TypeError, "Can't convert object of type %s to CheckedInt" \
359 % type(value).__name__
360 self._check()
361
362 def getValue(self):
363 return long(self.value)
364
365class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
366class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
367
368class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
369class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
370class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
371class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
372class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
373class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
374class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
375class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
376
377class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
378class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
379class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
380class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
381
382class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
383
384class Float(ParamValue, float):
385 cxx_type = 'double'
386
387 def __init__(self, value):
388 if isinstance(value, (int, long, float, NumericParamValue, Float)):
389 self.value = float(value)
390 else:
391 raise TypeError, "Can't convert object of type %s to Float" \
392 % type(value).__name__
393
394 def getValue(self):
395 return float(self.value)
396
397class MemorySize(CheckedInt):
398 cxx_type = 'uint64_t'
399 size = 64
400 unsigned = True
401 def __init__(self, value):
402 if isinstance(value, MemorySize):
403 self.value = value.value
404 else:
405 self.value = convert.toMemorySize(value)
406 self._check()
407
408class MemorySize32(CheckedInt):
409 cxx_type = 'uint32_t'
410 size = 32
411 unsigned = True
412 def __init__(self, value):
413 if isinstance(value, MemorySize):
414 self.value = value.value
415 else:
416 self.value = convert.toMemorySize(value)
417 self._check()
418
419class Addr(CheckedInt):
420 cxx_type = 'Addr'
421 size = 64
422 unsigned = True
423 def __init__(self, value):
424 if isinstance(value, Addr):
425 self.value = value.value
426 else:
427 try:
428 self.value = convert.toMemorySize(value)
429 except TypeError:
430 self.value = long(value)
431 self._check()
432 def __add__(self, other):
433 if isinstance(other, Addr):
434 return self.value + other.value
435 else:
436 return self.value + other
437
438
439class MetaRange(MetaParamValue):
440 def __init__(cls, name, bases, dict):
441 super(MetaRange, cls).__init__(name, bases, dict)
442 if name == 'Range':
443 return
444 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
445 cls.cxx_predecls = \
446 ['#include "base/range.hh"'] + cls.type.cxx_predecls
447
448class Range(ParamValue):
449 __metaclass__ = MetaRange
450 type = Int # default; can be overridden in subclasses
451 def __init__(self, *args, **kwargs):
452 def handle_kwargs(self, kwargs):
453 if 'end' in kwargs:
454 self.second = self.type(kwargs.pop('end'))
455 elif 'size' in kwargs:
456 self.second = self.first + self.type(kwargs.pop('size')) - 1
457 else:
458 raise TypeError, "Either end or size must be specified"
459
460 if len(args) == 0:
461 self.first = self.type(kwargs.pop('start'))
462 handle_kwargs(self, kwargs)
463
464 elif len(args) == 1:
465 if kwargs:
466 self.first = self.type(args[0])
467 handle_kwargs(self, kwargs)
468 elif isinstance(args[0], Range):
469 self.first = self.type(args[0].first)
470 self.second = self.type(args[0].second)
471 elif isinstance(args[0], (list, tuple)):
472 self.first = self.type(args[0][0])
473 self.second = self.type(args[0][1])
474 else:
475 self.first = self.type(0)
476 self.second = self.type(args[0]) - 1
477
478 elif len(args) == 2:
479 self.first = self.type(args[0])
480 self.second = self.type(args[1])
481 else:
482 raise TypeError, "Too many arguments specified"
483
484 if kwargs:
485 raise TypeError, "too many keywords: %s" % kwargs.keys()
486
487 def __str__(self):
488 return '%s:%s' % (self.first, self.second)
489
490class AddrRange(Range):
491 type = Addr
492 swig_predecls = ['%include "python/swig/range.i"']
493
494 def getValue(self):
495 from m5.objects.params import AddrRange
496
497 value = AddrRange()
498 value.start = long(self.first)
499 value.end = long(self.second)
500 return value
501
502class TickRange(Range):
503 type = Tick
504 swig_predecls = ['%include "python/swig/range.i"']
505
506 def getValue(self):
507 from m5.objects.params import TickRange
508
509 value = TickRange()
510 value.start = long(self.first)
511 value.end = long(self.second)
512 return value
513
514# Boolean parameter type. Python doesn't let you subclass bool, since
515# it doesn't want to let you create multiple instances of True and
516# False. Thus this is a little more complicated than String.
517class Bool(ParamValue):
518 cxx_type = 'bool'
519 def __init__(self, value):
520 try:
521 self.value = convert.toBool(value)
522 except TypeError:
523 self.value = bool(value)
524
525 def getValue(self):
526 return bool(self.value)
527
528 def __str__(self):
529 return str(self.value)
530
531 def ini_str(self):
532 if self.value:
533 return 'true'
534 return 'false'
535
536def IncEthernetAddr(addr, val = 1):
537 bytes = map(lambda x: int(x, 16), addr.split(':'))
538 bytes[5] += val
539 for i in (5, 4, 3, 2, 1):
540 val,rem = divmod(bytes[i], 256)
541 bytes[i] = rem
542 if val == 0:
543 break
544 bytes[i - 1] += val
545 assert(bytes[0] <= 255)
546 return ':'.join(map(lambda x: '%02x' % x, bytes))
547
548_NextEthernetAddr = "00:90:00:00:00:01"
549def NextEthernetAddr():
550 global _NextEthernetAddr
551
552 value = _NextEthernetAddr
553 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
554 return value
555
556class EthernetAddr(ParamValue):
557 cxx_type = 'Net::EthAddr'
558 cxx_predecls = ['#include "base/inet.hh"']
559 swig_predecls = ['%include "python/swig/inet.i"']
560 def __init__(self, value):
561 if value == NextEthernetAddr:
562 self.value = value
563 return
564
565 if not isinstance(value, str):
566 raise TypeError, "expected an ethernet address and didn't get one"
567
568 bytes = value.split(':')
569 if len(bytes) != 6:
570 raise TypeError, 'invalid ethernet address %s' % value
571
572 for byte in bytes:
573 if not 0 <= int(byte) <= 256:
574 raise TypeError, 'invalid ethernet address %s' % value
575
576 self.value = value
577
578 def unproxy(self, base):
579 if self.value == NextEthernetAddr:
580 return EthernetAddr(self.value())
581 return self
582
583 def getValue(self):
584 from m5.objects.params import EthAddr
585 return EthAddr(self.value)
586
587 def ini_str(self):
588 return self.value
589
590time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
591 "%a %b %d %H:%M:%S %Z %Y",
592 "%Y/%m/%d %H:%M:%S",
593 "%Y/%m/%d %H:%M",
594 "%Y/%m/%d",
595 "%m/%d/%Y %H:%M:%S",
596 "%m/%d/%Y %H:%M",
597 "%m/%d/%Y",
598 "%m/%d/%y %H:%M:%S",
599 "%m/%d/%y %H:%M",
600 "%m/%d/%y"]
601
602
603def parse_time(value):
604 from time import gmtime, strptime, struct_time, time
605 from datetime import datetime, date
606
607 if isinstance(value, struct_time):
608 return value
609
610 if isinstance(value, (int, long)):
611 return gmtime(value)
612
613 if isinstance(value, (datetime, date)):
614 return value.timetuple()
615
616 if isinstance(value, str):
617 if value in ('Now', 'Today'):
618 return time.gmtime(time.time())
619
620 for format in time_formats:
621 try:
622 return strptime(value, format)
623 except ValueError:
624 pass
625
626 raise ValueError, "Could not parse '%s' as a time" % value
627
628class Time(ParamValue):
629 cxx_type = 'tm'
630 cxx_predecls = [ '#include <time.h>' ]
631 swig_predecls = [ '%include "python/swig/time.i"' ]
632 def __init__(self, value):
633 self.value = parse_time(value)
634
635 def getValue(self):
636 from m5.objects.params import tm
637
638 c_time = tm()
639 py_time = self.value
640
641 # UNIX is years since 1900
642 c_time.tm_year = py_time.tm_year - 1900;
643
644 # Python starts at 1, UNIX starts at 0
645 c_time.tm_mon = py_time.tm_mon - 1;
646 c_time.tm_mday = py_time.tm_mday;
647 c_time.tm_hour = py_time.tm_hour;
648 c_time.tm_min = py_time.tm_min;
649 c_time.tm_sec = py_time.tm_sec;
650
651 # Python has 0 as Monday, UNIX is 0 as sunday
652 c_time.tm_wday = py_time.tm_wday + 1
653 if c_time.tm_wday > 6:
654 c_time.tm_wday -= 7;
655
656 # Python starts at 1, Unix starts at 0
657 c_time.tm_yday = py_time.tm_yday - 1;
658
659 return c_time
660
661 def __str__(self):
662 return time.asctime(self.value)
663
664 def ini_str(self):
665 return str(self)
666
667# Enumerated types are a little more complex. The user specifies the
668# type as Enum(foo) where foo is either a list or dictionary of
669# alternatives (typically strings, but not necessarily so). (In the
670# long run, the integer value of the parameter will be the list index
671# or the corresponding dictionary value. For now, since we only check
672# that the alternative is valid and then spit it into a .ini file,
673# there's not much point in using the dictionary.)
674
675# What Enum() must do is generate a new type encapsulating the
676# provided list/dictionary so that specific values of the parameter
677# can be instances of that type. We define two hidden internal
678# classes (_ListEnum and _DictEnum) to serve as base classes, then
679# derive the new type from the appropriate base class on the fly.
680
681allEnums = {}
682# Metaclass for Enum types
683class MetaEnum(MetaParamValue):
684 def __new__(mcls, name, bases, dict):
685 assert name not in allEnums
686
687 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
688 allEnums[name] = cls
689 return cls
690
691 def __init__(cls, name, bases, init_dict):
692 if init_dict.has_key('map'):
693 if not isinstance(cls.map, dict):
694 raise TypeError, "Enum-derived class attribute 'map' " \
695 "must be of type dict"
696 # build list of value strings from map
697 cls.vals = cls.map.keys()
698 cls.vals.sort()
699 elif init_dict.has_key('vals'):
700 if not isinstance(cls.vals, list):
701 raise TypeError, "Enum-derived class attribute 'vals' " \
702 "must be of type list"
703 # build string->value map from vals sequence
704 cls.map = {}
705 for idx,val in enumerate(cls.vals):
706 cls.map[val] = idx
707 else:
708 raise TypeError, "Enum-derived class must define "\
709 "attribute 'map' or 'vals'"
710
711 cls.cxx_type = 'Enums::%s' % name
712
713 super(MetaEnum, cls).__init__(name, bases, init_dict)
714
715 # Generate C++ class declaration for this enum type.
716 # Note that we wrap the enum in a class/struct to act as a namespace,
717 # so that the enum strings can be brief w/o worrying about collisions.
718 def cxx_decl(cls):
719 name = cls.__name__
720 code = "#ifndef __ENUM__%s\n" % name
721 code += '#define __ENUM__%s\n' % name
722 code += '\n'
723 code += 'namespace Enums {\n'
724 code += ' enum %s {\n' % name
725 for val in cls.vals:
726 code += ' %s = %d,\n' % (val, cls.map[val])
727 code += ' Num_%s = %d,\n' % (name, len(cls.vals))
728 code += ' };\n'
729 code += ' extern const char *%sStrings[Num_%s];\n' % (name, name)
730 code += '}\n'
731 code += '\n'
732 code += '#endif\n'
733 return code
734
735 def cxx_def(cls):
736 name = cls.__name__
737 code = '#include "enums/%s.hh"\n' % name
738 code += 'namespace Enums {\n'
739 code += ' const char *%sStrings[Num_%s] =\n' % (name, name)
740 code += ' {\n'
741 for val in cls.vals:
742 code += ' "%s",\n' % val
743 code += ' };\n'
744 code += '}\n'
745 return code
746
747# Base class for enum types.
748class Enum(ParamValue):
749 __metaclass__ = MetaEnum
750 vals = []
751
752 def __init__(self, value):
753 if value not in self.map:
754 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
755 % (value, self.vals)
756 self.value = value
757
758 def getValue(self):
759 return int(self.map[self.value])
760
761 def __str__(self):
762 return self.value
763
764# how big does a rounding error need to be before we warn about it?
765frequency_tolerance = 0.001 # 0.1%
766
767class TickParamValue(NumericParamValue):
768 cxx_type = 'Tick'
769 cxx_predecls = ['#include "base/types.hh"']
770 swig_predecls = ['%import "stdint.i"\n' +
771 '%import "base/types.hh"']
772
773 def getValue(self):
774 return long(self.value)
775
776class Latency(TickParamValue):
777 def __init__(self, value):
778 if isinstance(value, (Latency, Clock)):
779 self.ticks = value.ticks
780 self.value = value.value
781 elif isinstance(value, Frequency):
782 self.ticks = value.ticks
783 self.value = 1.0 / value.value
784 elif value.endswith('t'):
785 self.ticks = True
786 self.value = int(value[:-1])
787 else:
788 self.ticks = False
789 self.value = convert.toLatency(value)
790
791 def __getattr__(self, attr):
792 if attr in ('latency', 'period'):
793 return self
794 if attr == 'frequency':
795 return Frequency(self)
796 raise AttributeError, "Latency object has no attribute '%s'" % attr
797
798 def getValue(self):
799 if self.ticks or self.value == 0:
800 value = self.value
801 else:
802 value = ticks.fromSeconds(self.value)
803 return long(value)
804
805 # convert latency to ticks
806 def ini_str(self):
807 return '%d' % self.getValue()
808
809class Frequency(TickParamValue):
810 def __init__(self, value):
811 if isinstance(value, (Latency, Clock)):
812 if value.value == 0:
813 self.value = 0
814 else:
815 self.value = 1.0 / value.value
816 self.ticks = value.ticks
817 elif isinstance(value, Frequency):
818 self.value = value.value
819 self.ticks = value.ticks
820 else:
821 self.ticks = False
822 self.value = convert.toFrequency(value)
823
824 def __getattr__(self, attr):
825 if attr == 'frequency':
826 return self
827 if attr in ('latency', 'period'):
828 return Latency(self)
829 raise AttributeError, "Frequency object has no attribute '%s'" % attr
830
831 # convert latency to ticks
832 def getValue(self):
833 if self.ticks or self.value == 0:
834 value = self.value
835 else:
836 value = ticks.fromSeconds(1.0 / self.value)
837 return long(value)
838
839 def ini_str(self):
840 return '%d' % self.getValue()
841
842# A generic frequency and/or Latency value. Value is stored as a latency,
843# but to avoid ambiguity this object does not support numeric ops (* or /).
844# An explicit conversion to a Latency or Frequency must be made first.
845class Clock(ParamValue):
846 cxx_type = 'Tick'
847 cxx_predecls = ['#include "base/types.hh"']
848 swig_predecls = ['%import "stdint.i"\n' +
849 '%import "base/types.hh"']
850 def __init__(self, value):
851 if isinstance(value, (Latency, Clock)):
852 self.ticks = value.ticks
853 self.value = value.value
854 elif isinstance(value, Frequency):
855 self.ticks = value.ticks
856 self.value = 1.0 / value.value
857 elif value.endswith('t'):
858 self.ticks = True
859 self.value = int(value[:-1])
860 else:
861 self.ticks = False
862 self.value = convert.anyToLatency(value)
863
864 def __getattr__(self, attr):
865 if attr == 'frequency':
866 return Frequency(self)
867 if attr in ('latency', 'period'):
868 return Latency(self)
869 raise AttributeError, "Frequency object has no attribute '%s'" % attr
870
871 def getValue(self):
872 return self.period.getValue()
873
874 def ini_str(self):
875 return self.period.ini_str()
876
877class NetworkBandwidth(float,ParamValue):
878 cxx_type = 'float'
879 def __new__(cls, value):
880 # convert to bits per second
881 val = convert.toNetworkBandwidth(value)
882 return super(cls, NetworkBandwidth).__new__(cls, val)
883
884 def __str__(self):
885 return str(self.val)
886
887 def getValue(self):
888 # convert to seconds per byte
889 value = 8.0 / float(self)
890 # convert to ticks per byte
891 value = ticks.fromSeconds(value)
892 return float(value)
893
894 def ini_str(self):
895 return '%f' % self.getValue()
896
897class MemoryBandwidth(float,ParamValue):
898 cxx_type = 'float'
899 def __new__(cls, value):
900 # we want the number of ticks per byte of data
901 val = convert.toMemoryBandwidth(value)
902 return super(cls, MemoryBandwidth).__new__(cls, val)
903
904 def __str__(self):
905 return str(self.val)
906
907 def getValue(self):
908 # convert to seconds per byte
909 value = float(self)
910 if value:
911 value = 1.0 / float(self)
912 # convert to ticks per byte
913 value = ticks.fromSeconds(value)
914 return float(value)
915
916 def ini_str(self):
917 return '%f' % self.getValue()
918
919#
920# "Constants"... handy aliases for various values.
921#
922
923# Special class for NULL pointers. Note the special check in
924# make_param_value() above that lets these be assigned where a
925# SimObject is required.
926# only one copy of a particular node
927class NullSimObject(object):
928 __metaclass__ = Singleton
929
930 def __call__(cls):
931 return cls
932
933 def _instantiate(self, parent = None, path = ''):
934 pass
935
936 def ini_str(self):
937 return 'Null'
938
939 def unproxy(self, base):
940 return self
941
942 def set_path(self, parent, name):
943 pass
944
945 def __str__(self):
946 return 'Null'
947
948 def getValue(self):
949 return None
950
951# The only instance you'll ever need...
952NULL = NullSimObject()
953
954def isNullPointer(value):
955 return isinstance(value, NullSimObject)
956
957# Some memory range specifications use this as a default upper bound.
958MaxAddr = Addr.max
959MaxTick = Tick.max
960AllMemory = AddrRange(0, MaxAddr)
961
962
963#####################################################################
964#
965# Port objects
966#
967# Ports are used to interconnect objects in the memory system.
968#
969#####################################################################
970
971# Port reference: encapsulates a reference to a particular port on a
972# particular SimObject.
973class PortRef(object):
974 def __init__(self, simobj, name):
975 assert(isSimObject(simobj) or isSimObjectClass(simobj))
976 self.simobj = simobj
977 self.name = name
978 self.peer = None # not associated with another port yet
979 self.ccConnected = False # C++ port connection done?
980 self.index = -1 # always -1 for non-vector ports
981
982 def __str__(self):
983 return '%s.%s' % (self.simobj, self.name)
984
985 # for config.ini, print peer's name (not ours)
986 def ini_str(self):
987 return str(self.peer)
988
989 def __getattr__(self, attr):
990 if attr == 'peerObj':
991 # shorthand for proxies
992 return self.peer.simobj
993 raise AttributeError, "'%s' object has no attribute '%s'" % \
994 (self.__class__.__name__, attr)
995
996 # Full connection is symmetric (both ways). Called via
997 # SimObject.__setattr__ as a result of a port assignment, e.g.,
998 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
999 # e.g., "obj1.portA[3] = obj2.portB".
1000 def connect(self, other):
1001 if isinstance(other, VectorPortRef):
1002 # reference to plain VectorPort is implicit append
1003 other = other._get_next()
1004 if self.peer and not proxy.isproxy(self.peer):
1005 print "warning: overwriting port", self, \
1006 "value", self.peer, "with", other
1007 self.peer.peer = None
1008 self.peer = other
1009 if proxy.isproxy(other):
1010 other.set_param_desc(PortParamDesc())
1011 elif isinstance(other, PortRef):
1012 if other.peer is not self:
1013 other.connect(self)
1014 else:
1015 raise TypeError, \
1016 "assigning non-port reference '%s' to port '%s'" \
1017 % (other, self)
1018
1019 def clone(self, simobj, memo):
1020 if memo.has_key(self):
1021 return memo[self]
1022 newRef = copy.copy(self)
1023 memo[self] = newRef
1024 newRef.simobj = simobj
1025 assert(isSimObject(newRef.simobj))
1026 if self.peer and not proxy.isproxy(self.peer):
1027 peerObj = self.peer.simobj(_memo=memo)
1028 newRef.peer = self.peer.clone(peerObj, memo)
1029 assert(not isinstance(newRef.peer, VectorPortRef))
1030 return newRef
1031
1032 def unproxy(self, simobj):
1033 assert(simobj is self.simobj)
1034 if proxy.isproxy(self.peer):
1035 try:
1036 realPeer = self.peer.unproxy(self.simobj)
1037 except:
1038 print "Error in unproxying port '%s' of %s" % \
1039 (self.name, self.simobj.path())
1040 raise
1041 self.connect(realPeer)
1042
1043 # Call C++ to create corresponding port connection between C++ objects
1044 def ccConnect(self):
1045 from m5.objects.params import connectPorts
1046
1047 if self.ccConnected: # already done this
1048 return
1049 peer = self.peer
1050 if not self.peer: # nothing to connect to
1051 return
1052 try:
1053 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1054 peer.simobj.getCCObject(), peer.name, peer.index)
1055 except:
1056 print "Error connecting port %s.%s to %s.%s" % \
1057 (self.simobj.path(), self.name,
1058 peer.simobj.path(), peer.name)
1059 raise
1060 self.ccConnected = True
1061 peer.ccConnected = True
1062
1063# A reference to an individual element of a VectorPort... much like a
1064# PortRef, but has an index.
1065class VectorPortElementRef(PortRef):
1066 def __init__(self, simobj, name, index):
1067 PortRef.__init__(self, simobj, name)
1068 self.index = index
1069
1070 def __str__(self):
1071 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1072
1073# A reference to a complete vector-valued port (not just a single element).
1074# Can be indexed to retrieve individual VectorPortElementRef instances.
1075class VectorPortRef(object):
1076 def __init__(self, simobj, name):
1077 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1078 self.simobj = simobj
1079 self.name = name
1080 self.elements = []
1081
1082 def __str__(self):
1083 return '%s.%s[:]' % (self.simobj, self.name)
1084
1085 # for config.ini, print peer's name (not ours)
1086 def ini_str(self):
1087 return ' '.join([el.ini_str() for el in self.elements])
1088
1089 def __getitem__(self, key):
1090 if not isinstance(key, int):
1091 raise TypeError, "VectorPort index must be integer"
1092 if key >= len(self.elements):
1093 # need to extend list
1094 ext = [VectorPortElementRef(self.simobj, self.name, i)
1095 for i in range(len(self.elements), key+1)]
1096 self.elements.extend(ext)
1097 return self.elements[key]
1098
1099 def _get_next(self):
1100 return self[len(self.elements)]
1101
1102 def __setitem__(self, key, value):
1103 if not isinstance(key, int):
1104 raise TypeError, "VectorPort index must be integer"
1105 self[key].connect(value)
1106
1107 def connect(self, other):
1108 if isinstance(other, (list, tuple)):
1109 # Assign list of port refs to vector port.
1110 # For now, append them... not sure if that's the right semantics
1111 # or if it should replace the current vector.
1112 for ref in other:
1113 self._get_next().connect(ref)
1114 else:
1115 # scalar assignment to plain VectorPort is implicit append
1116 self._get_next().connect(other)
1117
1118 def clone(self, simobj, memo):
1119 if memo.has_key(self):
1120 return memo[self]
1121 newRef = copy.copy(self)
1122 memo[self] = newRef
1123 newRef.simobj = simobj
1124 assert(isSimObject(newRef.simobj))
1125 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1126 return newRef
1127
1128 def unproxy(self, simobj):
1129 [el.unproxy(simobj) for el in self.elements]
1130
1131 def ccConnect(self):
1132 [el.ccConnect() for el in self.elements]
1133
1134# Port description object. Like a ParamDesc object, this represents a
1135# logical port in the SimObject class, not a particular port on a
1136# SimObject instance. The latter are represented by PortRef objects.
1137class Port(object):
1138 # Port("description") or Port(default, "description")
1139 def __init__(self, *args):
1140 if len(args) == 1:
1141 self.desc = args[0]
1142 elif len(args) == 2:
1143 self.default = args[0]
1144 self.desc = args[1]
1145 else:
1146 raise TypeError, 'wrong number of arguments'
1147 # self.name is set by SimObject class on assignment
1148 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1149
1150 # Generate a PortRef for this port on the given SimObject with the
1151 # given name
1152 def makeRef(self, simobj):
1153 return PortRef(simobj, self.name)
1154
1155 # Connect an instance of this port (on the given SimObject with
1156 # the given name) with the port described by the supplied PortRef
1157 def connect(self, simobj, ref):
1158 self.makeRef(simobj).connect(ref)
1159
1160# VectorPort description object. Like Port, but represents a vector
1161# of connections (e.g., as on a Bus).
1162class VectorPort(Port):
1163 def __init__(self, *args):
1164 Port.__init__(self, *args)
1165 self.isVec = True
1166
1167 def makeRef(self, simobj):
1168 return VectorPortRef(simobj, self.name)
1169
1170# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1171# proxy objects (via set_param_desc()) so that proxy error messages
1172# make sense.
1173class PortParamDesc(object):
1174 __metaclass__ = Singleton
1175
1176 ptype_str = 'Port'
1177 ptype = Port
1178
1179baseEnums = allEnums.copy()
1180baseParams = allParams.copy()
1181
1182def clear():
1183 global allEnums, allParams
1184
1185 allEnums = baseEnums.copy()
1186 allParams = baseParams.copy()
1187
1188__all__ = ['Param', 'VectorParam',
1189 'Enum', 'Bool', 'String', 'Float',
1190 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1191 'Int32', 'UInt32', 'Int64', 'UInt64',
1192 'Counter', 'Addr', 'Tick', 'Percent',
1193 'TcpPort', 'UdpPort', 'EthernetAddr',
1194 'MemorySize', 'MemorySize32',
1195 'Latency', 'Frequency', 'Clock',
1196 'NetworkBandwidth', 'MemoryBandwidth',
1197 'Range', 'AddrRange', 'TickRange',
1198 'MaxAddr', 'MaxTick', 'AllMemory',
1199 'Time',
1200 'NextEthernetAddr', 'NULL',
1201 'Port', 'VectorPort']
1202
1203import SimObject
234 else:
235 return VectorParamValue(tmp_list)
236
237 def swig_predecls(self):
238 return ['%%include "%s_vptype.i"' % self.ptype_str]
239
240 def swig_decl(self):
241 cxx_type = re.sub('std::', '', self.ptype.cxx_type)
242 vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
243 (self.ptype_str, cxx_type)
244 return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
245
246 def cxx_predecls(self):
247 return ['#include <vector>'] + self.ptype.cxx_predecls
248
249 def cxx_decl(self):
250 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
251
252class ParamFactory(object):
253 def __init__(self, param_desc_class, ptype_str = None):
254 self.param_desc_class = param_desc_class
255 self.ptype_str = ptype_str
256
257 def __getattr__(self, attr):
258 if self.ptype_str:
259 attr = self.ptype_str + '.' + attr
260 return ParamFactory(self.param_desc_class, attr)
261
262 # E.g., Param.Int(5, "number of widgets")
263 def __call__(self, *args, **kwargs):
264 ptype = None
265 try:
266 ptype = allParams[self.ptype_str]
267 except KeyError:
268 # if name isn't defined yet, assume it's a SimObject, and
269 # try to resolve it later
270 pass
271 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
272
273Param = ParamFactory(ParamDesc)
274VectorParam = ParamFactory(VectorParamDesc)
275
276#####################################################################
277#
278# Parameter Types
279#
280# Though native Python types could be used to specify parameter types
281# (the 'ptype' field of the Param and VectorParam classes), it's more
282# flexible to define our own set of types. This gives us more control
283# over how Python expressions are converted to values (via the
284# __init__() constructor) and how these values are printed out (via
285# the __str__() conversion method).
286#
287#####################################################################
288
289# String-valued parameter. Just mixin the ParamValue class with the
290# built-in str class.
291class String(ParamValue,str):
292 cxx_type = 'std::string'
293 cxx_predecls = ['#include <string>']
294 swig_predecls = ['%include "std_string.i"\n' +
295 '%apply const std::string& {std::string *};']
296 swig_predecls = ['%include "std_string.i"' ]
297
298 def getValue(self):
299 return self
300
301# superclass for "numeric" parameter values, to emulate math
302# operations in a type-safe way. e.g., a Latency times an int returns
303# a new Latency object.
304class NumericParamValue(ParamValue):
305 def __str__(self):
306 return str(self.value)
307
308 def __float__(self):
309 return float(self.value)
310
311 def __long__(self):
312 return long(self.value)
313
314 def __int__(self):
315 return int(self.value)
316
317 # hook for bounds checking
318 def _check(self):
319 return
320
321 def __mul__(self, other):
322 newobj = self.__class__(self)
323 newobj.value *= other
324 newobj._check()
325 return newobj
326
327 __rmul__ = __mul__
328
329 def __div__(self, other):
330 newobj = self.__class__(self)
331 newobj.value /= other
332 newobj._check()
333 return newobj
334
335 def __sub__(self, other):
336 newobj = self.__class__(self)
337 newobj.value -= other
338 newobj._check()
339 return newobj
340
341# Metaclass for bounds-checked integer parameters. See CheckedInt.
342class CheckedIntType(MetaParamValue):
343 def __init__(cls, name, bases, dict):
344 super(CheckedIntType, cls).__init__(name, bases, dict)
345
346 # CheckedInt is an abstract base class, so we actually don't
347 # want to do any processing on it... the rest of this code is
348 # just for classes that derive from CheckedInt.
349 if name == 'CheckedInt':
350 return
351
352 if not cls.cxx_predecls:
353 # most derived types require this, so we just do it here once
354 cls.cxx_predecls = ['#include "base/types.hh"']
355
356 if not cls.swig_predecls:
357 # most derived types require this, so we just do it here once
358 cls.swig_predecls = ['%import "stdint.i"\n' +
359 '%import "base/types.hh"']
360
361 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
362 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
363 panic("CheckedInt subclass %s must define either\n" \
364 " 'min' and 'max' or 'size' and 'unsigned'\n",
365 name);
366 if cls.unsigned:
367 cls.min = 0
368 cls.max = 2 ** cls.size - 1
369 else:
370 cls.min = -(2 ** (cls.size - 1))
371 cls.max = (2 ** (cls.size - 1)) - 1
372
373# Abstract superclass for bounds-checked integer parameters. This
374# class is subclassed to generate parameter classes with specific
375# bounds. Initialization of the min and max bounds is done in the
376# metaclass CheckedIntType.__init__.
377class CheckedInt(NumericParamValue):
378 __metaclass__ = CheckedIntType
379
380 def _check(self):
381 if not self.min <= self.value <= self.max:
382 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
383 (self.min, self.value, self.max)
384
385 def __init__(self, value):
386 if isinstance(value, str):
387 self.value = convert.toInteger(value)
388 elif isinstance(value, (int, long, float, NumericParamValue)):
389 self.value = long(value)
390 else:
391 raise TypeError, "Can't convert object of type %s to CheckedInt" \
392 % type(value).__name__
393 self._check()
394
395 def getValue(self):
396 return long(self.value)
397
398class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False
399class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
400
401class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False
402class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True
403class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False
404class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
405class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False
406class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True
407class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False
408class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True
409
410class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True
411class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True
412class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
413class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True
414
415class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100
416
417class Float(ParamValue, float):
418 cxx_type = 'double'
419
420 def __init__(self, value):
421 if isinstance(value, (int, long, float, NumericParamValue, Float)):
422 self.value = float(value)
423 else:
424 raise TypeError, "Can't convert object of type %s to Float" \
425 % type(value).__name__
426
427 def getValue(self):
428 return float(self.value)
429
430class MemorySize(CheckedInt):
431 cxx_type = 'uint64_t'
432 size = 64
433 unsigned = True
434 def __init__(self, value):
435 if isinstance(value, MemorySize):
436 self.value = value.value
437 else:
438 self.value = convert.toMemorySize(value)
439 self._check()
440
441class MemorySize32(CheckedInt):
442 cxx_type = 'uint32_t'
443 size = 32
444 unsigned = True
445 def __init__(self, value):
446 if isinstance(value, MemorySize):
447 self.value = value.value
448 else:
449 self.value = convert.toMemorySize(value)
450 self._check()
451
452class Addr(CheckedInt):
453 cxx_type = 'Addr'
454 size = 64
455 unsigned = True
456 def __init__(self, value):
457 if isinstance(value, Addr):
458 self.value = value.value
459 else:
460 try:
461 self.value = convert.toMemorySize(value)
462 except TypeError:
463 self.value = long(value)
464 self._check()
465 def __add__(self, other):
466 if isinstance(other, Addr):
467 return self.value + other.value
468 else:
469 return self.value + other
470
471
472class MetaRange(MetaParamValue):
473 def __init__(cls, name, bases, dict):
474 super(MetaRange, cls).__init__(name, bases, dict)
475 if name == 'Range':
476 return
477 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
478 cls.cxx_predecls = \
479 ['#include "base/range.hh"'] + cls.type.cxx_predecls
480
481class Range(ParamValue):
482 __metaclass__ = MetaRange
483 type = Int # default; can be overridden in subclasses
484 def __init__(self, *args, **kwargs):
485 def handle_kwargs(self, kwargs):
486 if 'end' in kwargs:
487 self.second = self.type(kwargs.pop('end'))
488 elif 'size' in kwargs:
489 self.second = self.first + self.type(kwargs.pop('size')) - 1
490 else:
491 raise TypeError, "Either end or size must be specified"
492
493 if len(args) == 0:
494 self.first = self.type(kwargs.pop('start'))
495 handle_kwargs(self, kwargs)
496
497 elif len(args) == 1:
498 if kwargs:
499 self.first = self.type(args[0])
500 handle_kwargs(self, kwargs)
501 elif isinstance(args[0], Range):
502 self.first = self.type(args[0].first)
503 self.second = self.type(args[0].second)
504 elif isinstance(args[0], (list, tuple)):
505 self.first = self.type(args[0][0])
506 self.second = self.type(args[0][1])
507 else:
508 self.first = self.type(0)
509 self.second = self.type(args[0]) - 1
510
511 elif len(args) == 2:
512 self.first = self.type(args[0])
513 self.second = self.type(args[1])
514 else:
515 raise TypeError, "Too many arguments specified"
516
517 if kwargs:
518 raise TypeError, "too many keywords: %s" % kwargs.keys()
519
520 def __str__(self):
521 return '%s:%s' % (self.first, self.second)
522
523class AddrRange(Range):
524 type = Addr
525 swig_predecls = ['%include "python/swig/range.i"']
526
527 def getValue(self):
528 from m5.objects.params import AddrRange
529
530 value = AddrRange()
531 value.start = long(self.first)
532 value.end = long(self.second)
533 return value
534
535class TickRange(Range):
536 type = Tick
537 swig_predecls = ['%include "python/swig/range.i"']
538
539 def getValue(self):
540 from m5.objects.params import TickRange
541
542 value = TickRange()
543 value.start = long(self.first)
544 value.end = long(self.second)
545 return value
546
547# Boolean parameter type. Python doesn't let you subclass bool, since
548# it doesn't want to let you create multiple instances of True and
549# False. Thus this is a little more complicated than String.
550class Bool(ParamValue):
551 cxx_type = 'bool'
552 def __init__(self, value):
553 try:
554 self.value = convert.toBool(value)
555 except TypeError:
556 self.value = bool(value)
557
558 def getValue(self):
559 return bool(self.value)
560
561 def __str__(self):
562 return str(self.value)
563
564 def ini_str(self):
565 if self.value:
566 return 'true'
567 return 'false'
568
569def IncEthernetAddr(addr, val = 1):
570 bytes = map(lambda x: int(x, 16), addr.split(':'))
571 bytes[5] += val
572 for i in (5, 4, 3, 2, 1):
573 val,rem = divmod(bytes[i], 256)
574 bytes[i] = rem
575 if val == 0:
576 break
577 bytes[i - 1] += val
578 assert(bytes[0] <= 255)
579 return ':'.join(map(lambda x: '%02x' % x, bytes))
580
581_NextEthernetAddr = "00:90:00:00:00:01"
582def NextEthernetAddr():
583 global _NextEthernetAddr
584
585 value = _NextEthernetAddr
586 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
587 return value
588
589class EthernetAddr(ParamValue):
590 cxx_type = 'Net::EthAddr'
591 cxx_predecls = ['#include "base/inet.hh"']
592 swig_predecls = ['%include "python/swig/inet.i"']
593 def __init__(self, value):
594 if value == NextEthernetAddr:
595 self.value = value
596 return
597
598 if not isinstance(value, str):
599 raise TypeError, "expected an ethernet address and didn't get one"
600
601 bytes = value.split(':')
602 if len(bytes) != 6:
603 raise TypeError, 'invalid ethernet address %s' % value
604
605 for byte in bytes:
606 if not 0 <= int(byte) <= 256:
607 raise TypeError, 'invalid ethernet address %s' % value
608
609 self.value = value
610
611 def unproxy(self, base):
612 if self.value == NextEthernetAddr:
613 return EthernetAddr(self.value())
614 return self
615
616 def getValue(self):
617 from m5.objects.params import EthAddr
618 return EthAddr(self.value)
619
620 def ini_str(self):
621 return self.value
622
623time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
624 "%a %b %d %H:%M:%S %Z %Y",
625 "%Y/%m/%d %H:%M:%S",
626 "%Y/%m/%d %H:%M",
627 "%Y/%m/%d",
628 "%m/%d/%Y %H:%M:%S",
629 "%m/%d/%Y %H:%M",
630 "%m/%d/%Y",
631 "%m/%d/%y %H:%M:%S",
632 "%m/%d/%y %H:%M",
633 "%m/%d/%y"]
634
635
636def parse_time(value):
637 from time import gmtime, strptime, struct_time, time
638 from datetime import datetime, date
639
640 if isinstance(value, struct_time):
641 return value
642
643 if isinstance(value, (int, long)):
644 return gmtime(value)
645
646 if isinstance(value, (datetime, date)):
647 return value.timetuple()
648
649 if isinstance(value, str):
650 if value in ('Now', 'Today'):
651 return time.gmtime(time.time())
652
653 for format in time_formats:
654 try:
655 return strptime(value, format)
656 except ValueError:
657 pass
658
659 raise ValueError, "Could not parse '%s' as a time" % value
660
661class Time(ParamValue):
662 cxx_type = 'tm'
663 cxx_predecls = [ '#include <time.h>' ]
664 swig_predecls = [ '%include "python/swig/time.i"' ]
665 def __init__(self, value):
666 self.value = parse_time(value)
667
668 def getValue(self):
669 from m5.objects.params import tm
670
671 c_time = tm()
672 py_time = self.value
673
674 # UNIX is years since 1900
675 c_time.tm_year = py_time.tm_year - 1900;
676
677 # Python starts at 1, UNIX starts at 0
678 c_time.tm_mon = py_time.tm_mon - 1;
679 c_time.tm_mday = py_time.tm_mday;
680 c_time.tm_hour = py_time.tm_hour;
681 c_time.tm_min = py_time.tm_min;
682 c_time.tm_sec = py_time.tm_sec;
683
684 # Python has 0 as Monday, UNIX is 0 as sunday
685 c_time.tm_wday = py_time.tm_wday + 1
686 if c_time.tm_wday > 6:
687 c_time.tm_wday -= 7;
688
689 # Python starts at 1, Unix starts at 0
690 c_time.tm_yday = py_time.tm_yday - 1;
691
692 return c_time
693
694 def __str__(self):
695 return time.asctime(self.value)
696
697 def ini_str(self):
698 return str(self)
699
700# Enumerated types are a little more complex. The user specifies the
701# type as Enum(foo) where foo is either a list or dictionary of
702# alternatives (typically strings, but not necessarily so). (In the
703# long run, the integer value of the parameter will be the list index
704# or the corresponding dictionary value. For now, since we only check
705# that the alternative is valid and then spit it into a .ini file,
706# there's not much point in using the dictionary.)
707
708# What Enum() must do is generate a new type encapsulating the
709# provided list/dictionary so that specific values of the parameter
710# can be instances of that type. We define two hidden internal
711# classes (_ListEnum and _DictEnum) to serve as base classes, then
712# derive the new type from the appropriate base class on the fly.
713
714allEnums = {}
715# Metaclass for Enum types
716class MetaEnum(MetaParamValue):
717 def __new__(mcls, name, bases, dict):
718 assert name not in allEnums
719
720 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
721 allEnums[name] = cls
722 return cls
723
724 def __init__(cls, name, bases, init_dict):
725 if init_dict.has_key('map'):
726 if not isinstance(cls.map, dict):
727 raise TypeError, "Enum-derived class attribute 'map' " \
728 "must be of type dict"
729 # build list of value strings from map
730 cls.vals = cls.map.keys()
731 cls.vals.sort()
732 elif init_dict.has_key('vals'):
733 if not isinstance(cls.vals, list):
734 raise TypeError, "Enum-derived class attribute 'vals' " \
735 "must be of type list"
736 # build string->value map from vals sequence
737 cls.map = {}
738 for idx,val in enumerate(cls.vals):
739 cls.map[val] = idx
740 else:
741 raise TypeError, "Enum-derived class must define "\
742 "attribute 'map' or 'vals'"
743
744 cls.cxx_type = 'Enums::%s' % name
745
746 super(MetaEnum, cls).__init__(name, bases, init_dict)
747
748 # Generate C++ class declaration for this enum type.
749 # Note that we wrap the enum in a class/struct to act as a namespace,
750 # so that the enum strings can be brief w/o worrying about collisions.
751 def cxx_decl(cls):
752 name = cls.__name__
753 code = "#ifndef __ENUM__%s\n" % name
754 code += '#define __ENUM__%s\n' % name
755 code += '\n'
756 code += 'namespace Enums {\n'
757 code += ' enum %s {\n' % name
758 for val in cls.vals:
759 code += ' %s = %d,\n' % (val, cls.map[val])
760 code += ' Num_%s = %d,\n' % (name, len(cls.vals))
761 code += ' };\n'
762 code += ' extern const char *%sStrings[Num_%s];\n' % (name, name)
763 code += '}\n'
764 code += '\n'
765 code += '#endif\n'
766 return code
767
768 def cxx_def(cls):
769 name = cls.__name__
770 code = '#include "enums/%s.hh"\n' % name
771 code += 'namespace Enums {\n'
772 code += ' const char *%sStrings[Num_%s] =\n' % (name, name)
773 code += ' {\n'
774 for val in cls.vals:
775 code += ' "%s",\n' % val
776 code += ' };\n'
777 code += '}\n'
778 return code
779
780# Base class for enum types.
781class Enum(ParamValue):
782 __metaclass__ = MetaEnum
783 vals = []
784
785 def __init__(self, value):
786 if value not in self.map:
787 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
788 % (value, self.vals)
789 self.value = value
790
791 def getValue(self):
792 return int(self.map[self.value])
793
794 def __str__(self):
795 return self.value
796
797# how big does a rounding error need to be before we warn about it?
798frequency_tolerance = 0.001 # 0.1%
799
800class TickParamValue(NumericParamValue):
801 cxx_type = 'Tick'
802 cxx_predecls = ['#include "base/types.hh"']
803 swig_predecls = ['%import "stdint.i"\n' +
804 '%import "base/types.hh"']
805
806 def getValue(self):
807 return long(self.value)
808
809class Latency(TickParamValue):
810 def __init__(self, value):
811 if isinstance(value, (Latency, Clock)):
812 self.ticks = value.ticks
813 self.value = value.value
814 elif isinstance(value, Frequency):
815 self.ticks = value.ticks
816 self.value = 1.0 / value.value
817 elif value.endswith('t'):
818 self.ticks = True
819 self.value = int(value[:-1])
820 else:
821 self.ticks = False
822 self.value = convert.toLatency(value)
823
824 def __getattr__(self, attr):
825 if attr in ('latency', 'period'):
826 return self
827 if attr == 'frequency':
828 return Frequency(self)
829 raise AttributeError, "Latency object has no attribute '%s'" % attr
830
831 def getValue(self):
832 if self.ticks or self.value == 0:
833 value = self.value
834 else:
835 value = ticks.fromSeconds(self.value)
836 return long(value)
837
838 # convert latency to ticks
839 def ini_str(self):
840 return '%d' % self.getValue()
841
842class Frequency(TickParamValue):
843 def __init__(self, value):
844 if isinstance(value, (Latency, Clock)):
845 if value.value == 0:
846 self.value = 0
847 else:
848 self.value = 1.0 / value.value
849 self.ticks = value.ticks
850 elif isinstance(value, Frequency):
851 self.value = value.value
852 self.ticks = value.ticks
853 else:
854 self.ticks = False
855 self.value = convert.toFrequency(value)
856
857 def __getattr__(self, attr):
858 if attr == 'frequency':
859 return self
860 if attr in ('latency', 'period'):
861 return Latency(self)
862 raise AttributeError, "Frequency object has no attribute '%s'" % attr
863
864 # convert latency to ticks
865 def getValue(self):
866 if self.ticks or self.value == 0:
867 value = self.value
868 else:
869 value = ticks.fromSeconds(1.0 / self.value)
870 return long(value)
871
872 def ini_str(self):
873 return '%d' % self.getValue()
874
875# A generic frequency and/or Latency value. Value is stored as a latency,
876# but to avoid ambiguity this object does not support numeric ops (* or /).
877# An explicit conversion to a Latency or Frequency must be made first.
878class Clock(ParamValue):
879 cxx_type = 'Tick'
880 cxx_predecls = ['#include "base/types.hh"']
881 swig_predecls = ['%import "stdint.i"\n' +
882 '%import "base/types.hh"']
883 def __init__(self, value):
884 if isinstance(value, (Latency, Clock)):
885 self.ticks = value.ticks
886 self.value = value.value
887 elif isinstance(value, Frequency):
888 self.ticks = value.ticks
889 self.value = 1.0 / value.value
890 elif value.endswith('t'):
891 self.ticks = True
892 self.value = int(value[:-1])
893 else:
894 self.ticks = False
895 self.value = convert.anyToLatency(value)
896
897 def __getattr__(self, attr):
898 if attr == 'frequency':
899 return Frequency(self)
900 if attr in ('latency', 'period'):
901 return Latency(self)
902 raise AttributeError, "Frequency object has no attribute '%s'" % attr
903
904 def getValue(self):
905 return self.period.getValue()
906
907 def ini_str(self):
908 return self.period.ini_str()
909
910class NetworkBandwidth(float,ParamValue):
911 cxx_type = 'float'
912 def __new__(cls, value):
913 # convert to bits per second
914 val = convert.toNetworkBandwidth(value)
915 return super(cls, NetworkBandwidth).__new__(cls, val)
916
917 def __str__(self):
918 return str(self.val)
919
920 def getValue(self):
921 # convert to seconds per byte
922 value = 8.0 / float(self)
923 # convert to ticks per byte
924 value = ticks.fromSeconds(value)
925 return float(value)
926
927 def ini_str(self):
928 return '%f' % self.getValue()
929
930class MemoryBandwidth(float,ParamValue):
931 cxx_type = 'float'
932 def __new__(cls, value):
933 # we want the number of ticks per byte of data
934 val = convert.toMemoryBandwidth(value)
935 return super(cls, MemoryBandwidth).__new__(cls, val)
936
937 def __str__(self):
938 return str(self.val)
939
940 def getValue(self):
941 # convert to seconds per byte
942 value = float(self)
943 if value:
944 value = 1.0 / float(self)
945 # convert to ticks per byte
946 value = ticks.fromSeconds(value)
947 return float(value)
948
949 def ini_str(self):
950 return '%f' % self.getValue()
951
952#
953# "Constants"... handy aliases for various values.
954#
955
956# Special class for NULL pointers. Note the special check in
957# make_param_value() above that lets these be assigned where a
958# SimObject is required.
959# only one copy of a particular node
960class NullSimObject(object):
961 __metaclass__ = Singleton
962
963 def __call__(cls):
964 return cls
965
966 def _instantiate(self, parent = None, path = ''):
967 pass
968
969 def ini_str(self):
970 return 'Null'
971
972 def unproxy(self, base):
973 return self
974
975 def set_path(self, parent, name):
976 pass
977
978 def __str__(self):
979 return 'Null'
980
981 def getValue(self):
982 return None
983
984# The only instance you'll ever need...
985NULL = NullSimObject()
986
987def isNullPointer(value):
988 return isinstance(value, NullSimObject)
989
990# Some memory range specifications use this as a default upper bound.
991MaxAddr = Addr.max
992MaxTick = Tick.max
993AllMemory = AddrRange(0, MaxAddr)
994
995
996#####################################################################
997#
998# Port objects
999#
1000# Ports are used to interconnect objects in the memory system.
1001#
1002#####################################################################
1003
1004# Port reference: encapsulates a reference to a particular port on a
1005# particular SimObject.
1006class PortRef(object):
1007 def __init__(self, simobj, name):
1008 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1009 self.simobj = simobj
1010 self.name = name
1011 self.peer = None # not associated with another port yet
1012 self.ccConnected = False # C++ port connection done?
1013 self.index = -1 # always -1 for non-vector ports
1014
1015 def __str__(self):
1016 return '%s.%s' % (self.simobj, self.name)
1017
1018 # for config.ini, print peer's name (not ours)
1019 def ini_str(self):
1020 return str(self.peer)
1021
1022 def __getattr__(self, attr):
1023 if attr == 'peerObj':
1024 # shorthand for proxies
1025 return self.peer.simobj
1026 raise AttributeError, "'%s' object has no attribute '%s'" % \
1027 (self.__class__.__name__, attr)
1028
1029 # Full connection is symmetric (both ways). Called via
1030 # SimObject.__setattr__ as a result of a port assignment, e.g.,
1031 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1032 # e.g., "obj1.portA[3] = obj2.portB".
1033 def connect(self, other):
1034 if isinstance(other, VectorPortRef):
1035 # reference to plain VectorPort is implicit append
1036 other = other._get_next()
1037 if self.peer and not proxy.isproxy(self.peer):
1038 print "warning: overwriting port", self, \
1039 "value", self.peer, "with", other
1040 self.peer.peer = None
1041 self.peer = other
1042 if proxy.isproxy(other):
1043 other.set_param_desc(PortParamDesc())
1044 elif isinstance(other, PortRef):
1045 if other.peer is not self:
1046 other.connect(self)
1047 else:
1048 raise TypeError, \
1049 "assigning non-port reference '%s' to port '%s'" \
1050 % (other, self)
1051
1052 def clone(self, simobj, memo):
1053 if memo.has_key(self):
1054 return memo[self]
1055 newRef = copy.copy(self)
1056 memo[self] = newRef
1057 newRef.simobj = simobj
1058 assert(isSimObject(newRef.simobj))
1059 if self.peer and not proxy.isproxy(self.peer):
1060 peerObj = self.peer.simobj(_memo=memo)
1061 newRef.peer = self.peer.clone(peerObj, memo)
1062 assert(not isinstance(newRef.peer, VectorPortRef))
1063 return newRef
1064
1065 def unproxy(self, simobj):
1066 assert(simobj is self.simobj)
1067 if proxy.isproxy(self.peer):
1068 try:
1069 realPeer = self.peer.unproxy(self.simobj)
1070 except:
1071 print "Error in unproxying port '%s' of %s" % \
1072 (self.name, self.simobj.path())
1073 raise
1074 self.connect(realPeer)
1075
1076 # Call C++ to create corresponding port connection between C++ objects
1077 def ccConnect(self):
1078 from m5.objects.params import connectPorts
1079
1080 if self.ccConnected: # already done this
1081 return
1082 peer = self.peer
1083 if not self.peer: # nothing to connect to
1084 return
1085 try:
1086 connectPorts(self.simobj.getCCObject(), self.name, self.index,
1087 peer.simobj.getCCObject(), peer.name, peer.index)
1088 except:
1089 print "Error connecting port %s.%s to %s.%s" % \
1090 (self.simobj.path(), self.name,
1091 peer.simobj.path(), peer.name)
1092 raise
1093 self.ccConnected = True
1094 peer.ccConnected = True
1095
1096# A reference to an individual element of a VectorPort... much like a
1097# PortRef, but has an index.
1098class VectorPortElementRef(PortRef):
1099 def __init__(self, simobj, name, index):
1100 PortRef.__init__(self, simobj, name)
1101 self.index = index
1102
1103 def __str__(self):
1104 return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1105
1106# A reference to a complete vector-valued port (not just a single element).
1107# Can be indexed to retrieve individual VectorPortElementRef instances.
1108class VectorPortRef(object):
1109 def __init__(self, simobj, name):
1110 assert(isSimObject(simobj) or isSimObjectClass(simobj))
1111 self.simobj = simobj
1112 self.name = name
1113 self.elements = []
1114
1115 def __str__(self):
1116 return '%s.%s[:]' % (self.simobj, self.name)
1117
1118 # for config.ini, print peer's name (not ours)
1119 def ini_str(self):
1120 return ' '.join([el.ini_str() for el in self.elements])
1121
1122 def __getitem__(self, key):
1123 if not isinstance(key, int):
1124 raise TypeError, "VectorPort index must be integer"
1125 if key >= len(self.elements):
1126 # need to extend list
1127 ext = [VectorPortElementRef(self.simobj, self.name, i)
1128 for i in range(len(self.elements), key+1)]
1129 self.elements.extend(ext)
1130 return self.elements[key]
1131
1132 def _get_next(self):
1133 return self[len(self.elements)]
1134
1135 def __setitem__(self, key, value):
1136 if not isinstance(key, int):
1137 raise TypeError, "VectorPort index must be integer"
1138 self[key].connect(value)
1139
1140 def connect(self, other):
1141 if isinstance(other, (list, tuple)):
1142 # Assign list of port refs to vector port.
1143 # For now, append them... not sure if that's the right semantics
1144 # or if it should replace the current vector.
1145 for ref in other:
1146 self._get_next().connect(ref)
1147 else:
1148 # scalar assignment to plain VectorPort is implicit append
1149 self._get_next().connect(other)
1150
1151 def clone(self, simobj, memo):
1152 if memo.has_key(self):
1153 return memo[self]
1154 newRef = copy.copy(self)
1155 memo[self] = newRef
1156 newRef.simobj = simobj
1157 assert(isSimObject(newRef.simobj))
1158 newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1159 return newRef
1160
1161 def unproxy(self, simobj):
1162 [el.unproxy(simobj) for el in self.elements]
1163
1164 def ccConnect(self):
1165 [el.ccConnect() for el in self.elements]
1166
1167# Port description object. Like a ParamDesc object, this represents a
1168# logical port in the SimObject class, not a particular port on a
1169# SimObject instance. The latter are represented by PortRef objects.
1170class Port(object):
1171 # Port("description") or Port(default, "description")
1172 def __init__(self, *args):
1173 if len(args) == 1:
1174 self.desc = args[0]
1175 elif len(args) == 2:
1176 self.default = args[0]
1177 self.desc = args[1]
1178 else:
1179 raise TypeError, 'wrong number of arguments'
1180 # self.name is set by SimObject class on assignment
1181 # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1182
1183 # Generate a PortRef for this port on the given SimObject with the
1184 # given name
1185 def makeRef(self, simobj):
1186 return PortRef(simobj, self.name)
1187
1188 # Connect an instance of this port (on the given SimObject with
1189 # the given name) with the port described by the supplied PortRef
1190 def connect(self, simobj, ref):
1191 self.makeRef(simobj).connect(ref)
1192
1193# VectorPort description object. Like Port, but represents a vector
1194# of connections (e.g., as on a Bus).
1195class VectorPort(Port):
1196 def __init__(self, *args):
1197 Port.__init__(self, *args)
1198 self.isVec = True
1199
1200 def makeRef(self, simobj):
1201 return VectorPortRef(simobj, self.name)
1202
1203# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1204# proxy objects (via set_param_desc()) so that proxy error messages
1205# make sense.
1206class PortParamDesc(object):
1207 __metaclass__ = Singleton
1208
1209 ptype_str = 'Port'
1210 ptype = Port
1211
1212baseEnums = allEnums.copy()
1213baseParams = allParams.copy()
1214
1215def clear():
1216 global allEnums, allParams
1217
1218 allEnums = baseEnums.copy()
1219 allParams = baseParams.copy()
1220
1221__all__ = ['Param', 'VectorParam',
1222 'Enum', 'Bool', 'String', 'Float',
1223 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1224 'Int32', 'UInt32', 'Int64', 'UInt64',
1225 'Counter', 'Addr', 'Tick', 'Percent',
1226 'TcpPort', 'UdpPort', 'EthernetAddr',
1227 'MemorySize', 'MemorySize32',
1228 'Latency', 'Frequency', 'Clock',
1229 'NetworkBandwidth', 'MemoryBandwidth',
1230 'Range', 'AddrRange', 'TickRange',
1231 'MaxAddr', 'MaxTick', 'AllMemory',
1232 'Time',
1233 'NextEthernetAddr', 'NULL',
1234 'Port', 'VectorPort']
1235
1236import SimObject