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