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