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