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