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