Merge ktlim@zizzer.eecs.umich.edu:/bk/m5
[gem5.git] / python / m5 / config.py
1 # Copyright (c) 2004 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 from __future__ import generators
28 import os, re, sys, types, inspect
29
30 from m5 import panic
31 from convert import *
32
33 noDot = False
34 try:
35 import pydot
36 except:
37 noDot = True
38
39 class Singleton(type):
40 def __call__(cls, *args, **kwargs):
41 if hasattr(cls, '_instance'):
42 return cls._instance
43
44 cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
45 return cls._instance
46
47 #####################################################################
48 #
49 # M5 Python Configuration Utility
50 #
51 # The basic idea is to write simple Python programs that build Python
52 # objects corresponding to M5 SimObjects for the deisred simulation
53 # configuration. For now, the Python emits a .ini file that can be
54 # parsed by M5. In the future, some tighter integration between M5
55 # and the Python interpreter may allow bypassing the .ini file.
56 #
57 # Each SimObject class in M5 is represented by a Python class with the
58 # same name. The Python inheritance tree mirrors the M5 C++ tree
59 # (e.g., SimpleCPU derives from BaseCPU in both cases, and all
60 # SimObjects inherit from a single SimObject base class). To specify
61 # an instance of an M5 SimObject in a configuration, the user simply
62 # instantiates the corresponding Python object. The parameters for
63 # that SimObject are given by assigning to attributes of the Python
64 # object, either using keyword assignment in the constructor or in
65 # separate assignment statements. For example:
66 #
67 # cache = BaseCache('my_cache', root, size=64*K)
68 # cache.hit_latency = 3
69 # cache.assoc = 8
70 #
71 # (The first two constructor arguments specify the name of the created
72 # cache and its parent node in the hierarchy.)
73 #
74 # The magic lies in the mapping of the Python attributes for SimObject
75 # classes to the actual SimObject parameter specifications. This
76 # allows parameter validity checking in the Python code. Continuing
77 # the example above, the statements "cache.blurfl=3" or
78 # "cache.assoc='hello'" would both result in runtime errors in Python,
79 # since the BaseCache object has no 'blurfl' parameter and the 'assoc'
80 # parameter requires an integer, respectively. This magic is done
81 # primarily by overriding the special __setattr__ method that controls
82 # assignment to object attributes.
83 #
84 # The Python module provides another class, ConfigNode, which is a
85 # superclass of SimObject. ConfigNode implements the parent/child
86 # relationship for building the configuration hierarchy tree.
87 # Concrete instances of ConfigNode can be used to group objects in the
88 # hierarchy, but do not correspond to SimObjects themselves (like a
89 # .ini section with "children=" but no "type=".
90 #
91 # Once a set of Python objects have been instantiated in a hierarchy,
92 # calling 'instantiate(obj)' (where obj is the root of the hierarchy)
93 # will generate a .ini file. See simple-4cpu.py for an example
94 # (corresponding to m5-test/simple-4cpu.ini).
95 #
96 #####################################################################
97
98 #####################################################################
99 #
100 # ConfigNode/SimObject classes
101 #
102 # The Python class hierarchy rooted by ConfigNode (which is the base
103 # class of SimObject, which in turn is the base class of all other M5
104 # SimObject classes) has special attribute behavior. In general, an
105 # object in this hierarchy has three categories of attribute-like
106 # things:
107 #
108 # 1. Regular Python methods and variables. These must start with an
109 # underscore to be treated normally.
110 #
111 # 2. SimObject parameters. These values are stored as normal Python
112 # attributes, but all assignments to these attributes are checked
113 # against the pre-defined set of parameters stored in the class's
114 # _params dictionary. Assignments to attributes that do not
115 # correspond to predefined parameters, or that are not of the correct
116 # type, incur runtime errors.
117 #
118 # 3. Hierarchy children. The child nodes of a ConfigNode are stored
119 # in the node's _children dictionary, but can be accessed using the
120 # Python attribute dot-notation (just as they are printed out by the
121 # simulator). Children cannot be created using attribute assigment;
122 # they must be added by specifying the parent node in the child's
123 # constructor or using the '+=' operator.
124
125 # The SimObject parameters are the most complex, for a few reasons.
126 # First, both parameter descriptions and parameter values are
127 # inherited. Thus parameter description lookup must go up the
128 # inheritance chain like normal attribute lookup, but this behavior
129 # must be explicitly coded since the lookup occurs in each class's
130 # _params attribute. Second, because parameter values can be set
131 # on SimObject classes (to implement default values), the parameter
132 # checking behavior must be enforced on class attribute assignments as
133 # well as instance attribute assignments. Finally, because we allow
134 # class specialization via inheritance (e.g., see the L1Cache class in
135 # the simple-4cpu.py example), we must do parameter checking even on
136 # class instantiation. To provide all these features, we use a
137 # metaclass to define most of the SimObject parameter behavior for
138 # this class hierarchy.
139 #
140 #####################################################################
141
142 class Proxy(object):
143 def __init__(self, path):
144 self._object = None
145 if path == 'any':
146 self._path = None
147 else:
148 # path is a list of (attr,index) tuples
149 self._path = [(path,None)]
150 self._index = None
151 self._multiplier = None
152
153 def __getattr__(self, attr):
154 if attr == '__bases__':
155 return super(Proxy, self).__getattr__(self, attr)
156 self._path.append((attr,None))
157 return self
158
159 def __setattr__(self, attr, value):
160 if not attr.startswith('_'):
161 raise AttributeError, 'cannot set attribute %s' % attr
162 super(Proxy, self).__setattr__(attr, value)
163
164 # support indexing on proxies (e.g., parent.cpu[0])
165 def __getitem__(self, key):
166 if not isinstance(key, int):
167 raise TypeError, "Proxy object requires integer index"
168 if self._path == None:
169 raise IndexError, "Index applied to 'any' proxy"
170 # replace index portion of last path element with new index
171 self._path[-1] = (self._path[-1][0], key)
172 return self
173
174 # support multiplying proxies by constants
175 def __mul__(self, other):
176 if not isinstance(other, int):
177 raise TypeError, "Proxy multiplier must be integer"
178 if self._multiplier == None:
179 self._multiplier = other
180 else:
181 # support chained multipliers
182 self._multiplier *= other
183 return self
184
185 def _mulcheck(self, result):
186 if self._multiplier == None:
187 return result
188 if not isinstance(result, int):
189 raise TypeError, "Proxy with multiplier resolves to " \
190 "non-integer value"
191 return result * self._multiplier
192
193 def unproxy(self, base, ptype):
194 obj = base
195 done = False
196 while not done:
197 if obj is None:
198 raise AttributeError, \
199 'Parent of %s type %s not found at path %s' \
200 % (base.name, ptype, self._path)
201 result, done = obj.find(ptype, self._path)
202 obj = obj.parent
203
204 if isinstance(result, Proxy):
205 result = result.unproxy(obj, ptype)
206
207 return self._mulcheck(result)
208
209 def getindex(obj, index):
210 if index == None:
211 return obj
212 try:
213 obj = obj[index]
214 except TypeError:
215 if index != 0:
216 raise
217 # if index is 0 and item is not subscriptable, just
218 # use item itself (so cpu[0] works on uniprocessors)
219 return obj
220 getindex = staticmethod(getindex)
221
222 class ProxyFactory(object):
223 def __getattr__(self, attr):
224 return Proxy(attr)
225
226 # global object for handling parent.foo proxies
227 parent = ProxyFactory()
228
229 def isSubClass(value, cls):
230 try:
231 return issubclass(value, cls)
232 except:
233 return False
234
235 def isConfigNode(value):
236 try:
237 return issubclass(value, ConfigNode)
238 except:
239 return False
240
241 def isSimObject(value):
242 try:
243 return issubclass(value, SimObject)
244 except:
245 return False
246
247 def isSimObjSequence(value):
248 if not isinstance(value, (list, tuple)):
249 return False
250
251 for val in value:
252 if not isNullPointer(val) and not isConfigNode(val):
253 return False
254
255 return True
256
257 def isParamContext(value):
258 try:
259 return issubclass(value, ParamContext)
260 except:
261 return False
262
263
264 class_decorator = 'M5M5_SIMOBJECT_'
265 expr_decorator = 'M5M5_EXPRESSION_'
266 dot_decorator = '_M5M5_DOT_'
267
268 # 'Global' map of legitimate types for SimObject parameters.
269 param_types = {}
270
271 # Dummy base class to identify types that are legitimate for SimObject
272 # parameters.
273 class ParamType(object):
274 pass
275
276 # Add types defined in given context (dict or module) that are derived
277 # from ParamType to param_types map.
278 def add_param_types(ctx):
279 if isinstance(ctx, types.DictType):
280 source_dict = ctx
281 elif isinstance(ctx, types.ModuleType):
282 source_dict = ctx.__dict__
283 else:
284 raise TypeError, \
285 "m5.config.add_param_types requires dict or module as arg"
286 for key,val in source_dict.iteritems():
287 if isinstance(val, type) and issubclass(val, ParamType):
288 param_types[key] = val
289
290 # The metaclass for ConfigNode (and thus for everything that derives
291 # from ConfigNode, including SimObject). This class controls how new
292 # classes that derive from ConfigNode are instantiated, and provides
293 # inherited class behavior (just like a class controls how instances
294 # of that class are instantiated, and provides inherited instance
295 # behavior).
296 class MetaConfigNode(type):
297 # Attributes that can be set only at initialization time
298 init_keywords = {}
299 # Attributes that can be set any time
300 keywords = { 'check' : types.FunctionType,
301 'children' : types.ListType }
302
303 # __new__ is called before __init__, and is where the statements
304 # in the body of the class definition get loaded into the class's
305 # __dict__. We intercept this to filter out parameter assignments
306 # and only allow "private" attributes to be passed to the base
307 # __new__ (starting with underscore).
308 def __new__(mcls, name, bases, dict):
309 # Copy "private" attributes (including special methods such as __new__)
310 # to the official dict. Everything else goes in _init_dict to be
311 # filtered in __init__.
312 cls_dict = {}
313 for key,val in dict.items():
314 if key.startswith('_'):
315 cls_dict[key] = val
316 del dict[key]
317 cls_dict['_init_dict'] = dict
318 return super(MetaConfigNode, mcls).__new__(mcls, name, bases, cls_dict)
319
320 # initialization
321 def __init__(cls, name, bases, dict):
322 super(MetaConfigNode, cls).__init__(name, bases, dict)
323
324 # initialize required attributes
325 cls._params = {}
326 cls._values = {}
327 cls._param_types = {}
328 cls._bases = [c for c in cls.__mro__ if isConfigNode(c)]
329 cls._anon_subclass_counter = 0
330
331 # If your parent has a value in it that's a config node, clone
332 # it. Do this now so if we update any of the values'
333 # attributes we are updating the clone and not the original.
334 for base in cls._bases:
335 for key,val in base._values.iteritems():
336
337 # don't clone if (1) we're about to overwrite it with
338 # a local setting or (2) we've already cloned a copy
339 # from an earlier (more derived) base
340 if cls._init_dict.has_key(key) or cls._values.has_key(key):
341 continue
342
343 if isConfigNode(val):
344 cls._values[key] = val()
345 elif isSimObjSequence(val):
346 cls._values[key] = [ v() for v in val ]
347 elif isNullPointer(val):
348 cls._values[key] = val
349
350 # process param types from _init_dict, as these may be needed
351 # by param descriptions also in _init_dict
352 for key,val in cls._init_dict.items():
353 if isinstance(val, type) and issubclass(val, ParamType):
354 cls._param_types[key] = val
355 if not issubclass(val, ConfigNode):
356 del cls._init_dict[key]
357
358 # now process remaining _init_dict items
359 for key,val in cls._init_dict.items():
360 # param descriptions
361 if isinstance(val, _Param):
362 cls._params[key] = val
363 # try to resolve local param types in local param_types scope
364 val.maybe_resolve_type(cls._param_types)
365
366 # init-time-only keywords
367 elif cls.init_keywords.has_key(key):
368 cls._set_keyword(key, val, cls.init_keywords[key])
369
370 # See description of decorators in the importer.py file.
371 # We just strip off the expr_decorator now since we don't
372 # need from this point on.
373 elif key.startswith(expr_decorator):
374 key = key[len(expr_decorator):]
375 # because it had dots into a list so that we can find the
376 # proper variable to modify.
377 key = key.split(dot_decorator)
378 c = cls
379 for item in key[:-1]:
380 c = getattr(c, item)
381 setattr(c, key[-1], val)
382
383 # default: use normal path (ends up in __setattr__)
384 else:
385 setattr(cls, key, val)
386
387
388 def _isvalue(cls, name):
389 for c in cls._bases:
390 if c._params.has_key(name):
391 return True
392
393 for c in cls._bases:
394 if c._values.has_key(name):
395 return True
396
397 return False
398
399 # generator that iterates across all parameters for this class and
400 # all classes it inherits from
401 def _getparams(cls):
402 params = {}
403 for c in cls._bases:
404 for p,v in c._params.iteritems():
405 if not params.has_key(p):
406 params[p] = v
407 return params
408
409 # Lookup a parameter description by name in the given class.
410 def _getparam(cls, name, default = AttributeError):
411 for c in cls._bases:
412 if c._params.has_key(name):
413 return c._params[name]
414 if isSubClass(default, Exception):
415 raise default, \
416 "object '%s' has no attribute '%s'" % (cls.__name__, name)
417 else:
418 return default
419
420 def _hasvalue(cls, name):
421 for c in cls._bases:
422 if c._values.has_key(name):
423 return True
424
425 return False
426
427 def _getvalues(cls):
428 values = {}
429 for i,c in enumerate(cls._bases):
430 for p,v in c._values.iteritems():
431 if not values.has_key(p):
432 values[p] = v
433 for p,v in c._params.iteritems():
434 if not values.has_key(p) and hasattr(v, 'default'):
435 try:
436 v.valid(v.default)
437 except TypeError:
438 panic("Invalid default %s for param %s in node %s"
439 % (v.default,p,cls.__name__))
440 v = v.default
441 cls._setvalue(p, v)
442 values[p] = v
443
444 return values
445
446 def _getvalue(cls, name, default = AttributeError):
447 value = None
448 for c in cls._bases:
449 if c._values.has_key(name):
450 value = c._values[name]
451 break
452 if value is not None:
453 return value
454
455 param = cls._getparam(name, None)
456 if param is not None and hasattr(param, 'default'):
457 param.valid(param.default)
458 value = param.default
459 cls._setvalue(name, value)
460 return value
461
462 if isSubClass(default, Exception):
463 raise default, 'value for %s not found' % name
464 else:
465 return default
466
467 def _setvalue(cls, name, value):
468 cls._values[name] = value
469
470 def __getattr__(cls, attr):
471 if cls._isvalue(attr):
472 return Value(cls, attr)
473
474 if attr == '_cpp_param_decl' and hasattr(cls, 'type'):
475 return cls.type + '*'
476
477 raise AttributeError, \
478 "object '%s' has no attribute '%s'" % (cls.__name__, attr)
479
480 def _set_keyword(cls, keyword, val, kwtype):
481 if not isinstance(val, kwtype):
482 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
483 (keyword, type(val), kwtype)
484 if isinstance(val, types.FunctionType):
485 val = classmethod(val)
486 type.__setattr__(cls, keyword, val)
487
488 # Set attribute (called on foo.attr = value when foo is an
489 # instance of class cls).
490 def __setattr__(cls, attr, value):
491 # normal processing for private attributes
492 if attr.startswith('_'):
493 type.__setattr__(cls, attr, value)
494 return
495
496 if cls.keywords.has_key(attr):
497 cls._set_keyword(attr, value, cls.keywords[attr])
498 return
499
500 # must be SimObject param
501 param = cls._getparam(attr, None)
502 if param:
503 # It's ok: set attribute by delegating to 'object' class.
504 # Note the use of param.make_value() to verify/canonicalize
505 # the assigned value
506 try:
507 param.valid(value)
508 except:
509 panic("Error setting param %s.%s to %s\n" % \
510 (cls.__name__, attr, value))
511 cls._setvalue(attr, value)
512 elif isConfigNode(value) or isSimObjSequence(value):
513 cls._setvalue(attr, value)
514 else:
515 raise AttributeError, \
516 "Class %s has no parameter %s" % (cls.__name__, attr)
517
518 def add_child(cls, instance, name, child):
519 if isNullPointer(child) or instance.top_child_names.has_key(name):
520 return
521
522 if isinstance(child, (list, tuple)):
523 kid = []
524 for i,c in enumerate(child):
525 n = '%s%d' % (name, i)
526 k = c.instantiate(n, instance)
527
528 instance.children.append(k)
529 instance.child_names[n] = k
530 instance.child_objects[c] = k
531 kid.append(k)
532 else:
533 kid = child.instantiate(name, instance)
534 instance.children.append(kid)
535 instance.child_names[name] = kid
536 instance.child_objects[child] = kid
537
538 instance.top_child_names[name] = kid
539
540 # Print instance info to .ini file.
541 def instantiate(cls, name, parent = None):
542 instance = Node(name, cls, parent, isParamContext(cls))
543
544 if hasattr(cls, 'check'):
545 cls.check()
546
547 for key,value in cls._getvalues().iteritems():
548 if isConfigNode(value):
549 cls.add_child(instance, key, value)
550 if isinstance(value, (list, tuple)):
551 vals = [ v for v in value if isConfigNode(v) ]
552 if len(vals):
553 cls.add_child(instance, key, vals)
554
555 for pname,param in cls._getparams().iteritems():
556 try:
557 value = cls._getvalue(pname)
558 except:
559 panic('Error getting %s' % pname)
560
561 try:
562 if isConfigNode(value):
563 value = instance.child_objects[value]
564 elif isinstance(value, (list, tuple)):
565 v = []
566 for val in value:
567 if isConfigNode(val):
568 v.append(instance.child_objects[val])
569 else:
570 v.append(val)
571 value = v
572
573 p = NodeParam(pname, param, value)
574 instance.params.append(p)
575 instance.param_names[pname] = p
576 except:
577 print 'Exception while evaluating %s.%s' % \
578 (instance.path, pname)
579 raise
580
581 return instance
582
583 def _convert(cls, value):
584 realvalue = value
585 if isinstance(value, Node):
586 realvalue = value.realtype
587
588 if isinstance(realvalue, Proxy):
589 return value
590
591 if realvalue == None or isNullPointer(realvalue):
592 return value
593
594 if isSubClass(realvalue, cls):
595 return value
596
597 raise TypeError, 'object %s type %s wrong type, should be %s' % \
598 (repr(realvalue), realvalue, cls)
599
600 def _string(cls, value):
601 if isNullPointer(value):
602 return 'Null'
603 return Node._string(value)
604
605 # The ConfigNode class is the root of the special hierarchy. Most of
606 # the code in this class deals with the configuration hierarchy itself
607 # (parent/child node relationships).
608 class ConfigNode(object):
609 # Specify metaclass. Any class inheriting from ConfigNode will
610 # get this metaclass.
611 __metaclass__ = MetaConfigNode
612
613 def __new__(cls, **kwargs):
614 name = cls.__name__ + ("_%d" % cls._anon_subclass_counter)
615 cls._anon_subclass_counter += 1
616 return cls.__metaclass__(name, (cls, ), kwargs)
617
618 class ParamContext(ConfigNode):
619 pass
620
621 class MetaSimObject(MetaConfigNode):
622 # init_keywords and keywords are inherited from MetaConfigNode,
623 # with overrides/additions
624 init_keywords = MetaConfigNode.init_keywords
625 init_keywords.update({ 'abstract' : types.BooleanType,
626 'type' : types.StringType })
627
628 keywords = MetaConfigNode.keywords
629 # no additional keywords
630
631 cpp_classes = []
632
633 # initialization
634 def __init__(cls, name, bases, dict):
635 super(MetaSimObject, cls).__init__(name, bases, dict)
636
637 if hasattr(cls, 'type'):
638 if name == 'SimObject':
639 cls._cpp_base = None
640 elif hasattr(cls._bases[1], 'type'):
641 cls._cpp_base = cls._bases[1].type
642 else:
643 panic("SimObject %s derives from a non-C++ SimObject %s "\
644 "(no 'type')" % (cls, cls_bases[1].__name__))
645
646 # This class corresponds to a C++ class: put it on the global
647 # list of C++ objects to generate param structs, etc.
648 MetaSimObject.cpp_classes.append(cls)
649
650 def _cpp_decl(cls):
651 name = cls.__name__
652 code = ""
653 code += "\n".join([e.cpp_declare() for e in cls._param_types.values()])
654 code += "\n"
655 param_names = cls._params.keys()
656 param_names.sort()
657 code += "struct Params"
658 if cls._cpp_base:
659 code += " : public %s::Params" % cls._cpp_base
660 code += " {\n "
661 code += "\n ".join([cls._params[pname].cpp_decl(pname) \
662 for pname in param_names])
663 code += "\n};\n"
664 return code
665
666 class NodeParam(object):
667 def __init__(self, name, param, value):
668 self.name = name
669 self.param = param
670 self.ptype = param.ptype
671 self.convert = param.convert
672 self.string = param.string
673 self.value = value
674
675 class Node(object):
676 all = {}
677 def __init__(self, name, realtype, parent, paramcontext):
678 self.name = name
679 self.realtype = realtype
680 if isSimObject(realtype):
681 self.type = realtype.type
682 else:
683 self.type = None
684 self.parent = parent
685 self.children = []
686 self.child_names = {}
687 self.child_objects = {}
688 self.top_child_names = {}
689 self.params = []
690 self.param_names = {}
691 self.paramcontext = paramcontext
692
693 path = [ self.name ]
694 node = self.parent
695 while node is not None:
696 if node.name != 'root':
697 path.insert(0, node.name)
698 else:
699 assert(node.parent is None)
700 node = node.parent
701 self.path = '.'.join(path)
702
703 def find(self, realtype, path):
704 if not path:
705 if issubclass(self.realtype, realtype):
706 return self, True
707
708 obj = None
709 for child in self.children:
710 if issubclass(child.realtype, realtype):
711 if obj is not None:
712 raise AttributeError, \
713 'parent.any matched more than one: %s %s' % \
714 (obj.path, child.path)
715 obj = child
716 return obj, obj is not None
717
718 try:
719 obj = self
720 for (node,index) in path[:-1]:
721 if obj.child_names.has_key(node):
722 obj = obj.child_names[node]
723 else:
724 obj = obj.top_child_names[node]
725 obj = Proxy.getindex(obj, index)
726
727 (last,index) = path[-1]
728 if obj.child_names.has_key(last):
729 value = obj.child_names[last]
730 return Proxy.getindex(value, index), True
731 elif obj.top_child_names.has_key(last):
732 value = obj.top_child_names[last]
733 return Proxy.getindex(value, index), True
734 elif obj.param_names.has_key(last):
735 value = obj.param_names[last]
736 realtype._convert(value.value)
737 return Proxy.getindex(value.value, index), True
738 except KeyError:
739 pass
740
741 return None, False
742
743 def unproxy(self, param, ptype):
744 if not isinstance(param, Proxy):
745 return param
746 return param.unproxy(self, ptype)
747
748 def fixup(self):
749 self.all[self.path] = self
750
751 for param in self.params:
752 ptype = param.ptype
753 pval = param.value
754
755 try:
756 if isinstance(pval, (list, tuple)):
757 param.value = [ self.unproxy(pv, ptype) for pv in pval ]
758 else:
759 param.value = self.unproxy(pval, ptype)
760 except:
761 print 'Error while fixing up %s:%s' % (self.path, param.name)
762 raise
763
764 for child in self.children:
765 assert(child != self)
766 child.fixup()
767
768 # print type and parameter values to .ini file
769 def display(self):
770 print '[' + self.path + ']' # .ini section header
771
772 if isSimObject(self.realtype):
773 print 'type = %s' % self.type
774
775 if self.children:
776 # instantiate children in same order they were added for
777 # backward compatibility (else we can end up with cpu1
778 # before cpu0). Changing ordering can also influence timing
779 # in the current memory system, as caches get added to a bus
780 # in different orders which affects their priority in the
781 # case of simulataneous requests. We should uncomment the
782 # following line once we take care of that issue.
783 # self.children.sort(lambda x,y: cmp(x.name, y.name))
784 children = [ c.name for c in self.children if not c.paramcontext]
785 print 'children =', ' '.join(children)
786
787 self.params.sort(lambda x,y: cmp(x.name, y.name))
788 for param in self.params:
789 try:
790 if param.value is None:
791 raise AttributeError, 'Parameter with no value'
792
793 value = param.convert(param.value)
794 string = param.string(value)
795 except:
796 print 'exception in %s:%s' % (self.path, param.name)
797 raise
798
799 print '%s = %s' % (param.name, string)
800
801 print
802
803 # recursively dump out children
804 for c in self.children:
805 c.display()
806
807 # print type and parameter values to .ini file
808 def outputDot(self, dot):
809
810
811 label = "{%s|" % self.path
812 if isSimObject(self.realtype):
813 label += '%s|' % self.type
814
815 if self.children:
816 # instantiate children in same order they were added for
817 # backward compatibility (else we can end up with cpu1
818 # before cpu0).
819 for c in self.children:
820 dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
821
822 simobjs = []
823 for param in self.params:
824 try:
825 if param.value is None:
826 raise AttributeError, 'Parameter with no value'
827
828 value = param.convert(param.value)
829 string = param.string(value)
830 except:
831 print 'exception in %s:%s' % (self.name, param.name)
832 raise
833 if isConfigNode(param.ptype) and string != "Null":
834 simobjs.append(string)
835 else:
836 label += '%s = %s\\n' % (param.name, string)
837
838 for so in simobjs:
839 label += "|<%s> %s" % (so, so)
840 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, tailport="w"))
841 label += '}'
842 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
843
844 # recursively dump out children
845 for c in self.children:
846 c.outputDot(dot)
847
848 def _string(cls, value):
849 if not isinstance(value, Node):
850 raise AttributeError, 'expecting %s got %s' % (Node, value)
851 return value.path
852 _string = classmethod(_string)
853
854 #####################################################################
855 #
856 # Parameter description classes
857 #
858 # The _params dictionary in each class maps parameter names to
859 # either a Param or a VectorParam object. These objects contain the
860 # parameter description string, the parameter type, and the default
861 # value (loaded from the PARAM section of the .odesc files). The
862 # _convert() method on these objects is used to force whatever value
863 # is assigned to the parameter to the appropriate type.
864 #
865 # Note that the default values are loaded into the class's attribute
866 # space when the parameter dictionary is initialized (in
867 # MetaConfigNode._setparams()); after that point they aren't used.
868 #
869 #####################################################################
870
871 def isNullPointer(value):
872 return isinstance(value, NullSimObject)
873
874 class Value(object):
875 def __init__(self, obj, attr):
876 super(Value, self).__setattr__('attr', attr)
877 super(Value, self).__setattr__('obj', obj)
878
879 def _getattr(self):
880 return self.obj._getvalue(self.attr)
881
882 def __setattr__(self, attr, value):
883 setattr(self._getattr(), attr, value)
884
885 def __getattr__(self, attr):
886 return getattr(self._getattr(), attr)
887
888 def __getitem__(self, index):
889 return self._getattr().__getitem__(index)
890
891 def __call__(self, *args, **kwargs):
892 return self._getattr().__call__(*args, **kwargs)
893
894 def __nonzero__(self):
895 return bool(self._getattr())
896
897 def __str__(self):
898 return str(self._getattr())
899
900 def __len__(self):
901 return len(self._getattr())
902
903 # Regular parameter.
904 class _Param(object):
905 def __init__(self, ptype, *args, **kwargs):
906 if isinstance(ptype, types.StringType):
907 self.ptype_string = ptype
908 elif isinstance(ptype, type):
909 self.ptype = ptype
910 else:
911 raise TypeError, "Param type is not a type (%s)" % ptype
912
913 if args:
914 if len(args) == 1:
915 self.desc = args[0]
916 elif len(args) == 2:
917 self.default = args[0]
918 self.desc = args[1]
919 else:
920 raise TypeError, 'too many arguments'
921
922 if kwargs.has_key('desc'):
923 assert(not hasattr(self, 'desc'))
924 self.desc = kwargs['desc']
925 del kwargs['desc']
926
927 if kwargs.has_key('default'):
928 assert(not hasattr(self, 'default'))
929 self.default = kwargs['default']
930 del kwargs['default']
931
932 if kwargs:
933 raise TypeError, 'extra unknown kwargs %s' % kwargs
934
935 if not hasattr(self, 'desc'):
936 raise TypeError, 'desc attribute missing'
937
938 def maybe_resolve_type(self, context):
939 # check if already resolved... don't use hasattr(),
940 # as that calls __getattr__()
941 if self.__dict__.has_key('ptype'):
942 return
943 try:
944 self.ptype = context[self.ptype_string]
945 except KeyError:
946 # no harm in trying... we'll try again later using global scope
947 pass
948
949 def __getattr__(self, attr):
950 if attr == 'ptype':
951 try:
952 self.ptype = param_types[self.ptype_string]
953 return self.ptype
954 except:
955 panic("undefined Param type %s" % self.ptype_string)
956 else:
957 raise AttributeError, "'%s' object has no attribute '%s'" % \
958 (type(self).__name__, attr)
959
960 def valid(self, value):
961 if not isinstance(value, Proxy):
962 self.ptype._convert(value)
963
964 def convert(self, value):
965 return self.ptype._convert(value)
966
967 def string(self, value):
968 return self.ptype._string(value)
969
970 def set(self, name, instance, value):
971 instance.__dict__[name] = value
972
973 def cpp_decl(self, name):
974 return '%s %s;' % (self.ptype._cpp_param_decl, name)
975
976 class _ParamProxy(object):
977 def __init__(self, type):
978 self.ptype = type
979
980 # E.g., Param.Int(5, "number of widgets")
981 def __call__(self, *args, **kwargs):
982 return _Param(self.ptype, *args, **kwargs)
983
984 # Strange magic to theoretically allow dotted names as Param classes,
985 # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
986 def __getattr__(self, attr):
987 if attr == '__bases__':
988 raise AttributeError, ''
989 cls = type(self)
990 return cls(attr)
991
992 def __setattr__(self, attr, value):
993 if attr != 'ptype':
994 raise AttributeError, \
995 'Attribute %s not available in %s' % (attr, self.__class__)
996 super(_ParamProxy, self).__setattr__(attr, value)
997
998
999 Param = _ParamProxy(None)
1000
1001 # Vector-valued parameter description. Just like Param, except that
1002 # the value is a vector (list) of the specified type instead of a
1003 # single value.
1004 class _VectorParam(_Param):
1005 def __init__(self, type, *args, **kwargs):
1006 _Param.__init__(self, type, *args, **kwargs)
1007
1008 def valid(self, value):
1009 if value == None:
1010 return True
1011
1012 if isinstance(value, (list, tuple)):
1013 for val in value:
1014 if not isinstance(val, Proxy):
1015 self.ptype._convert(val)
1016 elif not isinstance(value, Proxy):
1017 self.ptype._convert(value)
1018
1019 # Convert assigned value to appropriate type. If the RHS is not a
1020 # list or tuple, it generates a single-element list.
1021 def convert(self, value):
1022 if value == None:
1023 return []
1024
1025 if isinstance(value, (list, tuple)):
1026 # list: coerce each element into new list
1027 return [ self.ptype._convert(v) for v in value ]
1028 else:
1029 # singleton: coerce & wrap in a list
1030 return self.ptype._convert(value)
1031
1032 def string(self, value):
1033 if isinstance(value, (list, tuple)):
1034 return ' '.join([ self.ptype._string(v) for v in value])
1035 else:
1036 return self.ptype._string(value)
1037
1038 def cpp_decl(self, name):
1039 return 'std::vector<%s> %s;' % (self.ptype._cpp_param_decl, name)
1040
1041 class _VectorParamProxy(_ParamProxy):
1042 # E.g., VectorParam.Int(5, "number of widgets")
1043 def __call__(self, *args, **kwargs):
1044 return _VectorParam(self.ptype, *args, **kwargs)
1045
1046 VectorParam = _VectorParamProxy(None)
1047
1048 #####################################################################
1049 #
1050 # Parameter Types
1051 #
1052 # Though native Python types could be used to specify parameter types
1053 # (the 'ptype' field of the Param and VectorParam classes), it's more
1054 # flexible to define our own set of types. This gives us more control
1055 # over how Python expressions are converted to values (via the
1056 # __init__() constructor) and how these values are printed out (via
1057 # the __str__() conversion method). Eventually we'll need these types
1058 # to correspond to distinct C++ types as well.
1059 #
1060 #####################################################################
1061
1062
1063 # Metaclass for bounds-checked integer parameters. See CheckedInt.
1064 class CheckedIntType(type):
1065 def __init__(cls, name, bases, dict):
1066 super(CheckedIntType, cls).__init__(name, bases, dict)
1067
1068 # CheckedInt is an abstract base class, so we actually don't
1069 # want to do any processing on it... the rest of this code is
1070 # just for classes that derive from CheckedInt.
1071 if name == 'CheckedInt':
1072 return
1073
1074 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1075 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1076 panic("CheckedInt subclass %s must define either\n" \
1077 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1078 % name);
1079 if cls.unsigned:
1080 cls.min = 0
1081 cls.max = 2 ** cls.size - 1
1082 else:
1083 cls.min = -(2 ** (cls.size - 1))
1084 cls.max = (2 ** (cls.size - 1)) - 1
1085
1086 cls._cpp_param_decl = cls.cppname
1087
1088 def _convert(cls, value):
1089 if isinstance(value, bool):
1090 return int(value)
1091
1092 if not isinstance(value, (int, long, float, str)):
1093 raise TypeError, 'Integer param of invalid type %s' % type(value)
1094
1095 if isinstance(value, (str, float)):
1096 value = long(float(value))
1097
1098 if not cls.min <= value <= cls.max:
1099 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1100 (cls.min, value, cls.max)
1101
1102 return value
1103
1104 def _string(cls, value):
1105 return str(value)
1106
1107 # Abstract superclass for bounds-checked integer parameters. This
1108 # class is subclassed to generate parameter classes with specific
1109 # bounds. Initialization of the min and max bounds is done in the
1110 # metaclass CheckedIntType.__init__.
1111 class CheckedInt(ParamType):
1112 __metaclass__ = CheckedIntType
1113
1114 class Int(CheckedInt): cppname = 'int'; size = 32; unsigned = False
1115 class Unsigned(CheckedInt): cppname = 'unsigned'; size = 32; unsigned = True
1116
1117 class Int8(CheckedInt): cppname = 'int8_t'; size = 8; unsigned = False
1118 class UInt8(CheckedInt): cppname = 'uint8_t'; size = 8; unsigned = True
1119 class Int16(CheckedInt): cppname = 'int16_t'; size = 16; unsigned = False
1120 class UInt16(CheckedInt): cppname = 'uint16_t'; size = 16; unsigned = True
1121 class Int32(CheckedInt): cppname = 'int32_t'; size = 32; unsigned = False
1122 class UInt32(CheckedInt): cppname = 'uint32_t'; size = 32; unsigned = True
1123 class Int64(CheckedInt): cppname = 'int64_t'; size = 64; unsigned = False
1124 class UInt64(CheckedInt): cppname = 'uint64_t'; size = 64; unsigned = True
1125
1126 class Counter(CheckedInt): cppname = 'Counter'; size = 64; unsigned = True
1127 class Addr(CheckedInt): cppname = 'Addr'; size = 64; unsigned = True
1128 class Tick(CheckedInt): cppname = 'Tick'; size = 64; unsigned = True
1129
1130 class Percent(CheckedInt): cppname = 'int'; min = 0; max = 100
1131
1132 class Pair(object):
1133 def __init__(self, first, second):
1134 self.first = first
1135 self.second = second
1136
1137 class MetaRange(type):
1138 def __init__(cls, name, bases, dict):
1139 super(MetaRange, cls).__init__(name, bases, dict)
1140 if name == 'Range':
1141 return
1142 cls._cpp_param_decl = 'Range<%s>' % cls.type._cpp_param_decl
1143
1144 def _convert(cls, value):
1145 if not isinstance(value, Pair):
1146 raise TypeError, 'value %s is not a Pair' % value
1147 return Pair(cls.type._convert(value.first),
1148 cls.type._convert(value.second))
1149
1150 def _string(cls, value):
1151 return '%s:%s' % (cls.type._string(value.first),
1152 cls.type._string(value.second))
1153
1154 class Range(ParamType):
1155 __metaclass__ = MetaRange
1156
1157 def RangeSize(start, size):
1158 return Pair(start, start + size - 1)
1159
1160 class AddrRange(Range): type = Addr
1161
1162 # Boolean parameter type.
1163 class Bool(ParamType):
1164 _cpp_param_decl = 'bool'
1165 def _convert(value):
1166 t = type(value)
1167 if t == bool:
1168 return value
1169
1170 if t == int or t == long:
1171 return bool(value)
1172
1173 if t == str:
1174 v = value.lower()
1175 if v == "true" or v == "t" or v == "yes" or v == "y":
1176 return True
1177 elif v == "false" or v == "f" or v == "no" or v == "n":
1178 return False
1179
1180 raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v, t)
1181 _convert = staticmethod(_convert)
1182
1183 def _string(value):
1184 if value:
1185 return "true"
1186 else:
1187 return "false"
1188 _string = staticmethod(_string)
1189
1190 # String-valued parameter.
1191 class String(ParamType):
1192 _cpp_param_decl = 'string'
1193
1194 # Constructor. Value must be Python string.
1195 def _convert(cls,value):
1196 if value is None:
1197 return ''
1198 if isinstance(value, str):
1199 return value
1200
1201 raise TypeError, \
1202 "String param got value %s %s" % (repr(value), type(value))
1203 _convert = classmethod(_convert)
1204
1205 # Generate printable string version. Not too tricky.
1206 def _string(cls, value):
1207 return value
1208 _string = classmethod(_string)
1209
1210
1211 def IncEthernetAddr(addr, val = 1):
1212 bytes = map(lambda x: int(x, 16), addr.split(':'))
1213 bytes[5] += val
1214 for i in (5, 4, 3, 2, 1):
1215 val,rem = divmod(bytes[i], 256)
1216 bytes[i] = rem
1217 if val == 0:
1218 break
1219 bytes[i - 1] += val
1220 assert(bytes[0] <= 255)
1221 return ':'.join(map(lambda x: '%02x' % x, bytes))
1222
1223 class NextEthernetAddr(object):
1224 __metaclass__ = Singleton
1225 addr = "00:90:00:00:00:01"
1226
1227 def __init__(self, inc = 1):
1228 self.value = self.addr
1229 self.addr = IncEthernetAddr(self.addr, inc)
1230
1231 class EthernetAddr(ParamType):
1232 _cpp_param_decl = 'EthAddr'
1233
1234 def _convert(cls, value):
1235 if value == NextEthernetAddr:
1236 return value
1237
1238 if not isinstance(value, str):
1239 raise TypeError, "expected an ethernet address and didn't get one"
1240
1241 bytes = value.split(':')
1242 if len(bytes) != 6:
1243 raise TypeError, 'invalid ethernet address %s' % value
1244
1245 for byte in bytes:
1246 if not 0 <= int(byte) <= 256:
1247 raise TypeError, 'invalid ethernet address %s' % value
1248
1249 return value
1250 _convert = classmethod(_convert)
1251
1252 def _string(cls, value):
1253 if value == NextEthernetAddr:
1254 value = value().value
1255 return value
1256 _string = classmethod(_string)
1257
1258 # Special class for NULL pointers. Note the special check in
1259 # make_param_value() above that lets these be assigned where a
1260 # SimObject is required.
1261 # only one copy of a particular node
1262 class NullSimObject(object):
1263 __metaclass__ = Singleton
1264
1265 def __call__(cls):
1266 return cls
1267
1268 def _instantiate(self, parent = None, path = ''):
1269 pass
1270
1271 def _convert(cls, value):
1272 if value == Nxone:
1273 return
1274
1275 if isinstance(value, cls):
1276 return value
1277
1278 raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1279 (repr(value), type(value), cls)
1280 _convert = classmethod(_convert)
1281
1282 def _string():
1283 return 'NULL'
1284 _string = staticmethod(_string)
1285
1286 # The only instance you'll ever need...
1287 Null = NULL = NullSimObject()
1288
1289 # Enumerated types are a little more complex. The user specifies the
1290 # type as Enum(foo) where foo is either a list or dictionary of
1291 # alternatives (typically strings, but not necessarily so). (In the
1292 # long run, the integer value of the parameter will be the list index
1293 # or the corresponding dictionary value. For now, since we only check
1294 # that the alternative is valid and then spit it into a .ini file,
1295 # there's not much point in using the dictionary.)
1296
1297 # What Enum() must do is generate a new type encapsulating the
1298 # provided list/dictionary so that specific values of the parameter
1299 # can be instances of that type. We define two hidden internal
1300 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1301 # derive the new type from the appropriate base class on the fly.
1302
1303
1304 # Metaclass for Enum types
1305 class MetaEnum(type):
1306
1307 def __init__(cls, name, bases, init_dict):
1308 if init_dict.has_key('map'):
1309 if not isinstance(cls.map, dict):
1310 raise TypeError, "Enum-derived class attribute 'map' " \
1311 "must be of type dict"
1312 # build list of value strings from map
1313 cls.vals = cls.map.keys()
1314 cls.vals.sort()
1315 elif init_dict.has_key('vals'):
1316 if not isinstance(cls.vals, list):
1317 raise TypeError, "Enum-derived class attribute 'vals' " \
1318 "must be of type list"
1319 # build string->value map from vals sequence
1320 cls.map = {}
1321 for idx,val in enumerate(cls.vals):
1322 cls.map[val] = idx
1323 else:
1324 raise TypeError, "Enum-derived class must define "\
1325 "attribute 'map' or 'vals'"
1326
1327 cls._cpp_param_decl = name
1328
1329 super(MetaEnum, cls).__init__(name, bases, init_dict)
1330
1331 def cpp_declare(cls):
1332 s = 'enum %s {\n ' % cls.__name__
1333 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1334 s += '\n};\n'
1335 return s
1336
1337 # Base class for enum types.
1338 class Enum(ParamType):
1339 __metaclass__ = MetaEnum
1340 vals = []
1341
1342 def _convert(self, value):
1343 if value not in self.map:
1344 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1345 % (value, self.vals)
1346 return value
1347 _convert = classmethod(_convert)
1348
1349 # Generate printable string version of value.
1350 def _string(self, value):
1351 return str(value)
1352 _string = classmethod(_string)
1353 #
1354 # "Constants"... handy aliases for various values.
1355 #
1356
1357 # Some memory range specifications use this as a default upper bound.
1358 MAX_ADDR = Addr.max
1359 MaxTick = Tick.max
1360
1361 # For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1362 K = 1024
1363 M = K*K
1364 G = K*M
1365
1366 #####################################################################
1367
1368 # The final hook to generate .ini files. Called from configuration
1369 # script once config is built.
1370 def instantiate(root):
1371 instance = root.instantiate('root')
1372 instance.fixup()
1373 instance.display()
1374 if not noDot:
1375 dot = pydot.Dot()
1376 instance.outputDot(dot)
1377 dot.orientation = "portrait"
1378 dot.size = "8.5,11"
1379 dot.ranksep="equally"
1380 dot.rank="samerank"
1381 dot.write("config.dot")
1382 dot.write_ps("config.ps")
1383
1384 # SimObject is a minimal extension of ConfigNode, implementing a
1385 # hierarchy node that corresponds to an M5 SimObject. It prints out a
1386 # "type=" line to indicate its SimObject class, prints out the
1387 # assigned parameters corresponding to its class, and allows
1388 # parameters to be set by keyword in the constructor. Note that most
1389 # of the heavy lifting for the SimObject param handling is done in the
1390 # MetaConfigNode metaclass.
1391 class SimObject(ConfigNode, ParamType):
1392 __metaclass__ = MetaSimObject
1393 type = 'SimObject'
1394
1395
1396 # __all__ defines the list of symbols that get exported when
1397 # 'from config import *' is invoked. Try to keep this reasonably
1398 # short to avoid polluting other namespaces.
1399 __all__ = ['ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam',
1400 'parent', 'Enum',
1401 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1402 'Int32', 'UInt32', 'Int64', 'UInt64',
1403 'Counter', 'Addr', 'Tick', 'Percent',
1404 'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M',
1405 'NextEthernetAddr',
1406 'instantiate']