config.py (2901:f9a45473ab55) config.py (2922:654ef3d30b61)
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
30import os, re, sys, types, inspect, copy
31
32import m5
33from m5 import panic, cc_main
34from convert import *
35from multidict import multidict
36
37noDot = False
38try:
39 import pydot
40except:
41 noDot = True
42
43class Singleton(type):
44 def __call__(cls, *args, **kwargs):
45 if hasattr(cls, '_instance'):
46 return cls._instance
47
48 cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
49 return cls._instance
50
51#####################################################################
52#
53# M5 Python Configuration Utility
54#
55# The basic idea is to write simple Python programs that build Python
56# objects corresponding to M5 SimObjects for the desired simulation
57# configuration. For now, the Python emits a .ini file that can be
58# parsed by M5. In the future, some tighter integration between M5
59# and the Python interpreter may allow bypassing the .ini file.
60#
61# Each SimObject class in M5 is represented by a Python class with the
62# same name. The Python inheritance tree mirrors the M5 C++ tree
63# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
64# SimObjects inherit from a single SimObject base class). To specify
65# an instance of an M5 SimObject in a configuration, the user simply
66# instantiates the corresponding Python object. The parameters for
67# that SimObject are given by assigning to attributes of the Python
68# object, either using keyword assignment in the constructor or in
69# separate assignment statements. For example:
70#
71# cache = BaseCache(size='64KB')
72# cache.hit_latency = 3
73# cache.assoc = 8
74#
75# The magic lies in the mapping of the Python attributes for SimObject
76# classes to the actual SimObject parameter specifications. This
77# allows parameter validity checking in the Python code. Continuing
78# the example above, the statements "cache.blurfl=3" or
79# "cache.assoc='hello'" would both result in runtime errors in Python,
80# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
81# parameter requires an integer, respectively. This magic is done
82# primarily by overriding the special __setattr__ method that controls
83# assignment to object attributes.
84#
85# Once a set of Python objects have been instantiated in a hierarchy,
86# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
87# will generate a .ini file.
88#
89#####################################################################
90
91# dict to look up SimObjects based on path
92instanceDict = {}
93
94#############################
95#
96# Utility methods
97#
98#############################
99
100def isSimObject(value):
101 return isinstance(value, SimObject)
102
103def isSimObjectSequence(value):
104 if not isinstance(value, (list, tuple)) or len(value) == 0:
105 return False
106
107 for val in value:
108 if not isNullPointer(val) and not isSimObject(val):
109 return False
110
111 return True
112
113def isSimObjectOrSequence(value):
114 return isSimObject(value) or isSimObjectSequence(value)
115
116def isNullPointer(value):
117 return isinstance(value, NullSimObject)
118
119# Apply method to object.
120# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>)
121def applyMethod(obj, meth, *args, **kwargs):
122 return getattr(obj, meth)(*args, **kwargs)
123
124# If the first argument is an (non-sequence) object, apply the named
125# method with the given arguments. If the first argument is a
126# sequence, apply the method to each element of the sequence (a la
127# 'map').
128def applyOrMap(objOrSeq, meth, *args, **kwargs):
129 if not isinstance(objOrSeq, (list, tuple)):
130 return applyMethod(objOrSeq, meth, *args, **kwargs)
131 else:
132 return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq]
133
134
135# The metaclass for SimObject. This class controls how new classes
136# that derive from SimObject are instantiated, and provides inherited
137# class behavior (just like a class controls how instances of that
138# class are instantiated, and provides inherited instance behavior).
139class MetaSimObject(type):
140 # Attributes that can be set only at initialization time
141 init_keywords = { 'abstract' : types.BooleanType,
142 'type' : types.StringType }
143 # Attributes that can be set any time
144 keywords = { 'check' : types.FunctionType }
145
146 # __new__ is called before __init__, and is where the statements
147 # in the body of the class definition get loaded into the class's
148 # __dict__. We intercept this to filter out parameter & port assignments
149 # and only allow "private" attributes to be passed to the base
150 # __new__ (starting with underscore).
151 def __new__(mcls, name, bases, dict):
152 # Copy "private" attributes, functions, and classes to the
153 # official dict. Everything else goes in _init_dict to be
154 # filtered in __init__.
155 cls_dict = {}
156 value_dict = {}
157 for key,val in dict.items():
158 if key.startswith('_') or isinstance(val, (types.FunctionType,
159 types.TypeType)):
160 cls_dict[key] = val
161 else:
162 # must be a param/port setting
163 value_dict[key] = val
164 cls_dict['_value_dict'] = value_dict
165 return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
166
167 # subclass initialization
168 def __init__(cls, name, bases, dict):
169 # calls type.__init__()... I think that's a no-op, but leave
170 # it here just in case it's not.
171 super(MetaSimObject, cls).__init__(name, bases, dict)
172
173 # initialize required attributes
174
175 # class-only attributes
176 cls._params = multidict() # param descriptions
177 cls._ports = multidict() # port descriptions
178
179 # class or instance attributes
180 cls._values = multidict() # param values
181 cls._port_map = multidict() # port bindings
182 cls._instantiated = False # really instantiated, cloned, or subclassed
183
184 # We don't support multiple inheritance. If you want to, you
185 # must fix multidict to deal with it properly.
186 if len(bases) > 1:
187 raise TypeError, "SimObjects do not support multiple inheritance"
188
189 base = bases[0]
190
191 # Set up general inheritance via multidicts. A subclass will
192 # inherit all its settings from the base class. The only time
193 # the following is not true is when we define the SimObject
194 # class itself (in which case the multidicts have no parent).
195 if isinstance(base, MetaSimObject):
196 cls._params.parent = base._params
197 cls._ports.parent = base._ports
198 cls._values.parent = base._values
199 cls._port_map.parent = base._port_map
200 # mark base as having been subclassed
201 base._instantiated = True
202
203 # Now process the _value_dict items. They could be defining
204 # new (or overriding existing) parameters or ports, setting
205 # class keywords (e.g., 'abstract'), or setting parameter
206 # values or port bindings. The first 3 can only be set when
207 # the class is defined, so we handle them here. The others
208 # can be set later too, so just emulate that by calling
209 # setattr().
210 for key,val in cls._value_dict.items():
211 # param descriptions
212 if isinstance(val, ParamDesc):
213 cls._new_param(key, val)
214
215 # port objects
216 elif isinstance(val, Port):
217 cls._ports[key] = val
218
219 # init-time-only keywords
220 elif cls.init_keywords.has_key(key):
221 cls._set_keyword(key, val, cls.init_keywords[key])
222
223 # default: use normal path (ends up in __setattr__)
224 else:
225 setattr(cls, key, val)
226
227 def _set_keyword(cls, keyword, val, kwtype):
228 if not isinstance(val, kwtype):
229 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
230 (keyword, type(val), kwtype)
231 if isinstance(val, types.FunctionType):
232 val = classmethod(val)
233 type.__setattr__(cls, keyword, val)
234
235 def _new_param(cls, name, value):
236 cls._params[name] = value
237 if hasattr(value, 'default'):
238 setattr(cls, name, value.default)
239
240 # Set attribute (called on foo.attr = value when foo is an
241 # instance of class cls).
242 def __setattr__(cls, attr, value):
243 # normal processing for private attributes
244 if attr.startswith('_'):
245 type.__setattr__(cls, attr, value)
246 return
247
248 if cls.keywords.has_key(attr):
249 cls._set_keyword(attr, value, cls.keywords[attr])
250 return
251
252 if cls._ports.has_key(attr):
253 self._ports[attr].connect(self, attr, value)
254 return
255
256 if isSimObjectOrSequence(value) and cls._instantiated:
257 raise RuntimeError, \
258 "cannot set SimObject parameter '%s' after\n" \
259 " class %s has been instantiated or subclassed" \
260 % (attr, cls.__name__)
261
262 # check for param
263 param = cls._params.get(attr, None)
264 if param:
265 try:
266 cls._values[attr] = param.convert(value)
267 except Exception, e:
268 msg = "%s\nError setting param %s.%s to %s\n" % \
269 (e, cls.__name__, attr, value)
270 e.args = (msg, )
271 raise
272 elif isSimObjectOrSequence(value):
273 # if RHS is a SimObject, it's an implicit child assignment
274 cls._values[attr] = value
275 else:
276 raise AttributeError, \
277 "Class %s has no parameter \'%s\'" % (cls.__name__, attr)
278
279 def __getattr__(cls, attr):
280 if cls._values.has_key(attr):
281 return cls._values[attr]
282
283 raise AttributeError, \
284 "object '%s' has no attribute '%s'" % (cls.__name__, attr)
285
286# The SimObject class is the root of the special hierarchy. Most of
287# the code in this class deals with the configuration hierarchy itself
288# (parent/child node relationships).
289class SimObject(object):
290 # Specify metaclass. Any class inheriting from SimObject will
291 # get this metaclass.
292 __metaclass__ = MetaSimObject
293
294 # Initialize new instance. For objects with SimObject-valued
295 # children, we need to recursively clone the classes represented
296 # by those param values as well in a consistent "deep copy"-style
297 # fashion. That is, we want to make sure that each instance is
298 # cloned only once, and that if there are multiple references to
299 # the same original object, we end up with the corresponding
300 # cloned references all pointing to the same cloned instance.
301 def __init__(self, **kwargs):
302 ancestor = kwargs.get('_ancestor')
303 memo_dict = kwargs.get('_memo')
304 if memo_dict is None:
305 # prepare to memoize any recursively instantiated objects
306 memo_dict = {}
307 elif ancestor:
308 # memoize me now to avoid problems with recursive calls
309 memo_dict[ancestor] = self
310
311 if not ancestor:
312 ancestor = self.__class__
313 ancestor._instantiated = True
314
315 # initialize required attributes
316 self._parent = None
317 self._children = {}
318 self._ccObject = None # pointer to C++ object
319 self._instantiated = False # really "cloned"
320
321 # Inherit parameter values from class using multidict so
322 # individual value settings can be overridden.
323 self._values = multidict(ancestor._values)
324 # clone SimObject-valued parameters
325 for key,val in ancestor._values.iteritems():
326 if isSimObject(val):
327 setattr(self, key, val(_memo=memo_dict))
328 elif isSimObjectSequence(val) and len(val):
329 setattr(self, key, [ v(_memo=memo_dict) for v in val ])
330 # clone port references. no need to use a multidict here
331 # since we will be creating new references for all ports.
332 self._port_map = {}
333 for key,val in ancestor._port_map.iteritems():
334 self._port_map[key] = applyOrMap(val, 'clone', memo_dict)
335 # apply attribute assignments from keyword args, if any
336 for key,val in kwargs.iteritems():
337 setattr(self, key, val)
338
339 # "Clone" the current instance by creating another instance of
340 # this instance's class, but that inherits its parameter values
341 # and port mappings from the current instance. If we're in a
342 # "deep copy" recursive clone, check the _memo dict to see if
343 # we've already cloned this instance.
344 def __call__(self, **kwargs):
345 memo_dict = kwargs.get('_memo')
346 if memo_dict is None:
347 # no memo_dict: must be top-level clone operation.
348 # this is only allowed at the root of a hierarchy
349 if self._parent:
350 raise RuntimeError, "attempt to clone object %s " \
351 "not at the root of a tree (parent = %s)" \
352 % (self, self._parent)
353 # create a new dict and use that.
354 memo_dict = {}
355 kwargs['_memo'] = memo_dict
356 elif memo_dict.has_key(self):
357 # clone already done & memoized
358 return memo_dict[self]
359 return self.__class__(_ancestor = self, **kwargs)
360
361 def __getattr__(self, attr):
362 if self._ports.has_key(attr):
363 # return reference that can be assigned to another port
364 # via __setattr__
365 return self._ports[attr].makeRef(self, attr)
366
367 if self._values.has_key(attr):
368 return self._values[attr]
369
370 raise AttributeError, "object '%s' has no attribute '%s'" \
371 % (self.__class__.__name__, attr)
372
373 # Set attribute (called on foo.attr = value when foo is an
374 # instance of class cls).
375 def __setattr__(self, attr, value):
376 # normal processing for private attributes
377 if attr.startswith('_'):
378 object.__setattr__(self, attr, value)
379 return
380
381 if self._ports.has_key(attr):
382 # set up port connection
383 self._ports[attr].connect(self, attr, value)
384 return
385
386 if isSimObjectOrSequence(value) and self._instantiated:
387 raise RuntimeError, \
388 "cannot set SimObject parameter '%s' after\n" \
389 " instance been cloned %s" % (attr, `self`)
390
391 # must be SimObject param
392 param = self._params.get(attr, None)
393 if param:
394 try:
395 value = param.convert(value)
396 except Exception, e:
397 msg = "%s\nError setting param %s.%s to %s\n" % \
398 (e, self.__class__.__name__, attr, value)
399 e.args = (msg, )
400 raise
401 elif isSimObjectOrSequence(value):
402 pass
403 else:
404 raise AttributeError, "Class %s has no parameter %s" \
405 % (self.__class__.__name__, attr)
406
407 # clear out old child with this name, if any
408 self.clear_child(attr)
409
410 if isSimObject(value):
411 value.set_path(self, attr)
412 elif isSimObjectSequence(value):
413 value = SimObjVector(value)
414 [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)]
415
416 self._values[attr] = value
417
418 # this hack allows tacking a '[0]' onto parameters that may or may
419 # not be vectors, and always getting the first element (e.g. cpus)
420 def __getitem__(self, key):
421 if key == 0:
422 return self
423 raise TypeError, "Non-zero index '%s' to SimObject" % key
424
425 # clear out children with given name, even if it's a vector
426 def clear_child(self, name):
427 if not self._children.has_key(name):
428 return
429 child = self._children[name]
430 if isinstance(child, SimObjVector):
431 for i in xrange(len(child)):
432 del self._children["s%d" % (name, i)]
433 del self._children[name]
434
435 def add_child(self, name, value):
436 self._children[name] = value
437
438 def set_path(self, parent, name):
439 if not self._parent:
440 self._parent = parent
441 self._name = name
442 parent.add_child(name, self)
443
444 def path(self):
445 if not self._parent:
446 return 'root'
447 ppath = self._parent.path()
448 if ppath == 'root':
449 return self._name
450 return ppath + "." + self._name
451
452 def __str__(self):
453 return self.path()
454
455 def ini_str(self):
456 return self.path()
457
458 def find_any(self, ptype):
459 if isinstance(self, ptype):
460 return self, True
461
462 found_obj = None
463 for child in self._children.itervalues():
464 if isinstance(child, ptype):
465 if found_obj != None and child != found_obj:
466 raise AttributeError, \
467 'parent.any matched more than one: %s %s' % \
468 (found_obj.path, child.path)
469 found_obj = child
470 # search param space
471 for pname,pdesc in self._params.iteritems():
472 if issubclass(pdesc.ptype, ptype):
473 match_obj = self._values[pname]
474 if found_obj != None and found_obj != match_obj:
475 raise AttributeError, \
476 'parent.any matched more than one: %s' % obj.path
477 found_obj = match_obj
478 return found_obj, found_obj != None
479
480 def unproxy(self, base):
481 return self
482
483 def print_ini(self):
484 print '[' + self.path() + ']' # .ini section header
485
486 instanceDict[self.path()] = self
487
488 if hasattr(self, 'type') and not isinstance(self, ParamContext):
489 print 'type=%s' % self.type
490
491 child_names = self._children.keys()
492 child_names.sort()
493 np_child_names = [c for c in child_names \
494 if not isinstance(self._children[c], ParamContext)]
495 if len(np_child_names):
496 print 'children=%s' % ' '.join(np_child_names)
497
498 param_names = self._params.keys()
499 param_names.sort()
500 for param in param_names:
501 value = self._values.get(param, None)
502 if value != None:
503 if isproxy(value):
504 try:
505 value = value.unproxy(self)
506 except:
507 print >> sys.stderr, \
508 "Error in unproxying param '%s' of %s" % \
509 (param, self.path())
510 raise
511 setattr(self, param, value)
512 print '%s=%s' % (param, self._values[param].ini_str())
513
514 print # blank line between objects
515
516 for child in child_names:
517 self._children[child].print_ini()
518
519 # Call C++ to create C++ object corresponding to this object and
520 # (recursively) all its children
521 def createCCObject(self):
522 self.getCCObject() # force creation
523 for child in self._children.itervalues():
524 child.createCCObject()
525
526 # Get C++ object corresponding to this object, calling C++ if
527 # necessary to construct it. Does *not* recursively create
528 # children.
529 def getCCObject(self):
530 if not self._ccObject:
531 self._ccObject = -1 # flag to catch cycles in recursion
532 self._ccObject = cc_main.createSimObject(self.path())
533 elif self._ccObject == -1:
534 raise RuntimeError, "%s: recursive call to getCCObject()" \
535 % self.path()
536 return self._ccObject
537
538 # Create C++ port connections corresponding to the connections in
539 # _port_map (& recursively for all children)
540 def connectPorts(self):
541 for portRef in self._port_map.itervalues():
542 applyOrMap(portRef, 'ccConnect')
543 for child in self._children.itervalues():
544 child.connectPorts()
545
546 def startDrain(self, drain_event, recursive):
547 count = 0
548 # ParamContexts don't serialize
549 if isinstance(self, SimObject) and not isinstance(self, ParamContext):
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
30import os, re, sys, types, inspect, copy
31
32import m5
33from m5 import panic, cc_main
34from convert import *
35from multidict import multidict
36
37noDot = False
38try:
39 import pydot
40except:
41 noDot = True
42
43class Singleton(type):
44 def __call__(cls, *args, **kwargs):
45 if hasattr(cls, '_instance'):
46 return cls._instance
47
48 cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
49 return cls._instance
50
51#####################################################################
52#
53# M5 Python Configuration Utility
54#
55# The basic idea is to write simple Python programs that build Python
56# objects corresponding to M5 SimObjects for the desired simulation
57# configuration. For now, the Python emits a .ini file that can be
58# parsed by M5. In the future, some tighter integration between M5
59# and the Python interpreter may allow bypassing the .ini file.
60#
61# Each SimObject class in M5 is represented by a Python class with the
62# same name. The Python inheritance tree mirrors the M5 C++ tree
63# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
64# SimObjects inherit from a single SimObject base class). To specify
65# an instance of an M5 SimObject in a configuration, the user simply
66# instantiates the corresponding Python object. The parameters for
67# that SimObject are given by assigning to attributes of the Python
68# object, either using keyword assignment in the constructor or in
69# separate assignment statements. For example:
70#
71# cache = BaseCache(size='64KB')
72# cache.hit_latency = 3
73# cache.assoc = 8
74#
75# The magic lies in the mapping of the Python attributes for SimObject
76# classes to the actual SimObject parameter specifications. This
77# allows parameter validity checking in the Python code. Continuing
78# the example above, the statements "cache.blurfl=3" or
79# "cache.assoc='hello'" would both result in runtime errors in Python,
80# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
81# parameter requires an integer, respectively. This magic is done
82# primarily by overriding the special __setattr__ method that controls
83# assignment to object attributes.
84#
85# Once a set of Python objects have been instantiated in a hierarchy,
86# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
87# will generate a .ini file.
88#
89#####################################################################
90
91# dict to look up SimObjects based on path
92instanceDict = {}
93
94#############################
95#
96# Utility methods
97#
98#############################
99
100def isSimObject(value):
101 return isinstance(value, SimObject)
102
103def isSimObjectSequence(value):
104 if not isinstance(value, (list, tuple)) or len(value) == 0:
105 return False
106
107 for val in value:
108 if not isNullPointer(val) and not isSimObject(val):
109 return False
110
111 return True
112
113def isSimObjectOrSequence(value):
114 return isSimObject(value) or isSimObjectSequence(value)
115
116def isNullPointer(value):
117 return isinstance(value, NullSimObject)
118
119# Apply method to object.
120# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>)
121def applyMethod(obj, meth, *args, **kwargs):
122 return getattr(obj, meth)(*args, **kwargs)
123
124# If the first argument is an (non-sequence) object, apply the named
125# method with the given arguments. If the first argument is a
126# sequence, apply the method to each element of the sequence (a la
127# 'map').
128def applyOrMap(objOrSeq, meth, *args, **kwargs):
129 if not isinstance(objOrSeq, (list, tuple)):
130 return applyMethod(objOrSeq, meth, *args, **kwargs)
131 else:
132 return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq]
133
134
135# The metaclass for SimObject. This class controls how new classes
136# that derive from SimObject are instantiated, and provides inherited
137# class behavior (just like a class controls how instances of that
138# class are instantiated, and provides inherited instance behavior).
139class MetaSimObject(type):
140 # Attributes that can be set only at initialization time
141 init_keywords = { 'abstract' : types.BooleanType,
142 'type' : types.StringType }
143 # Attributes that can be set any time
144 keywords = { 'check' : types.FunctionType }
145
146 # __new__ is called before __init__, and is where the statements
147 # in the body of the class definition get loaded into the class's
148 # __dict__. We intercept this to filter out parameter & port assignments
149 # and only allow "private" attributes to be passed to the base
150 # __new__ (starting with underscore).
151 def __new__(mcls, name, bases, dict):
152 # Copy "private" attributes, functions, and classes to the
153 # official dict. Everything else goes in _init_dict to be
154 # filtered in __init__.
155 cls_dict = {}
156 value_dict = {}
157 for key,val in dict.items():
158 if key.startswith('_') or isinstance(val, (types.FunctionType,
159 types.TypeType)):
160 cls_dict[key] = val
161 else:
162 # must be a param/port setting
163 value_dict[key] = val
164 cls_dict['_value_dict'] = value_dict
165 return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
166
167 # subclass initialization
168 def __init__(cls, name, bases, dict):
169 # calls type.__init__()... I think that's a no-op, but leave
170 # it here just in case it's not.
171 super(MetaSimObject, cls).__init__(name, bases, dict)
172
173 # initialize required attributes
174
175 # class-only attributes
176 cls._params = multidict() # param descriptions
177 cls._ports = multidict() # port descriptions
178
179 # class or instance attributes
180 cls._values = multidict() # param values
181 cls._port_map = multidict() # port bindings
182 cls._instantiated = False # really instantiated, cloned, or subclassed
183
184 # We don't support multiple inheritance. If you want to, you
185 # must fix multidict to deal with it properly.
186 if len(bases) > 1:
187 raise TypeError, "SimObjects do not support multiple inheritance"
188
189 base = bases[0]
190
191 # Set up general inheritance via multidicts. A subclass will
192 # inherit all its settings from the base class. The only time
193 # the following is not true is when we define the SimObject
194 # class itself (in which case the multidicts have no parent).
195 if isinstance(base, MetaSimObject):
196 cls._params.parent = base._params
197 cls._ports.parent = base._ports
198 cls._values.parent = base._values
199 cls._port_map.parent = base._port_map
200 # mark base as having been subclassed
201 base._instantiated = True
202
203 # Now process the _value_dict items. They could be defining
204 # new (or overriding existing) parameters or ports, setting
205 # class keywords (e.g., 'abstract'), or setting parameter
206 # values or port bindings. The first 3 can only be set when
207 # the class is defined, so we handle them here. The others
208 # can be set later too, so just emulate that by calling
209 # setattr().
210 for key,val in cls._value_dict.items():
211 # param descriptions
212 if isinstance(val, ParamDesc):
213 cls._new_param(key, val)
214
215 # port objects
216 elif isinstance(val, Port):
217 cls._ports[key] = val
218
219 # init-time-only keywords
220 elif cls.init_keywords.has_key(key):
221 cls._set_keyword(key, val, cls.init_keywords[key])
222
223 # default: use normal path (ends up in __setattr__)
224 else:
225 setattr(cls, key, val)
226
227 def _set_keyword(cls, keyword, val, kwtype):
228 if not isinstance(val, kwtype):
229 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
230 (keyword, type(val), kwtype)
231 if isinstance(val, types.FunctionType):
232 val = classmethod(val)
233 type.__setattr__(cls, keyword, val)
234
235 def _new_param(cls, name, value):
236 cls._params[name] = value
237 if hasattr(value, 'default'):
238 setattr(cls, name, value.default)
239
240 # Set attribute (called on foo.attr = value when foo is an
241 # instance of class cls).
242 def __setattr__(cls, attr, value):
243 # normal processing for private attributes
244 if attr.startswith('_'):
245 type.__setattr__(cls, attr, value)
246 return
247
248 if cls.keywords.has_key(attr):
249 cls._set_keyword(attr, value, cls.keywords[attr])
250 return
251
252 if cls._ports.has_key(attr):
253 self._ports[attr].connect(self, attr, value)
254 return
255
256 if isSimObjectOrSequence(value) and cls._instantiated:
257 raise RuntimeError, \
258 "cannot set SimObject parameter '%s' after\n" \
259 " class %s has been instantiated or subclassed" \
260 % (attr, cls.__name__)
261
262 # check for param
263 param = cls._params.get(attr, None)
264 if param:
265 try:
266 cls._values[attr] = param.convert(value)
267 except Exception, e:
268 msg = "%s\nError setting param %s.%s to %s\n" % \
269 (e, cls.__name__, attr, value)
270 e.args = (msg, )
271 raise
272 elif isSimObjectOrSequence(value):
273 # if RHS is a SimObject, it's an implicit child assignment
274 cls._values[attr] = value
275 else:
276 raise AttributeError, \
277 "Class %s has no parameter \'%s\'" % (cls.__name__, attr)
278
279 def __getattr__(cls, attr):
280 if cls._values.has_key(attr):
281 return cls._values[attr]
282
283 raise AttributeError, \
284 "object '%s' has no attribute '%s'" % (cls.__name__, attr)
285
286# The SimObject class is the root of the special hierarchy. Most of
287# the code in this class deals with the configuration hierarchy itself
288# (parent/child node relationships).
289class SimObject(object):
290 # Specify metaclass. Any class inheriting from SimObject will
291 # get this metaclass.
292 __metaclass__ = MetaSimObject
293
294 # Initialize new instance. For objects with SimObject-valued
295 # children, we need to recursively clone the classes represented
296 # by those param values as well in a consistent "deep copy"-style
297 # fashion. That is, we want to make sure that each instance is
298 # cloned only once, and that if there are multiple references to
299 # the same original object, we end up with the corresponding
300 # cloned references all pointing to the same cloned instance.
301 def __init__(self, **kwargs):
302 ancestor = kwargs.get('_ancestor')
303 memo_dict = kwargs.get('_memo')
304 if memo_dict is None:
305 # prepare to memoize any recursively instantiated objects
306 memo_dict = {}
307 elif ancestor:
308 # memoize me now to avoid problems with recursive calls
309 memo_dict[ancestor] = self
310
311 if not ancestor:
312 ancestor = self.__class__
313 ancestor._instantiated = True
314
315 # initialize required attributes
316 self._parent = None
317 self._children = {}
318 self._ccObject = None # pointer to C++ object
319 self._instantiated = False # really "cloned"
320
321 # Inherit parameter values from class using multidict so
322 # individual value settings can be overridden.
323 self._values = multidict(ancestor._values)
324 # clone SimObject-valued parameters
325 for key,val in ancestor._values.iteritems():
326 if isSimObject(val):
327 setattr(self, key, val(_memo=memo_dict))
328 elif isSimObjectSequence(val) and len(val):
329 setattr(self, key, [ v(_memo=memo_dict) for v in val ])
330 # clone port references. no need to use a multidict here
331 # since we will be creating new references for all ports.
332 self._port_map = {}
333 for key,val in ancestor._port_map.iteritems():
334 self._port_map[key] = applyOrMap(val, 'clone', memo_dict)
335 # apply attribute assignments from keyword args, if any
336 for key,val in kwargs.iteritems():
337 setattr(self, key, val)
338
339 # "Clone" the current instance by creating another instance of
340 # this instance's class, but that inherits its parameter values
341 # and port mappings from the current instance. If we're in a
342 # "deep copy" recursive clone, check the _memo dict to see if
343 # we've already cloned this instance.
344 def __call__(self, **kwargs):
345 memo_dict = kwargs.get('_memo')
346 if memo_dict is None:
347 # no memo_dict: must be top-level clone operation.
348 # this is only allowed at the root of a hierarchy
349 if self._parent:
350 raise RuntimeError, "attempt to clone object %s " \
351 "not at the root of a tree (parent = %s)" \
352 % (self, self._parent)
353 # create a new dict and use that.
354 memo_dict = {}
355 kwargs['_memo'] = memo_dict
356 elif memo_dict.has_key(self):
357 # clone already done & memoized
358 return memo_dict[self]
359 return self.__class__(_ancestor = self, **kwargs)
360
361 def __getattr__(self, attr):
362 if self._ports.has_key(attr):
363 # return reference that can be assigned to another port
364 # via __setattr__
365 return self._ports[attr].makeRef(self, attr)
366
367 if self._values.has_key(attr):
368 return self._values[attr]
369
370 raise AttributeError, "object '%s' has no attribute '%s'" \
371 % (self.__class__.__name__, attr)
372
373 # Set attribute (called on foo.attr = value when foo is an
374 # instance of class cls).
375 def __setattr__(self, attr, value):
376 # normal processing for private attributes
377 if attr.startswith('_'):
378 object.__setattr__(self, attr, value)
379 return
380
381 if self._ports.has_key(attr):
382 # set up port connection
383 self._ports[attr].connect(self, attr, value)
384 return
385
386 if isSimObjectOrSequence(value) and self._instantiated:
387 raise RuntimeError, \
388 "cannot set SimObject parameter '%s' after\n" \
389 " instance been cloned %s" % (attr, `self`)
390
391 # must be SimObject param
392 param = self._params.get(attr, None)
393 if param:
394 try:
395 value = param.convert(value)
396 except Exception, e:
397 msg = "%s\nError setting param %s.%s to %s\n" % \
398 (e, self.__class__.__name__, attr, value)
399 e.args = (msg, )
400 raise
401 elif isSimObjectOrSequence(value):
402 pass
403 else:
404 raise AttributeError, "Class %s has no parameter %s" \
405 % (self.__class__.__name__, attr)
406
407 # clear out old child with this name, if any
408 self.clear_child(attr)
409
410 if isSimObject(value):
411 value.set_path(self, attr)
412 elif isSimObjectSequence(value):
413 value = SimObjVector(value)
414 [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)]
415
416 self._values[attr] = value
417
418 # this hack allows tacking a '[0]' onto parameters that may or may
419 # not be vectors, and always getting the first element (e.g. cpus)
420 def __getitem__(self, key):
421 if key == 0:
422 return self
423 raise TypeError, "Non-zero index '%s' to SimObject" % key
424
425 # clear out children with given name, even if it's a vector
426 def clear_child(self, name):
427 if not self._children.has_key(name):
428 return
429 child = self._children[name]
430 if isinstance(child, SimObjVector):
431 for i in xrange(len(child)):
432 del self._children["s%d" % (name, i)]
433 del self._children[name]
434
435 def add_child(self, name, value):
436 self._children[name] = value
437
438 def set_path(self, parent, name):
439 if not self._parent:
440 self._parent = parent
441 self._name = name
442 parent.add_child(name, self)
443
444 def path(self):
445 if not self._parent:
446 return 'root'
447 ppath = self._parent.path()
448 if ppath == 'root':
449 return self._name
450 return ppath + "." + self._name
451
452 def __str__(self):
453 return self.path()
454
455 def ini_str(self):
456 return self.path()
457
458 def find_any(self, ptype):
459 if isinstance(self, ptype):
460 return self, True
461
462 found_obj = None
463 for child in self._children.itervalues():
464 if isinstance(child, ptype):
465 if found_obj != None and child != found_obj:
466 raise AttributeError, \
467 'parent.any matched more than one: %s %s' % \
468 (found_obj.path, child.path)
469 found_obj = child
470 # search param space
471 for pname,pdesc in self._params.iteritems():
472 if issubclass(pdesc.ptype, ptype):
473 match_obj = self._values[pname]
474 if found_obj != None and found_obj != match_obj:
475 raise AttributeError, \
476 'parent.any matched more than one: %s' % obj.path
477 found_obj = match_obj
478 return found_obj, found_obj != None
479
480 def unproxy(self, base):
481 return self
482
483 def print_ini(self):
484 print '[' + self.path() + ']' # .ini section header
485
486 instanceDict[self.path()] = self
487
488 if hasattr(self, 'type') and not isinstance(self, ParamContext):
489 print 'type=%s' % self.type
490
491 child_names = self._children.keys()
492 child_names.sort()
493 np_child_names = [c for c in child_names \
494 if not isinstance(self._children[c], ParamContext)]
495 if len(np_child_names):
496 print 'children=%s' % ' '.join(np_child_names)
497
498 param_names = self._params.keys()
499 param_names.sort()
500 for param in param_names:
501 value = self._values.get(param, None)
502 if value != None:
503 if isproxy(value):
504 try:
505 value = value.unproxy(self)
506 except:
507 print >> sys.stderr, \
508 "Error in unproxying param '%s' of %s" % \
509 (param, self.path())
510 raise
511 setattr(self, param, value)
512 print '%s=%s' % (param, self._values[param].ini_str())
513
514 print # blank line between objects
515
516 for child in child_names:
517 self._children[child].print_ini()
518
519 # Call C++ to create C++ object corresponding to this object and
520 # (recursively) all its children
521 def createCCObject(self):
522 self.getCCObject() # force creation
523 for child in self._children.itervalues():
524 child.createCCObject()
525
526 # Get C++ object corresponding to this object, calling C++ if
527 # necessary to construct it. Does *not* recursively create
528 # children.
529 def getCCObject(self):
530 if not self._ccObject:
531 self._ccObject = -1 # flag to catch cycles in recursion
532 self._ccObject = cc_main.createSimObject(self.path())
533 elif self._ccObject == -1:
534 raise RuntimeError, "%s: recursive call to getCCObject()" \
535 % self.path()
536 return self._ccObject
537
538 # Create C++ port connections corresponding to the connections in
539 # _port_map (& recursively for all children)
540 def connectPorts(self):
541 for portRef in self._port_map.itervalues():
542 applyOrMap(portRef, 'ccConnect')
543 for child in self._children.itervalues():
544 child.connectPorts()
545
546 def startDrain(self, drain_event, recursive):
547 count = 0
548 # ParamContexts don't serialize
549 if isinstance(self, SimObject) and not isinstance(self, ParamContext):
550 count += self._ccObject.drain(drain_event)
550 if not self._ccObject.drain(drain_event):
551 count = 1
551 if recursive:
552 for child in self._children.itervalues():
553 count += child.startDrain(drain_event, True)
554 return count
555
556 def resume(self):
557 if isinstance(self, SimObject) and not isinstance(self, ParamContext):
558 self._ccObject.resume()
559 for child in self._children.itervalues():
560 child.resume()
561
562 def changeTiming(self, mode):
552 if recursive:
553 for child in self._children.itervalues():
554 count += child.startDrain(drain_event, True)
555 return count
556
557 def resume(self):
558 if isinstance(self, SimObject) and not isinstance(self, ParamContext):
559 self._ccObject.resume()
560 for child in self._children.itervalues():
561 child.resume()
562
563 def changeTiming(self, mode):
563 if isinstance(self, System):
564 if isinstance(self, SimObject) and not isinstance(self, ParamContext):
564 self._ccObject.setMemoryMode(mode)
565 for child in self._children.itervalues():
566 child.changeTiming(mode)
567
568 def takeOverFrom(self, old_cpu):
569 cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject)
570 self._ccObject.takeOverFrom(cpu_ptr)
571
572 # generate output file for 'dot' to display as a pretty graph.
573 # this code is currently broken.
574 def outputDot(self, dot):
575 label = "{%s|" % self.path
576 if isSimObject(self.realtype):
577 label += '%s|' % self.type
578
579 if self.children:
580 # instantiate children in same order they were added for
581 # backward compatibility (else we can end up with cpu1
582 # before cpu0).
583 for c in self.children:
584 dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
585
586 simobjs = []
587 for param in self.params:
588 try:
589 if param.value is None:
590 raise AttributeError, 'Parameter with no value'
591
592 value = param.value
593 string = param.string(value)
594 except Exception, e:
595 msg = 'exception in %s:%s\n%s' % (self.name, param.name, e)
596 e.args = (msg, )
597 raise
598
599 if isSimObject(param.ptype) and string != "Null":
600 simobjs.append(string)
601 else:
602 label += '%s = %s\\n' % (param.name, string)
603
604 for so in simobjs:
605 label += "|<%s> %s" % (so, so)
606 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
607 tailport="w"))
608 label += '}'
609 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
610
611 # recursively dump out children
612 for c in self.children:
613 c.outputDot(dot)
614
615class ParamContext(SimObject):
616 pass
617
618#####################################################################
619#
620# Proxy object support.
621#
622#####################################################################
623
624class BaseProxy(object):
625 def __init__(self, search_self, search_up):
626 self._search_self = search_self
627 self._search_up = search_up
628 self._multiplier = None
629
630 def __setattr__(self, attr, value):
631 if not attr.startswith('_'):
632 raise AttributeError, 'cannot set attribute on proxy object'
633 super(BaseProxy, self).__setattr__(attr, value)
634
635 # support multiplying proxies by constants
636 def __mul__(self, other):
637 if not isinstance(other, (int, long, float)):
638 raise TypeError, "Proxy multiplier must be integer"
639 if self._multiplier == None:
640 self._multiplier = other
641 else:
642 # support chained multipliers
643 self._multiplier *= other
644 return self
645
646 __rmul__ = __mul__
647
648 def _mulcheck(self, result):
649 if self._multiplier == None:
650 return result
651 return result * self._multiplier
652
653 def unproxy(self, base):
654 obj = base
655 done = False
656
657 if self._search_self:
658 result, done = self.find(obj)
659
660 if self._search_up:
661 while not done:
662 obj = obj._parent
663 if not obj:
664 break
665 result, done = self.find(obj)
666
667 if not done:
565 self._ccObject.setMemoryMode(mode)
566 for child in self._children.itervalues():
567 child.changeTiming(mode)
568
569 def takeOverFrom(self, old_cpu):
570 cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject)
571 self._ccObject.takeOverFrom(cpu_ptr)
572
573 # generate output file for 'dot' to display as a pretty graph.
574 # this code is currently broken.
575 def outputDot(self, dot):
576 label = "{%s|" % self.path
577 if isSimObject(self.realtype):
578 label += '%s|' % self.type
579
580 if self.children:
581 # instantiate children in same order they were added for
582 # backward compatibility (else we can end up with cpu1
583 # before cpu0).
584 for c in self.children:
585 dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
586
587 simobjs = []
588 for param in self.params:
589 try:
590 if param.value is None:
591 raise AttributeError, 'Parameter with no value'
592
593 value = param.value
594 string = param.string(value)
595 except Exception, e:
596 msg = 'exception in %s:%s\n%s' % (self.name, param.name, e)
597 e.args = (msg, )
598 raise
599
600 if isSimObject(param.ptype) and string != "Null":
601 simobjs.append(string)
602 else:
603 label += '%s = %s\\n' % (param.name, string)
604
605 for so in simobjs:
606 label += "|<%s> %s" % (so, so)
607 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
608 tailport="w"))
609 label += '}'
610 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
611
612 # recursively dump out children
613 for c in self.children:
614 c.outputDot(dot)
615
616class ParamContext(SimObject):
617 pass
618
619#####################################################################
620#
621# Proxy object support.
622#
623#####################################################################
624
625class BaseProxy(object):
626 def __init__(self, search_self, search_up):
627 self._search_self = search_self
628 self._search_up = search_up
629 self._multiplier = None
630
631 def __setattr__(self, attr, value):
632 if not attr.startswith('_'):
633 raise AttributeError, 'cannot set attribute on proxy object'
634 super(BaseProxy, self).__setattr__(attr, value)
635
636 # support multiplying proxies by constants
637 def __mul__(self, other):
638 if not isinstance(other, (int, long, float)):
639 raise TypeError, "Proxy multiplier must be integer"
640 if self._multiplier == None:
641 self._multiplier = other
642 else:
643 # support chained multipliers
644 self._multiplier *= other
645 return self
646
647 __rmul__ = __mul__
648
649 def _mulcheck(self, result):
650 if self._multiplier == None:
651 return result
652 return result * self._multiplier
653
654 def unproxy(self, base):
655 obj = base
656 done = False
657
658 if self._search_self:
659 result, done = self.find(obj)
660
661 if self._search_up:
662 while not done:
663 obj = obj._parent
664 if not obj:
665 break
666 result, done = self.find(obj)
667
668 if not done:
668 raise AttributeError, "Can't resolve proxy '%s' from '%s'" % \
669 raise AttributeError, \
670 "Can't resolve proxy '%s' from '%s'" % \
669 (self.path(), base.path())
670
671 if isinstance(result, BaseProxy):
672 if result == self:
673 raise RuntimeError, "Cycle in unproxy"
674 result = result.unproxy(obj)
675
676 return self._mulcheck(result)
677
678 def getindex(obj, index):
679 if index == None:
680 return obj
681 try:
682 obj = obj[index]
683 except TypeError:
684 if index != 0:
685 raise
686 # if index is 0 and item is not subscriptable, just
687 # use item itself (so cpu[0] works on uniprocessors)
688 return obj
689 getindex = staticmethod(getindex)
690
691 def set_param_desc(self, pdesc):
692 self._pdesc = pdesc
693
694class AttrProxy(BaseProxy):
695 def __init__(self, search_self, search_up, attr):
696 super(AttrProxy, self).__init__(search_self, search_up)
697 self._attr = attr
698 self._modifiers = []
699
700 def __getattr__(self, attr):
701 # python uses __bases__ internally for inheritance
702 if attr.startswith('_'):
703 return super(AttrProxy, self).__getattr__(self, attr)
704 if hasattr(self, '_pdesc'):
705 raise AttributeError, "Attribute reference on bound proxy"
706 self._modifiers.append(attr)
707 return self
708
709 # support indexing on proxies (e.g., Self.cpu[0])
710 def __getitem__(self, key):
711 if not isinstance(key, int):
712 raise TypeError, "Proxy object requires integer index"
713 self._modifiers.append(key)
714 return self
715
716 def find(self, obj):
717 try:
718 val = getattr(obj, self._attr)
719 except:
720 return None, False
721 while isproxy(val):
722 val = val.unproxy(obj)
723 for m in self._modifiers:
724 if isinstance(m, str):
725 val = getattr(val, m)
726 elif isinstance(m, int):
727 val = val[m]
728 else:
729 assert("Item must be string or integer")
730 while isproxy(val):
731 val = val.unproxy(obj)
732 return val, True
733
734 def path(self):
735 p = self._attr
736 for m in self._modifiers:
737 if isinstance(m, str):
738 p += '.%s' % m
739 elif isinstance(m, int):
740 p += '[%d]' % m
741 else:
742 assert("Item must be string or integer")
743 return p
744
745class AnyProxy(BaseProxy):
746 def find(self, obj):
747 return obj.find_any(self._pdesc.ptype)
748
749 def path(self):
750 return 'any'
751
752def isproxy(obj):
753 if isinstance(obj, (BaseProxy, EthernetAddr)):
754 return True
755 elif isinstance(obj, (list, tuple)):
756 for v in obj:
757 if isproxy(v):
758 return True
759 return False
760
761class ProxyFactory(object):
762 def __init__(self, search_self, search_up):
763 self.search_self = search_self
764 self.search_up = search_up
765
766 def __getattr__(self, attr):
767 if attr == 'any':
768 return AnyProxy(self.search_self, self.search_up)
769 else:
770 return AttrProxy(self.search_self, self.search_up, attr)
771
772# global objects for handling proxies
773Parent = ProxyFactory(search_self = False, search_up = True)
774Self = ProxyFactory(search_self = True, search_up = False)
775
776#####################################################################
777#
778# Parameter description classes
779#
780# The _params dictionary in each class maps parameter names to either
781# a Param or a VectorParam object. These objects contain the
782# parameter description string, the parameter type, and the default
783# value (if any). The convert() method on these objects is used to
784# force whatever value is assigned to the parameter to the appropriate
785# type.
786#
787# Note that the default values are loaded into the class's attribute
788# space when the parameter dictionary is initialized (in
789# MetaSimObject._new_param()); after that point they aren't used.
790#
791#####################################################################
792
793# Dummy base class to identify types that are legitimate for SimObject
794# parameters.
795class ParamValue(object):
796
797 # default for printing to .ini file is regular string conversion.
798 # will be overridden in some cases
799 def ini_str(self):
800 return str(self)
801
802 # allows us to blithely call unproxy() on things without checking
803 # if they're really proxies or not
804 def unproxy(self, base):
805 return self
806
807# Regular parameter description.
808class ParamDesc(object):
809 def __init__(self, ptype_str, ptype, *args, **kwargs):
810 self.ptype_str = ptype_str
811 # remember ptype only if it is provided
812 if ptype != None:
813 self.ptype = ptype
814
815 if args:
816 if len(args) == 1:
817 self.desc = args[0]
818 elif len(args) == 2:
819 self.default = args[0]
820 self.desc = args[1]
821 else:
822 raise TypeError, 'too many arguments'
823
824 if kwargs.has_key('desc'):
825 assert(not hasattr(self, 'desc'))
826 self.desc = kwargs['desc']
827 del kwargs['desc']
828
829 if kwargs.has_key('default'):
830 assert(not hasattr(self, 'default'))
831 self.default = kwargs['default']
832 del kwargs['default']
833
834 if kwargs:
835 raise TypeError, 'extra unknown kwargs %s' % kwargs
836
837 if not hasattr(self, 'desc'):
838 raise TypeError, 'desc attribute missing'
839
840 def __getattr__(self, attr):
841 if attr == 'ptype':
842 try:
843 ptype = eval(self.ptype_str, m5.objects.__dict__)
844 if not isinstance(ptype, type):
845 panic("Param qualifier is not a type: %s" % self.ptype)
846 self.ptype = ptype
847 return ptype
848 except NameError:
849 pass
850 raise AttributeError, "'%s' object has no attribute '%s'" % \
851 (type(self).__name__, attr)
852
853 def convert(self, value):
854 if isinstance(value, BaseProxy):
855 value.set_param_desc(self)
856 return value
857 if not hasattr(self, 'ptype') and isNullPointer(value):
858 # deferred evaluation of SimObject; continue to defer if
859 # we're just assigning a null pointer
860 return value
861 if isinstance(value, self.ptype):
862 return value
863 if isNullPointer(value) and issubclass(self.ptype, SimObject):
864 return value
865 return self.ptype(value)
866
867# Vector-valued parameter description. Just like ParamDesc, except
868# that the value is a vector (list) of the specified type instead of a
869# single value.
870
871class VectorParamValue(list):
872 def ini_str(self):
873 return ' '.join([v.ini_str() for v in self])
874
875 def unproxy(self, base):
876 return [v.unproxy(base) for v in self]
877
878class SimObjVector(VectorParamValue):
879 def print_ini(self):
880 for v in self:
881 v.print_ini()
882
883class VectorParamDesc(ParamDesc):
884 # Convert assigned value to appropriate type. If the RHS is not a
885 # list or tuple, it generates a single-element list.
886 def convert(self, value):
887 if isinstance(value, (list, tuple)):
888 # list: coerce each element into new list
889 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
890 if isSimObjectSequence(tmp_list):
891 return SimObjVector(tmp_list)
892 else:
893 return VectorParamValue(tmp_list)
894 else:
895 # singleton: leave it be (could coerce to a single-element
896 # list here, but for some historical reason we don't...
897 return ParamDesc.convert(self, value)
898
899
900class ParamFactory(object):
901 def __init__(self, param_desc_class, ptype_str = None):
902 self.param_desc_class = param_desc_class
903 self.ptype_str = ptype_str
904
905 def __getattr__(self, attr):
906 if self.ptype_str:
907 attr = self.ptype_str + '.' + attr
908 return ParamFactory(self.param_desc_class, attr)
909
910 # E.g., Param.Int(5, "number of widgets")
911 def __call__(self, *args, **kwargs):
912 caller_frame = inspect.currentframe().f_back
913 ptype = None
914 try:
915 ptype = eval(self.ptype_str,
916 caller_frame.f_globals, caller_frame.f_locals)
917 if not isinstance(ptype, type):
918 raise TypeError, \
919 "Param qualifier is not a type: %s" % ptype
920 except NameError:
921 # if name isn't defined yet, assume it's a SimObject, and
922 # try to resolve it later
923 pass
924 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
925
926Param = ParamFactory(ParamDesc)
927VectorParam = ParamFactory(VectorParamDesc)
928
929#####################################################################
930#
931# Parameter Types
932#
933# Though native Python types could be used to specify parameter types
934# (the 'ptype' field of the Param and VectorParam classes), it's more
935# flexible to define our own set of types. This gives us more control
936# over how Python expressions are converted to values (via the
937# __init__() constructor) and how these values are printed out (via
938# the __str__() conversion method). Eventually we'll need these types
939# to correspond to distinct C++ types as well.
940#
941#####################################################################
942
943# superclass for "numeric" parameter values, to emulate math
944# operations in a type-safe way. e.g., a Latency times an int returns
945# a new Latency object.
946class NumericParamValue(ParamValue):
947 def __str__(self):
948 return str(self.value)
949
950 def __float__(self):
951 return float(self.value)
952
953 # hook for bounds checking
954 def _check(self):
955 return
956
957 def __mul__(self, other):
958 newobj = self.__class__(self)
959 newobj.value *= other
960 newobj._check()
961 return newobj
962
963 __rmul__ = __mul__
964
965 def __div__(self, other):
966 newobj = self.__class__(self)
967 newobj.value /= other
968 newobj._check()
969 return newobj
970
971 def __sub__(self, other):
972 newobj = self.__class__(self)
973 newobj.value -= other
974 newobj._check()
975 return newobj
976
977class Range(ParamValue):
978 type = int # default; can be overridden in subclasses
979 def __init__(self, *args, **kwargs):
980
981 def handle_kwargs(self, kwargs):
982 if 'end' in kwargs:
983 self.second = self.type(kwargs.pop('end'))
984 elif 'size' in kwargs:
985 self.second = self.first + self.type(kwargs.pop('size')) - 1
986 else:
987 raise TypeError, "Either end or size must be specified"
988
989 if len(args) == 0:
990 self.first = self.type(kwargs.pop('start'))
991 handle_kwargs(self, kwargs)
992
993 elif len(args) == 1:
994 if kwargs:
995 self.first = self.type(args[0])
996 handle_kwargs(self, kwargs)
997 elif isinstance(args[0], Range):
998 self.first = self.type(args[0].first)
999 self.second = self.type(args[0].second)
1000 else:
1001 self.first = self.type(0)
1002 self.second = self.type(args[0]) - 1
1003
1004 elif len(args) == 2:
1005 self.first = self.type(args[0])
1006 self.second = self.type(args[1])
1007 else:
1008 raise TypeError, "Too many arguments specified"
1009
1010 if kwargs:
1011 raise TypeError, "too many keywords: %s" % kwargs.keys()
1012
1013 def __str__(self):
1014 return '%s:%s' % (self.first, self.second)
1015
1016# Metaclass for bounds-checked integer parameters. See CheckedInt.
1017class CheckedIntType(type):
1018 def __init__(cls, name, bases, dict):
1019 super(CheckedIntType, cls).__init__(name, bases, dict)
1020
1021 # CheckedInt is an abstract base class, so we actually don't
1022 # want to do any processing on it... the rest of this code is
1023 # just for classes that derive from CheckedInt.
1024 if name == 'CheckedInt':
1025 return
1026
1027 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1028 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1029 panic("CheckedInt subclass %s must define either\n" \
1030 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1031 % name);
1032 if cls.unsigned:
1033 cls.min = 0
1034 cls.max = 2 ** cls.size - 1
1035 else:
1036 cls.min = -(2 ** (cls.size - 1))
1037 cls.max = (2 ** (cls.size - 1)) - 1
1038
1039# Abstract superclass for bounds-checked integer parameters. This
1040# class is subclassed to generate parameter classes with specific
1041# bounds. Initialization of the min and max bounds is done in the
1042# metaclass CheckedIntType.__init__.
1043class CheckedInt(NumericParamValue):
1044 __metaclass__ = CheckedIntType
1045
1046 def _check(self):
1047 if not self.min <= self.value <= self.max:
1048 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1049 (self.min, self.value, self.max)
1050
1051 def __init__(self, value):
1052 if isinstance(value, str):
1053 self.value = toInteger(value)
1054 elif isinstance(value, (int, long, float)):
1055 self.value = long(value)
1056 self._check()
1057
1058class Int(CheckedInt): size = 32; unsigned = False
1059class Unsigned(CheckedInt): size = 32; unsigned = True
1060
1061class Int8(CheckedInt): size = 8; unsigned = False
1062class UInt8(CheckedInt): size = 8; unsigned = True
1063class Int16(CheckedInt): size = 16; unsigned = False
1064class UInt16(CheckedInt): size = 16; unsigned = True
1065class Int32(CheckedInt): size = 32; unsigned = False
1066class UInt32(CheckedInt): size = 32; unsigned = True
1067class Int64(CheckedInt): size = 64; unsigned = False
1068class UInt64(CheckedInt): size = 64; unsigned = True
1069
1070class Counter(CheckedInt): size = 64; unsigned = True
1071class Tick(CheckedInt): size = 64; unsigned = True
1072class TcpPort(CheckedInt): size = 16; unsigned = True
1073class UdpPort(CheckedInt): size = 16; unsigned = True
1074
1075class Percent(CheckedInt): min = 0; max = 100
1076
1077class Float(ParamValue, float):
1078 pass
1079
1080class MemorySize(CheckedInt):
1081 size = 64
1082 unsigned = True
1083 def __init__(self, value):
1084 if isinstance(value, MemorySize):
1085 self.value = value.value
1086 else:
1087 self.value = toMemorySize(value)
1088 self._check()
1089
1090class MemorySize32(CheckedInt):
1091 size = 32
1092 unsigned = True
1093 def __init__(self, value):
1094 if isinstance(value, MemorySize):
1095 self.value = value.value
1096 else:
1097 self.value = toMemorySize(value)
1098 self._check()
1099
1100class Addr(CheckedInt):
1101 size = 64
1102 unsigned = True
1103 def __init__(self, value):
1104 if isinstance(value, Addr):
1105 self.value = value.value
1106 else:
1107 try:
1108 self.value = toMemorySize(value)
1109 except TypeError:
1110 self.value = long(value)
1111 self._check()
1112
1113class AddrRange(Range):
1114 type = Addr
1115
1116# String-valued parameter. Just mixin the ParamValue class
1117# with the built-in str class.
1118class String(ParamValue,str):
1119 pass
1120
1121# Boolean parameter type. Python doesn't let you subclass bool, since
1122# it doesn't want to let you create multiple instances of True and
1123# False. Thus this is a little more complicated than String.
1124class Bool(ParamValue):
1125 def __init__(self, value):
1126 try:
1127 self.value = toBool(value)
1128 except TypeError:
1129 self.value = bool(value)
1130
1131 def __str__(self):
1132 return str(self.value)
1133
1134 def ini_str(self):
1135 if self.value:
1136 return 'true'
1137 return 'false'
1138
1139def IncEthernetAddr(addr, val = 1):
1140 bytes = map(lambda x: int(x, 16), addr.split(':'))
1141 bytes[5] += val
1142 for i in (5, 4, 3, 2, 1):
1143 val,rem = divmod(bytes[i], 256)
1144 bytes[i] = rem
1145 if val == 0:
1146 break
1147 bytes[i - 1] += val
1148 assert(bytes[0] <= 255)
1149 return ':'.join(map(lambda x: '%02x' % x, bytes))
1150
1151class NextEthernetAddr(object):
1152 addr = "00:90:00:00:00:01"
1153
1154 def __init__(self, inc = 1):
1155 self.value = NextEthernetAddr.addr
1156 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
1157
1158class EthernetAddr(ParamValue):
1159 def __init__(self, value):
1160 if value == NextEthernetAddr:
1161 self.value = value
1162 return
1163
1164 if not isinstance(value, str):
1165 raise TypeError, "expected an ethernet address and didn't get one"
1166
1167 bytes = value.split(':')
1168 if len(bytes) != 6:
1169 raise TypeError, 'invalid ethernet address %s' % value
1170
1171 for byte in bytes:
1172 if not 0 <= int(byte) <= 256:
1173 raise TypeError, 'invalid ethernet address %s' % value
1174
1175 self.value = value
1176
1177 def unproxy(self, base):
1178 if self.value == NextEthernetAddr:
1179 self.addr = self.value().value
1180 return self
1181
1182 def __str__(self):
1183 if self.value == NextEthernetAddr:
1184 if hasattr(self, 'addr'):
1185 return self.addr
1186 else:
1187 return "NextEthernetAddr (unresolved)"
1188 else:
1189 return self.value
1190
1191# Special class for NULL pointers. Note the special check in
1192# make_param_value() above that lets these be assigned where a
1193# SimObject is required.
1194# only one copy of a particular node
1195class NullSimObject(object):
1196 __metaclass__ = Singleton
1197
1198 def __call__(cls):
1199 return cls
1200
1201 def _instantiate(self, parent = None, path = ''):
1202 pass
1203
1204 def ini_str(self):
1205 return 'Null'
1206
1207 def unproxy(self, base):
1208 return self
1209
1210 def set_path(self, parent, name):
1211 pass
1212 def __str__(self):
1213 return 'Null'
1214
1215# The only instance you'll ever need...
1216Null = NULL = NullSimObject()
1217
1218# Enumerated types are a little more complex. The user specifies the
1219# type as Enum(foo) where foo is either a list or dictionary of
1220# alternatives (typically strings, but not necessarily so). (In the
1221# long run, the integer value of the parameter will be the list index
1222# or the corresponding dictionary value. For now, since we only check
1223# that the alternative is valid and then spit it into a .ini file,
1224# there's not much point in using the dictionary.)
1225
1226# What Enum() must do is generate a new type encapsulating the
1227# provided list/dictionary so that specific values of the parameter
1228# can be instances of that type. We define two hidden internal
1229# classes (_ListEnum and _DictEnum) to serve as base classes, then
1230# derive the new type from the appropriate base class on the fly.
1231
1232
1233# Metaclass for Enum types
1234class MetaEnum(type):
1235 def __init__(cls, name, bases, init_dict):
1236 if init_dict.has_key('map'):
1237 if not isinstance(cls.map, dict):
1238 raise TypeError, "Enum-derived class attribute 'map' " \
1239 "must be of type dict"
1240 # build list of value strings from map
1241 cls.vals = cls.map.keys()
1242 cls.vals.sort()
1243 elif init_dict.has_key('vals'):
1244 if not isinstance(cls.vals, list):
1245 raise TypeError, "Enum-derived class attribute 'vals' " \
1246 "must be of type list"
1247 # build string->value map from vals sequence
1248 cls.map = {}
1249 for idx,val in enumerate(cls.vals):
1250 cls.map[val] = idx
1251 else:
1252 raise TypeError, "Enum-derived class must define "\
1253 "attribute 'map' or 'vals'"
1254
1255 super(MetaEnum, cls).__init__(name, bases, init_dict)
1256
1257 def cpp_declare(cls):
1258 s = 'enum %s {\n ' % cls.__name__
1259 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1260 s += '\n};\n'
1261 return s
1262
1263# Base class for enum types.
1264class Enum(ParamValue):
1265 __metaclass__ = MetaEnum
1266 vals = []
1267
1268 def __init__(self, value):
1269 if value not in self.map:
1270 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1271 % (value, self.vals)
1272 self.value = value
1273
1274 def __str__(self):
1275 return self.value
1276
1277ticks_per_sec = None
1278
1279# how big does a rounding error need to be before we warn about it?
1280frequency_tolerance = 0.001 # 0.1%
1281
1282# convert a floting-point # of ticks to integer, and warn if rounding
1283# discards too much precision
1284def tick_check(float_ticks):
1285 if float_ticks == 0:
1286 return 0
1287 int_ticks = int(round(float_ticks))
1288 err = (float_ticks - int_ticks) / float_ticks
1289 if err > frequency_tolerance:
1290 print >> sys.stderr, "Warning: rounding error > tolerance"
1291 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
1292 #raise ValueError
1293 return int_ticks
1294
1295def getLatency(value):
1296 if isinstance(value, Latency) or isinstance(value, Clock):
1297 return value.value
1298 elif isinstance(value, Frequency) or isinstance(value, RootClock):
1299 return 1 / value.value
1300 elif isinstance(value, str):
1301 try:
1302 return toLatency(value)
1303 except ValueError:
1304 try:
1305 return 1 / toFrequency(value)
1306 except ValueError:
1307 pass # fall through
1308 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
1309
1310
1311class Latency(NumericParamValue):
1312 def __init__(self, value):
1313 self.value = getLatency(value)
1314
1315 def __getattr__(self, attr):
1316 if attr in ('latency', 'period'):
1317 return self
1318 if attr == 'frequency':
1319 return Frequency(self)
1320 raise AttributeError, "Latency object has no attribute '%s'" % attr
1321
1322 # convert latency to ticks
1323 def ini_str(self):
1324 return str(tick_check(self.value * ticks_per_sec))
1325
1326class Frequency(NumericParamValue):
1327 def __init__(self, value):
1328 self.value = 1 / getLatency(value)
1329
1330 def __getattr__(self, attr):
1331 if attr == 'frequency':
1332 return self
1333 if attr in ('latency', 'period'):
1334 return Latency(self)
1335 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1336
1337 # convert frequency to ticks per period
1338 def ini_str(self):
1339 return self.period.ini_str()
1340
1341# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
1342# We can't inherit from Frequency because we don't want it to be directly
1343# assignable to a regular Frequency parameter.
1344class RootClock(ParamValue):
1345 def __init__(self, value):
1346 self.value = 1 / getLatency(value)
1347
1348 def __getattr__(self, attr):
1349 if attr == 'frequency':
1350 return Frequency(self)
1351 if attr in ('latency', 'period'):
1352 return Latency(self)
1353 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1354
1355 def ini_str(self):
1356 return str(tick_check(self.value))
1357
1358# A generic frequency and/or Latency value. Value is stored as a latency,
1359# but to avoid ambiguity this object does not support numeric ops (* or /).
1360# An explicit conversion to a Latency or Frequency must be made first.
1361class Clock(ParamValue):
1362 def __init__(self, value):
1363 self.value = getLatency(value)
1364
1365 def __getattr__(self, attr):
1366 if attr == 'frequency':
1367 return Frequency(self)
1368 if attr in ('latency', 'period'):
1369 return Latency(self)
1370 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1371
1372 def ini_str(self):
1373 return self.period.ini_str()
1374
1375class NetworkBandwidth(float,ParamValue):
1376 def __new__(cls, value):
1377 val = toNetworkBandwidth(value) / 8.0
1378 return super(cls, NetworkBandwidth).__new__(cls, val)
1379
1380 def __str__(self):
1381 return str(self.val)
1382
1383 def ini_str(self):
1384 return '%f' % (ticks_per_sec / float(self))
1385
1386class MemoryBandwidth(float,ParamValue):
1387 def __new__(self, value):
1388 val = toMemoryBandwidth(value)
1389 return super(cls, MemoryBandwidth).__new__(cls, val)
1390
1391 def __str__(self):
1392 return str(self.val)
1393
1394 def ini_str(self):
1395 return '%f' % (ticks_per_sec / float(self))
1396
1397#
1398# "Constants"... handy aliases for various values.
1399#
1400
1401# Some memory range specifications use this as a default upper bound.
1402MaxAddr = Addr.max
1403MaxTick = Tick.max
1404AllMemory = AddrRange(0, MaxAddr)
1405
1406
1407#####################################################################
1408#
1409# Port objects
1410#
1411# Ports are used to interconnect objects in the memory system.
1412#
1413#####################################################################
1414
1415# Port reference: encapsulates a reference to a particular port on a
1416# particular SimObject.
1417class PortRef(object):
1418 def __init__(self, simobj, name, isVec):
1419 assert(isSimObject(simobj))
1420 self.simobj = simobj
1421 self.name = name
1422 self.index = -1
1423 self.isVec = isVec # is this a vector port?
1424 self.peer = None # not associated with another port yet
1425 self.ccConnected = False # C++ port connection done?
1426
1427 # Set peer port reference. Called via __setattr__ as a result of
1428 # a port assignment, e.g., "obj1.port1 = obj2.port2".
1429 def setPeer(self, other):
1430 if self.isVec:
1431 curMap = self.simobj._port_map.get(self.name, [])
1432 self.index = len(curMap)
1433 curMap.append(other)
1434 else:
1435 curMap = self.simobj._port_map.get(self.name)
1436 if curMap and not self.isVec:
1437 print "warning: overwriting port", self.simobj, self.name
1438 curMap = other
1439 self.simobj._port_map[self.name] = curMap
1440 self.peer = other
1441
1442 def clone(self, memo):
1443 newRef = copy.copy(self)
1444 assert(isSimObject(newRef.simobj))
1445 newRef.simobj = newRef.simobj(_memo=memo)
1446 # Tricky: if I'm the *second* PortRef in the pair to be
1447 # cloned, then my peer is still in the middle of its clone
1448 # method, and thus hasn't returned to its owner's
1449 # SimObject.__init__ to get installed in _port_map. As a
1450 # result I have no way of finding the *new* peer object. So I
1451 # mark myself as "waiting" for my peer, and I let the *first*
1452 # PortRef clone call set up both peer pointers after I return.
1453 newPeer = newRef.simobj._port_map.get(self.name)
1454 if newPeer:
1455 if self.isVec:
1456 assert(self.index != -1)
1457 newPeer = newPeer[self.index]
1458 # other guy is all set up except for his peer pointer
1459 assert(newPeer.peer == -1) # peer must be waiting for handshake
1460 newPeer.peer = newRef
1461 newRef.peer = newPeer
1462 else:
1463 # other guy is in clone; just wait for him to do the work
1464 newRef.peer = -1 # mark as waiting for handshake
1465 return newRef
1466
1467 # Call C++ to create corresponding port connection between C++ objects
1468 def ccConnect(self):
1469 if self.ccConnected: # already done this
1470 return
1471 peer = self.peer
1472 cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
1473 peer.simobj.getCCObject(), peer.name, peer.index)
1474 self.ccConnected = True
1475 peer.ccConnected = True
1476
1477# Port description object. Like a ParamDesc object, this represents a
1478# logical port in the SimObject class, not a particular port on a
1479# SimObject instance. The latter are represented by PortRef objects.
1480class Port(object):
1481 def __init__(self, desc):
1482 self.desc = desc
1483 self.isVec = False
1484
1485 # Generate a PortRef for this port on the given SimObject with the
1486 # given name
1487 def makeRef(self, simobj, name):
1488 return PortRef(simobj, name, self.isVec)
1489
1490 # Connect an instance of this port (on the given SimObject with
1491 # the given name) with the port described by the supplied PortRef
1492 def connect(self, simobj, name, ref):
1493 if not isinstance(ref, PortRef):
1494 raise TypeError, \
1495 "assigning non-port reference port '%s'" % name
1496 myRef = self.makeRef(simobj, name)
1497 myRef.setPeer(ref)
1498 ref.setPeer(myRef)
1499
1500# VectorPort description object. Like Port, but represents a vector
1501# of connections (e.g., as on a Bus).
1502class VectorPort(Port):
1503 def __init__(self, desc):
1504 Port.__init__(self, desc)
1505 self.isVec = True
1506
1507#####################################################################
1508
1509# __all__ defines the list of symbols that get exported when
1510# 'from config import *' is invoked. Try to keep this reasonably
1511# short to avoid polluting other namespaces.
1512__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam',
1513 'Parent', 'Self',
1514 'Enum', 'Bool', 'String', 'Float',
1515 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1516 'Int32', 'UInt32', 'Int64', 'UInt64',
1517 'Counter', 'Addr', 'Tick', 'Percent',
1518 'TcpPort', 'UdpPort', 'EthernetAddr',
1519 'MemorySize', 'MemorySize32',
1520 'Latency', 'Frequency', 'RootClock', 'Clock',
1521 'NetworkBandwidth', 'MemoryBandwidth',
1522 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory',
1523 'Null', 'NULL',
1524 'NextEthernetAddr',
1525 'Port', 'VectorPort']
1526
671 (self.path(), base.path())
672
673 if isinstance(result, BaseProxy):
674 if result == self:
675 raise RuntimeError, "Cycle in unproxy"
676 result = result.unproxy(obj)
677
678 return self._mulcheck(result)
679
680 def getindex(obj, index):
681 if index == None:
682 return obj
683 try:
684 obj = obj[index]
685 except TypeError:
686 if index != 0:
687 raise
688 # if index is 0 and item is not subscriptable, just
689 # use item itself (so cpu[0] works on uniprocessors)
690 return obj
691 getindex = staticmethod(getindex)
692
693 def set_param_desc(self, pdesc):
694 self._pdesc = pdesc
695
696class AttrProxy(BaseProxy):
697 def __init__(self, search_self, search_up, attr):
698 super(AttrProxy, self).__init__(search_self, search_up)
699 self._attr = attr
700 self._modifiers = []
701
702 def __getattr__(self, attr):
703 # python uses __bases__ internally for inheritance
704 if attr.startswith('_'):
705 return super(AttrProxy, self).__getattr__(self, attr)
706 if hasattr(self, '_pdesc'):
707 raise AttributeError, "Attribute reference on bound proxy"
708 self._modifiers.append(attr)
709 return self
710
711 # support indexing on proxies (e.g., Self.cpu[0])
712 def __getitem__(self, key):
713 if not isinstance(key, int):
714 raise TypeError, "Proxy object requires integer index"
715 self._modifiers.append(key)
716 return self
717
718 def find(self, obj):
719 try:
720 val = getattr(obj, self._attr)
721 except:
722 return None, False
723 while isproxy(val):
724 val = val.unproxy(obj)
725 for m in self._modifiers:
726 if isinstance(m, str):
727 val = getattr(val, m)
728 elif isinstance(m, int):
729 val = val[m]
730 else:
731 assert("Item must be string or integer")
732 while isproxy(val):
733 val = val.unproxy(obj)
734 return val, True
735
736 def path(self):
737 p = self._attr
738 for m in self._modifiers:
739 if isinstance(m, str):
740 p += '.%s' % m
741 elif isinstance(m, int):
742 p += '[%d]' % m
743 else:
744 assert("Item must be string or integer")
745 return p
746
747class AnyProxy(BaseProxy):
748 def find(self, obj):
749 return obj.find_any(self._pdesc.ptype)
750
751 def path(self):
752 return 'any'
753
754def isproxy(obj):
755 if isinstance(obj, (BaseProxy, EthernetAddr)):
756 return True
757 elif isinstance(obj, (list, tuple)):
758 for v in obj:
759 if isproxy(v):
760 return True
761 return False
762
763class ProxyFactory(object):
764 def __init__(self, search_self, search_up):
765 self.search_self = search_self
766 self.search_up = search_up
767
768 def __getattr__(self, attr):
769 if attr == 'any':
770 return AnyProxy(self.search_self, self.search_up)
771 else:
772 return AttrProxy(self.search_self, self.search_up, attr)
773
774# global objects for handling proxies
775Parent = ProxyFactory(search_self = False, search_up = True)
776Self = ProxyFactory(search_self = True, search_up = False)
777
778#####################################################################
779#
780# Parameter description classes
781#
782# The _params dictionary in each class maps parameter names to either
783# a Param or a VectorParam object. These objects contain the
784# parameter description string, the parameter type, and the default
785# value (if any). The convert() method on these objects is used to
786# force whatever value is assigned to the parameter to the appropriate
787# type.
788#
789# Note that the default values are loaded into the class's attribute
790# space when the parameter dictionary is initialized (in
791# MetaSimObject._new_param()); after that point they aren't used.
792#
793#####################################################################
794
795# Dummy base class to identify types that are legitimate for SimObject
796# parameters.
797class ParamValue(object):
798
799 # default for printing to .ini file is regular string conversion.
800 # will be overridden in some cases
801 def ini_str(self):
802 return str(self)
803
804 # allows us to blithely call unproxy() on things without checking
805 # if they're really proxies or not
806 def unproxy(self, base):
807 return self
808
809# Regular parameter description.
810class ParamDesc(object):
811 def __init__(self, ptype_str, ptype, *args, **kwargs):
812 self.ptype_str = ptype_str
813 # remember ptype only if it is provided
814 if ptype != None:
815 self.ptype = ptype
816
817 if args:
818 if len(args) == 1:
819 self.desc = args[0]
820 elif len(args) == 2:
821 self.default = args[0]
822 self.desc = args[1]
823 else:
824 raise TypeError, 'too many arguments'
825
826 if kwargs.has_key('desc'):
827 assert(not hasattr(self, 'desc'))
828 self.desc = kwargs['desc']
829 del kwargs['desc']
830
831 if kwargs.has_key('default'):
832 assert(not hasattr(self, 'default'))
833 self.default = kwargs['default']
834 del kwargs['default']
835
836 if kwargs:
837 raise TypeError, 'extra unknown kwargs %s' % kwargs
838
839 if not hasattr(self, 'desc'):
840 raise TypeError, 'desc attribute missing'
841
842 def __getattr__(self, attr):
843 if attr == 'ptype':
844 try:
845 ptype = eval(self.ptype_str, m5.objects.__dict__)
846 if not isinstance(ptype, type):
847 panic("Param qualifier is not a type: %s" % self.ptype)
848 self.ptype = ptype
849 return ptype
850 except NameError:
851 pass
852 raise AttributeError, "'%s' object has no attribute '%s'" % \
853 (type(self).__name__, attr)
854
855 def convert(self, value):
856 if isinstance(value, BaseProxy):
857 value.set_param_desc(self)
858 return value
859 if not hasattr(self, 'ptype') and isNullPointer(value):
860 # deferred evaluation of SimObject; continue to defer if
861 # we're just assigning a null pointer
862 return value
863 if isinstance(value, self.ptype):
864 return value
865 if isNullPointer(value) and issubclass(self.ptype, SimObject):
866 return value
867 return self.ptype(value)
868
869# Vector-valued parameter description. Just like ParamDesc, except
870# that the value is a vector (list) of the specified type instead of a
871# single value.
872
873class VectorParamValue(list):
874 def ini_str(self):
875 return ' '.join([v.ini_str() for v in self])
876
877 def unproxy(self, base):
878 return [v.unproxy(base) for v in self]
879
880class SimObjVector(VectorParamValue):
881 def print_ini(self):
882 for v in self:
883 v.print_ini()
884
885class VectorParamDesc(ParamDesc):
886 # Convert assigned value to appropriate type. If the RHS is not a
887 # list or tuple, it generates a single-element list.
888 def convert(self, value):
889 if isinstance(value, (list, tuple)):
890 # list: coerce each element into new list
891 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
892 if isSimObjectSequence(tmp_list):
893 return SimObjVector(tmp_list)
894 else:
895 return VectorParamValue(tmp_list)
896 else:
897 # singleton: leave it be (could coerce to a single-element
898 # list here, but for some historical reason we don't...
899 return ParamDesc.convert(self, value)
900
901
902class ParamFactory(object):
903 def __init__(self, param_desc_class, ptype_str = None):
904 self.param_desc_class = param_desc_class
905 self.ptype_str = ptype_str
906
907 def __getattr__(self, attr):
908 if self.ptype_str:
909 attr = self.ptype_str + '.' + attr
910 return ParamFactory(self.param_desc_class, attr)
911
912 # E.g., Param.Int(5, "number of widgets")
913 def __call__(self, *args, **kwargs):
914 caller_frame = inspect.currentframe().f_back
915 ptype = None
916 try:
917 ptype = eval(self.ptype_str,
918 caller_frame.f_globals, caller_frame.f_locals)
919 if not isinstance(ptype, type):
920 raise TypeError, \
921 "Param qualifier is not a type: %s" % ptype
922 except NameError:
923 # if name isn't defined yet, assume it's a SimObject, and
924 # try to resolve it later
925 pass
926 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
927
928Param = ParamFactory(ParamDesc)
929VectorParam = ParamFactory(VectorParamDesc)
930
931#####################################################################
932#
933# Parameter Types
934#
935# Though native Python types could be used to specify parameter types
936# (the 'ptype' field of the Param and VectorParam classes), it's more
937# flexible to define our own set of types. This gives us more control
938# over how Python expressions are converted to values (via the
939# __init__() constructor) and how these values are printed out (via
940# the __str__() conversion method). Eventually we'll need these types
941# to correspond to distinct C++ types as well.
942#
943#####################################################################
944
945# superclass for "numeric" parameter values, to emulate math
946# operations in a type-safe way. e.g., a Latency times an int returns
947# a new Latency object.
948class NumericParamValue(ParamValue):
949 def __str__(self):
950 return str(self.value)
951
952 def __float__(self):
953 return float(self.value)
954
955 # hook for bounds checking
956 def _check(self):
957 return
958
959 def __mul__(self, other):
960 newobj = self.__class__(self)
961 newobj.value *= other
962 newobj._check()
963 return newobj
964
965 __rmul__ = __mul__
966
967 def __div__(self, other):
968 newobj = self.__class__(self)
969 newobj.value /= other
970 newobj._check()
971 return newobj
972
973 def __sub__(self, other):
974 newobj = self.__class__(self)
975 newobj.value -= other
976 newobj._check()
977 return newobj
978
979class Range(ParamValue):
980 type = int # default; can be overridden in subclasses
981 def __init__(self, *args, **kwargs):
982
983 def handle_kwargs(self, kwargs):
984 if 'end' in kwargs:
985 self.second = self.type(kwargs.pop('end'))
986 elif 'size' in kwargs:
987 self.second = self.first + self.type(kwargs.pop('size')) - 1
988 else:
989 raise TypeError, "Either end or size must be specified"
990
991 if len(args) == 0:
992 self.first = self.type(kwargs.pop('start'))
993 handle_kwargs(self, kwargs)
994
995 elif len(args) == 1:
996 if kwargs:
997 self.first = self.type(args[0])
998 handle_kwargs(self, kwargs)
999 elif isinstance(args[0], Range):
1000 self.first = self.type(args[0].first)
1001 self.second = self.type(args[0].second)
1002 else:
1003 self.first = self.type(0)
1004 self.second = self.type(args[0]) - 1
1005
1006 elif len(args) == 2:
1007 self.first = self.type(args[0])
1008 self.second = self.type(args[1])
1009 else:
1010 raise TypeError, "Too many arguments specified"
1011
1012 if kwargs:
1013 raise TypeError, "too many keywords: %s" % kwargs.keys()
1014
1015 def __str__(self):
1016 return '%s:%s' % (self.first, self.second)
1017
1018# Metaclass for bounds-checked integer parameters. See CheckedInt.
1019class CheckedIntType(type):
1020 def __init__(cls, name, bases, dict):
1021 super(CheckedIntType, cls).__init__(name, bases, dict)
1022
1023 # CheckedInt is an abstract base class, so we actually don't
1024 # want to do any processing on it... the rest of this code is
1025 # just for classes that derive from CheckedInt.
1026 if name == 'CheckedInt':
1027 return
1028
1029 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1030 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1031 panic("CheckedInt subclass %s must define either\n" \
1032 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1033 % name);
1034 if cls.unsigned:
1035 cls.min = 0
1036 cls.max = 2 ** cls.size - 1
1037 else:
1038 cls.min = -(2 ** (cls.size - 1))
1039 cls.max = (2 ** (cls.size - 1)) - 1
1040
1041# Abstract superclass for bounds-checked integer parameters. This
1042# class is subclassed to generate parameter classes with specific
1043# bounds. Initialization of the min and max bounds is done in the
1044# metaclass CheckedIntType.__init__.
1045class CheckedInt(NumericParamValue):
1046 __metaclass__ = CheckedIntType
1047
1048 def _check(self):
1049 if not self.min <= self.value <= self.max:
1050 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1051 (self.min, self.value, self.max)
1052
1053 def __init__(self, value):
1054 if isinstance(value, str):
1055 self.value = toInteger(value)
1056 elif isinstance(value, (int, long, float)):
1057 self.value = long(value)
1058 self._check()
1059
1060class Int(CheckedInt): size = 32; unsigned = False
1061class Unsigned(CheckedInt): size = 32; unsigned = True
1062
1063class Int8(CheckedInt): size = 8; unsigned = False
1064class UInt8(CheckedInt): size = 8; unsigned = True
1065class Int16(CheckedInt): size = 16; unsigned = False
1066class UInt16(CheckedInt): size = 16; unsigned = True
1067class Int32(CheckedInt): size = 32; unsigned = False
1068class UInt32(CheckedInt): size = 32; unsigned = True
1069class Int64(CheckedInt): size = 64; unsigned = False
1070class UInt64(CheckedInt): size = 64; unsigned = True
1071
1072class Counter(CheckedInt): size = 64; unsigned = True
1073class Tick(CheckedInt): size = 64; unsigned = True
1074class TcpPort(CheckedInt): size = 16; unsigned = True
1075class UdpPort(CheckedInt): size = 16; unsigned = True
1076
1077class Percent(CheckedInt): min = 0; max = 100
1078
1079class Float(ParamValue, float):
1080 pass
1081
1082class MemorySize(CheckedInt):
1083 size = 64
1084 unsigned = True
1085 def __init__(self, value):
1086 if isinstance(value, MemorySize):
1087 self.value = value.value
1088 else:
1089 self.value = toMemorySize(value)
1090 self._check()
1091
1092class MemorySize32(CheckedInt):
1093 size = 32
1094 unsigned = True
1095 def __init__(self, value):
1096 if isinstance(value, MemorySize):
1097 self.value = value.value
1098 else:
1099 self.value = toMemorySize(value)
1100 self._check()
1101
1102class Addr(CheckedInt):
1103 size = 64
1104 unsigned = True
1105 def __init__(self, value):
1106 if isinstance(value, Addr):
1107 self.value = value.value
1108 else:
1109 try:
1110 self.value = toMemorySize(value)
1111 except TypeError:
1112 self.value = long(value)
1113 self._check()
1114
1115class AddrRange(Range):
1116 type = Addr
1117
1118# String-valued parameter. Just mixin the ParamValue class
1119# with the built-in str class.
1120class String(ParamValue,str):
1121 pass
1122
1123# Boolean parameter type. Python doesn't let you subclass bool, since
1124# it doesn't want to let you create multiple instances of True and
1125# False. Thus this is a little more complicated than String.
1126class Bool(ParamValue):
1127 def __init__(self, value):
1128 try:
1129 self.value = toBool(value)
1130 except TypeError:
1131 self.value = bool(value)
1132
1133 def __str__(self):
1134 return str(self.value)
1135
1136 def ini_str(self):
1137 if self.value:
1138 return 'true'
1139 return 'false'
1140
1141def IncEthernetAddr(addr, val = 1):
1142 bytes = map(lambda x: int(x, 16), addr.split(':'))
1143 bytes[5] += val
1144 for i in (5, 4, 3, 2, 1):
1145 val,rem = divmod(bytes[i], 256)
1146 bytes[i] = rem
1147 if val == 0:
1148 break
1149 bytes[i - 1] += val
1150 assert(bytes[0] <= 255)
1151 return ':'.join(map(lambda x: '%02x' % x, bytes))
1152
1153class NextEthernetAddr(object):
1154 addr = "00:90:00:00:00:01"
1155
1156 def __init__(self, inc = 1):
1157 self.value = NextEthernetAddr.addr
1158 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
1159
1160class EthernetAddr(ParamValue):
1161 def __init__(self, value):
1162 if value == NextEthernetAddr:
1163 self.value = value
1164 return
1165
1166 if not isinstance(value, str):
1167 raise TypeError, "expected an ethernet address and didn't get one"
1168
1169 bytes = value.split(':')
1170 if len(bytes) != 6:
1171 raise TypeError, 'invalid ethernet address %s' % value
1172
1173 for byte in bytes:
1174 if not 0 <= int(byte) <= 256:
1175 raise TypeError, 'invalid ethernet address %s' % value
1176
1177 self.value = value
1178
1179 def unproxy(self, base):
1180 if self.value == NextEthernetAddr:
1181 self.addr = self.value().value
1182 return self
1183
1184 def __str__(self):
1185 if self.value == NextEthernetAddr:
1186 if hasattr(self, 'addr'):
1187 return self.addr
1188 else:
1189 return "NextEthernetAddr (unresolved)"
1190 else:
1191 return self.value
1192
1193# Special class for NULL pointers. Note the special check in
1194# make_param_value() above that lets these be assigned where a
1195# SimObject is required.
1196# only one copy of a particular node
1197class NullSimObject(object):
1198 __metaclass__ = Singleton
1199
1200 def __call__(cls):
1201 return cls
1202
1203 def _instantiate(self, parent = None, path = ''):
1204 pass
1205
1206 def ini_str(self):
1207 return 'Null'
1208
1209 def unproxy(self, base):
1210 return self
1211
1212 def set_path(self, parent, name):
1213 pass
1214 def __str__(self):
1215 return 'Null'
1216
1217# The only instance you'll ever need...
1218Null = NULL = NullSimObject()
1219
1220# Enumerated types are a little more complex. The user specifies the
1221# type as Enum(foo) where foo is either a list or dictionary of
1222# alternatives (typically strings, but not necessarily so). (In the
1223# long run, the integer value of the parameter will be the list index
1224# or the corresponding dictionary value. For now, since we only check
1225# that the alternative is valid and then spit it into a .ini file,
1226# there's not much point in using the dictionary.)
1227
1228# What Enum() must do is generate a new type encapsulating the
1229# provided list/dictionary so that specific values of the parameter
1230# can be instances of that type. We define two hidden internal
1231# classes (_ListEnum and _DictEnum) to serve as base classes, then
1232# derive the new type from the appropriate base class on the fly.
1233
1234
1235# Metaclass for Enum types
1236class MetaEnum(type):
1237 def __init__(cls, name, bases, init_dict):
1238 if init_dict.has_key('map'):
1239 if not isinstance(cls.map, dict):
1240 raise TypeError, "Enum-derived class attribute 'map' " \
1241 "must be of type dict"
1242 # build list of value strings from map
1243 cls.vals = cls.map.keys()
1244 cls.vals.sort()
1245 elif init_dict.has_key('vals'):
1246 if not isinstance(cls.vals, list):
1247 raise TypeError, "Enum-derived class attribute 'vals' " \
1248 "must be of type list"
1249 # build string->value map from vals sequence
1250 cls.map = {}
1251 for idx,val in enumerate(cls.vals):
1252 cls.map[val] = idx
1253 else:
1254 raise TypeError, "Enum-derived class must define "\
1255 "attribute 'map' or 'vals'"
1256
1257 super(MetaEnum, cls).__init__(name, bases, init_dict)
1258
1259 def cpp_declare(cls):
1260 s = 'enum %s {\n ' % cls.__name__
1261 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1262 s += '\n};\n'
1263 return s
1264
1265# Base class for enum types.
1266class Enum(ParamValue):
1267 __metaclass__ = MetaEnum
1268 vals = []
1269
1270 def __init__(self, value):
1271 if value not in self.map:
1272 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1273 % (value, self.vals)
1274 self.value = value
1275
1276 def __str__(self):
1277 return self.value
1278
1279ticks_per_sec = None
1280
1281# how big does a rounding error need to be before we warn about it?
1282frequency_tolerance = 0.001 # 0.1%
1283
1284# convert a floting-point # of ticks to integer, and warn if rounding
1285# discards too much precision
1286def tick_check(float_ticks):
1287 if float_ticks == 0:
1288 return 0
1289 int_ticks = int(round(float_ticks))
1290 err = (float_ticks - int_ticks) / float_ticks
1291 if err > frequency_tolerance:
1292 print >> sys.stderr, "Warning: rounding error > tolerance"
1293 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
1294 #raise ValueError
1295 return int_ticks
1296
1297def getLatency(value):
1298 if isinstance(value, Latency) or isinstance(value, Clock):
1299 return value.value
1300 elif isinstance(value, Frequency) or isinstance(value, RootClock):
1301 return 1 / value.value
1302 elif isinstance(value, str):
1303 try:
1304 return toLatency(value)
1305 except ValueError:
1306 try:
1307 return 1 / toFrequency(value)
1308 except ValueError:
1309 pass # fall through
1310 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
1311
1312
1313class Latency(NumericParamValue):
1314 def __init__(self, value):
1315 self.value = getLatency(value)
1316
1317 def __getattr__(self, attr):
1318 if attr in ('latency', 'period'):
1319 return self
1320 if attr == 'frequency':
1321 return Frequency(self)
1322 raise AttributeError, "Latency object has no attribute '%s'" % attr
1323
1324 # convert latency to ticks
1325 def ini_str(self):
1326 return str(tick_check(self.value * ticks_per_sec))
1327
1328class Frequency(NumericParamValue):
1329 def __init__(self, value):
1330 self.value = 1 / getLatency(value)
1331
1332 def __getattr__(self, attr):
1333 if attr == 'frequency':
1334 return self
1335 if attr in ('latency', 'period'):
1336 return Latency(self)
1337 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1338
1339 # convert frequency to ticks per period
1340 def ini_str(self):
1341 return self.period.ini_str()
1342
1343# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
1344# We can't inherit from Frequency because we don't want it to be directly
1345# assignable to a regular Frequency parameter.
1346class RootClock(ParamValue):
1347 def __init__(self, value):
1348 self.value = 1 / getLatency(value)
1349
1350 def __getattr__(self, attr):
1351 if attr == 'frequency':
1352 return Frequency(self)
1353 if attr in ('latency', 'period'):
1354 return Latency(self)
1355 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1356
1357 def ini_str(self):
1358 return str(tick_check(self.value))
1359
1360# A generic frequency and/or Latency value. Value is stored as a latency,
1361# but to avoid ambiguity this object does not support numeric ops (* or /).
1362# An explicit conversion to a Latency or Frequency must be made first.
1363class Clock(ParamValue):
1364 def __init__(self, value):
1365 self.value = getLatency(value)
1366
1367 def __getattr__(self, attr):
1368 if attr == 'frequency':
1369 return Frequency(self)
1370 if attr in ('latency', 'period'):
1371 return Latency(self)
1372 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1373
1374 def ini_str(self):
1375 return self.period.ini_str()
1376
1377class NetworkBandwidth(float,ParamValue):
1378 def __new__(cls, value):
1379 val = toNetworkBandwidth(value) / 8.0
1380 return super(cls, NetworkBandwidth).__new__(cls, val)
1381
1382 def __str__(self):
1383 return str(self.val)
1384
1385 def ini_str(self):
1386 return '%f' % (ticks_per_sec / float(self))
1387
1388class MemoryBandwidth(float,ParamValue):
1389 def __new__(self, value):
1390 val = toMemoryBandwidth(value)
1391 return super(cls, MemoryBandwidth).__new__(cls, val)
1392
1393 def __str__(self):
1394 return str(self.val)
1395
1396 def ini_str(self):
1397 return '%f' % (ticks_per_sec / float(self))
1398
1399#
1400# "Constants"... handy aliases for various values.
1401#
1402
1403# Some memory range specifications use this as a default upper bound.
1404MaxAddr = Addr.max
1405MaxTick = Tick.max
1406AllMemory = AddrRange(0, MaxAddr)
1407
1408
1409#####################################################################
1410#
1411# Port objects
1412#
1413# Ports are used to interconnect objects in the memory system.
1414#
1415#####################################################################
1416
1417# Port reference: encapsulates a reference to a particular port on a
1418# particular SimObject.
1419class PortRef(object):
1420 def __init__(self, simobj, name, isVec):
1421 assert(isSimObject(simobj))
1422 self.simobj = simobj
1423 self.name = name
1424 self.index = -1
1425 self.isVec = isVec # is this a vector port?
1426 self.peer = None # not associated with another port yet
1427 self.ccConnected = False # C++ port connection done?
1428
1429 # Set peer port reference. Called via __setattr__ as a result of
1430 # a port assignment, e.g., "obj1.port1 = obj2.port2".
1431 def setPeer(self, other):
1432 if self.isVec:
1433 curMap = self.simobj._port_map.get(self.name, [])
1434 self.index = len(curMap)
1435 curMap.append(other)
1436 else:
1437 curMap = self.simobj._port_map.get(self.name)
1438 if curMap and not self.isVec:
1439 print "warning: overwriting port", self.simobj, self.name
1440 curMap = other
1441 self.simobj._port_map[self.name] = curMap
1442 self.peer = other
1443
1444 def clone(self, memo):
1445 newRef = copy.copy(self)
1446 assert(isSimObject(newRef.simobj))
1447 newRef.simobj = newRef.simobj(_memo=memo)
1448 # Tricky: if I'm the *second* PortRef in the pair to be
1449 # cloned, then my peer is still in the middle of its clone
1450 # method, and thus hasn't returned to its owner's
1451 # SimObject.__init__ to get installed in _port_map. As a
1452 # result I have no way of finding the *new* peer object. So I
1453 # mark myself as "waiting" for my peer, and I let the *first*
1454 # PortRef clone call set up both peer pointers after I return.
1455 newPeer = newRef.simobj._port_map.get(self.name)
1456 if newPeer:
1457 if self.isVec:
1458 assert(self.index != -1)
1459 newPeer = newPeer[self.index]
1460 # other guy is all set up except for his peer pointer
1461 assert(newPeer.peer == -1) # peer must be waiting for handshake
1462 newPeer.peer = newRef
1463 newRef.peer = newPeer
1464 else:
1465 # other guy is in clone; just wait for him to do the work
1466 newRef.peer = -1 # mark as waiting for handshake
1467 return newRef
1468
1469 # Call C++ to create corresponding port connection between C++ objects
1470 def ccConnect(self):
1471 if self.ccConnected: # already done this
1472 return
1473 peer = self.peer
1474 cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
1475 peer.simobj.getCCObject(), peer.name, peer.index)
1476 self.ccConnected = True
1477 peer.ccConnected = True
1478
1479# Port description object. Like a ParamDesc object, this represents a
1480# logical port in the SimObject class, not a particular port on a
1481# SimObject instance. The latter are represented by PortRef objects.
1482class Port(object):
1483 def __init__(self, desc):
1484 self.desc = desc
1485 self.isVec = False
1486
1487 # Generate a PortRef for this port on the given SimObject with the
1488 # given name
1489 def makeRef(self, simobj, name):
1490 return PortRef(simobj, name, self.isVec)
1491
1492 # Connect an instance of this port (on the given SimObject with
1493 # the given name) with the port described by the supplied PortRef
1494 def connect(self, simobj, name, ref):
1495 if not isinstance(ref, PortRef):
1496 raise TypeError, \
1497 "assigning non-port reference port '%s'" % name
1498 myRef = self.makeRef(simobj, name)
1499 myRef.setPeer(ref)
1500 ref.setPeer(myRef)
1501
1502# VectorPort description object. Like Port, but represents a vector
1503# of connections (e.g., as on a Bus).
1504class VectorPort(Port):
1505 def __init__(self, desc):
1506 Port.__init__(self, desc)
1507 self.isVec = True
1508
1509#####################################################################
1510
1511# __all__ defines the list of symbols that get exported when
1512# 'from config import *' is invoked. Try to keep this reasonably
1513# short to avoid polluting other namespaces.
1514__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam',
1515 'Parent', 'Self',
1516 'Enum', 'Bool', 'String', 'Float',
1517 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1518 'Int32', 'UInt32', 'Int64', 'UInt64',
1519 'Counter', 'Addr', 'Tick', 'Percent',
1520 'TcpPort', 'UdpPort', 'EthernetAddr',
1521 'MemorySize', 'MemorySize32',
1522 'Latency', 'Frequency', 'RootClock', 'Clock',
1523 'NetworkBandwidth', 'MemoryBandwidth',
1524 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory',
1525 'Null', 'NULL',
1526 'NextEthernetAddr',
1527 'Port', 'VectorPort']
1528