config.py revision 2901:f9a45473ab55
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 count += self._ccObject.drain(drain_event) 551 if recursive: 552 for child in self._children.itervalues(): 553 count += child.startDrain(drain_event, True) 554 return count 555 556 def resume(self): 557 if isinstance(self, SimObject) and not isinstance(self, ParamContext): 558 self._ccObject.resume() 559 for child in self._children.itervalues(): 560 child.resume() 561 562 def changeTiming(self, mode): 563 if isinstance(self, System): 564 self._ccObject.setMemoryMode(mode) 565 for child in self._children.itervalues(): 566 child.changeTiming(mode) 567 568 def takeOverFrom(self, old_cpu): 569 cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject) 570 self._ccObject.takeOverFrom(cpu_ptr) 571 572 # generate output file for 'dot' to display as a pretty graph. 573 # this code is currently broken. 574 def outputDot(self, dot): 575 label = "{%s|" % self.path 576 if isSimObject(self.realtype): 577 label += '%s|' % self.type 578 579 if self.children: 580 # instantiate children in same order they were added for 581 # backward compatibility (else we can end up with cpu1 582 # before cpu0). 583 for c in self.children: 584 dot.add_edge(pydot.Edge(self.path,c.path, style="bold")) 585 586 simobjs = [] 587 for param in self.params: 588 try: 589 if param.value is None: 590 raise AttributeError, 'Parameter with no value' 591 592 value = param.value 593 string = param.string(value) 594 except Exception, e: 595 msg = 'exception in %s:%s\n%s' % (self.name, param.name, e) 596 e.args = (msg, ) 597 raise 598 599 if isSimObject(param.ptype) and string != "Null": 600 simobjs.append(string) 601 else: 602 label += '%s = %s\\n' % (param.name, string) 603 604 for so in simobjs: 605 label += "|<%s> %s" % (so, so) 606 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, 607 tailport="w")) 608 label += '}' 609 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label)) 610 611 # recursively dump out children 612 for c in self.children: 613 c.outputDot(dot) 614 615class ParamContext(SimObject): 616 pass 617 618##################################################################### 619# 620# Proxy object support. 621# 622##################################################################### 623 624class BaseProxy(object): 625 def __init__(self, search_self, search_up): 626 self._search_self = search_self 627 self._search_up = search_up 628 self._multiplier = None 629 630 def __setattr__(self, attr, value): 631 if not attr.startswith('_'): 632 raise AttributeError, 'cannot set attribute on proxy object' 633 super(BaseProxy, self).__setattr__(attr, value) 634 635 # support multiplying proxies by constants 636 def __mul__(self, other): 637 if not isinstance(other, (int, long, float)): 638 raise TypeError, "Proxy multiplier must be integer" 639 if self._multiplier == None: 640 self._multiplier = other 641 else: 642 # support chained multipliers 643 self._multiplier *= other 644 return self 645 646 __rmul__ = __mul__ 647 648 def _mulcheck(self, result): 649 if self._multiplier == None: 650 return result 651 return result * self._multiplier 652 653 def unproxy(self, base): 654 obj = base 655 done = False 656 657 if self._search_self: 658 result, done = self.find(obj) 659 660 if self._search_up: 661 while not done: 662 obj = obj._parent 663 if not obj: 664 break 665 result, done = self.find(obj) 666 667 if not done: 668 raise AttributeError, "Can't resolve proxy '%s' from '%s'" % \ 669 (self.path(), base.path()) 670 671 if isinstance(result, BaseProxy): 672 if result == self: 673 raise RuntimeError, "Cycle in unproxy" 674 result = result.unproxy(obj) 675 676 return self._mulcheck(result) 677 678 def getindex(obj, index): 679 if index == None: 680 return obj 681 try: 682 obj = obj[index] 683 except TypeError: 684 if index != 0: 685 raise 686 # if index is 0 and item is not subscriptable, just 687 # use item itself (so cpu[0] works on uniprocessors) 688 return obj 689 getindex = staticmethod(getindex) 690 691 def set_param_desc(self, pdesc): 692 self._pdesc = pdesc 693 694class AttrProxy(BaseProxy): 695 def __init__(self, search_self, search_up, attr): 696 super(AttrProxy, self).__init__(search_self, search_up) 697 self._attr = attr 698 self._modifiers = [] 699 700 def __getattr__(self, attr): 701 # python uses __bases__ internally for inheritance 702 if attr.startswith('_'): 703 return super(AttrProxy, self).__getattr__(self, attr) 704 if hasattr(self, '_pdesc'): 705 raise AttributeError, "Attribute reference on bound proxy" 706 self._modifiers.append(attr) 707 return self 708 709 # support indexing on proxies (e.g., Self.cpu[0]) 710 def __getitem__(self, key): 711 if not isinstance(key, int): 712 raise TypeError, "Proxy object requires integer index" 713 self._modifiers.append(key) 714 return self 715 716 def find(self, obj): 717 try: 718 val = getattr(obj, self._attr) 719 except: 720 return None, False 721 while isproxy(val): 722 val = val.unproxy(obj) 723 for m in self._modifiers: 724 if isinstance(m, str): 725 val = getattr(val, m) 726 elif isinstance(m, int): 727 val = val[m] 728 else: 729 assert("Item must be string or integer") 730 while isproxy(val): 731 val = val.unproxy(obj) 732 return val, True 733 734 def path(self): 735 p = self._attr 736 for m in self._modifiers: 737 if isinstance(m, str): 738 p += '.%s' % m 739 elif isinstance(m, int): 740 p += '[%d]' % m 741 else: 742 assert("Item must be string or integer") 743 return p 744 745class AnyProxy(BaseProxy): 746 def find(self, obj): 747 return obj.find_any(self._pdesc.ptype) 748 749 def path(self): 750 return 'any' 751 752def isproxy(obj): 753 if isinstance(obj, (BaseProxy, EthernetAddr)): 754 return True 755 elif isinstance(obj, (list, tuple)): 756 for v in obj: 757 if isproxy(v): 758 return True 759 return False 760 761class ProxyFactory(object): 762 def __init__(self, search_self, search_up): 763 self.search_self = search_self 764 self.search_up = search_up 765 766 def __getattr__(self, attr): 767 if attr == 'any': 768 return AnyProxy(self.search_self, self.search_up) 769 else: 770 return AttrProxy(self.search_self, self.search_up, attr) 771 772# global objects for handling proxies 773Parent = ProxyFactory(search_self = False, search_up = True) 774Self = ProxyFactory(search_self = True, search_up = False) 775 776##################################################################### 777# 778# Parameter description classes 779# 780# The _params dictionary in each class maps parameter names to either 781# a Param or a VectorParam object. These objects contain the 782# parameter description string, the parameter type, and the default 783# value (if any). The convert() method on these objects is used to 784# force whatever value is assigned to the parameter to the appropriate 785# type. 786# 787# Note that the default values are loaded into the class's attribute 788# space when the parameter dictionary is initialized (in 789# MetaSimObject._new_param()); after that point they aren't used. 790# 791##################################################################### 792 793# Dummy base class to identify types that are legitimate for SimObject 794# parameters. 795class ParamValue(object): 796 797 # default for printing to .ini file is regular string conversion. 798 # will be overridden in some cases 799 def ini_str(self): 800 return str(self) 801 802 # allows us to blithely call unproxy() on things without checking 803 # if they're really proxies or not 804 def unproxy(self, base): 805 return self 806 807# Regular parameter description. 808class ParamDesc(object): 809 def __init__(self, ptype_str, ptype, *args, **kwargs): 810 self.ptype_str = ptype_str 811 # remember ptype only if it is provided 812 if ptype != None: 813 self.ptype = ptype 814 815 if args: 816 if len(args) == 1: 817 self.desc = args[0] 818 elif len(args) == 2: 819 self.default = args[0] 820 self.desc = args[1] 821 else: 822 raise TypeError, 'too many arguments' 823 824 if kwargs.has_key('desc'): 825 assert(not hasattr(self, 'desc')) 826 self.desc = kwargs['desc'] 827 del kwargs['desc'] 828 829 if kwargs.has_key('default'): 830 assert(not hasattr(self, 'default')) 831 self.default = kwargs['default'] 832 del kwargs['default'] 833 834 if kwargs: 835 raise TypeError, 'extra unknown kwargs %s' % kwargs 836 837 if not hasattr(self, 'desc'): 838 raise TypeError, 'desc attribute missing' 839 840 def __getattr__(self, attr): 841 if attr == 'ptype': 842 try: 843 ptype = eval(self.ptype_str, m5.objects.__dict__) 844 if not isinstance(ptype, type): 845 panic("Param qualifier is not a type: %s" % self.ptype) 846 self.ptype = ptype 847 return ptype 848 except NameError: 849 pass 850 raise AttributeError, "'%s' object has no attribute '%s'" % \ 851 (type(self).__name__, attr) 852 853 def convert(self, value): 854 if isinstance(value, BaseProxy): 855 value.set_param_desc(self) 856 return value 857 if not hasattr(self, 'ptype') and isNullPointer(value): 858 # deferred evaluation of SimObject; continue to defer if 859 # we're just assigning a null pointer 860 return value 861 if isinstance(value, self.ptype): 862 return value 863 if isNullPointer(value) and issubclass(self.ptype, SimObject): 864 return value 865 return self.ptype(value) 866 867# Vector-valued parameter description. Just like ParamDesc, except 868# that the value is a vector (list) of the specified type instead of a 869# single value. 870 871class VectorParamValue(list): 872 def ini_str(self): 873 return ' '.join([v.ini_str() for v in self]) 874 875 def unproxy(self, base): 876 return [v.unproxy(base) for v in self] 877 878class SimObjVector(VectorParamValue): 879 def print_ini(self): 880 for v in self: 881 v.print_ini() 882 883class VectorParamDesc(ParamDesc): 884 # Convert assigned value to appropriate type. If the RHS is not a 885 # list or tuple, it generates a single-element list. 886 def convert(self, value): 887 if isinstance(value, (list, tuple)): 888 # list: coerce each element into new list 889 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 890 if isSimObjectSequence(tmp_list): 891 return SimObjVector(tmp_list) 892 else: 893 return VectorParamValue(tmp_list) 894 else: 895 # singleton: leave it be (could coerce to a single-element 896 # list here, but for some historical reason we don't... 897 return ParamDesc.convert(self, value) 898 899 900class ParamFactory(object): 901 def __init__(self, param_desc_class, ptype_str = None): 902 self.param_desc_class = param_desc_class 903 self.ptype_str = ptype_str 904 905 def __getattr__(self, attr): 906 if self.ptype_str: 907 attr = self.ptype_str + '.' + attr 908 return ParamFactory(self.param_desc_class, attr) 909 910 # E.g., Param.Int(5, "number of widgets") 911 def __call__(self, *args, **kwargs): 912 caller_frame = inspect.currentframe().f_back 913 ptype = None 914 try: 915 ptype = eval(self.ptype_str, 916 caller_frame.f_globals, caller_frame.f_locals) 917 if not isinstance(ptype, type): 918 raise TypeError, \ 919 "Param qualifier is not a type: %s" % ptype 920 except NameError: 921 # if name isn't defined yet, assume it's a SimObject, and 922 # try to resolve it later 923 pass 924 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 925 926Param = ParamFactory(ParamDesc) 927VectorParam = ParamFactory(VectorParamDesc) 928 929##################################################################### 930# 931# Parameter Types 932# 933# Though native Python types could be used to specify parameter types 934# (the 'ptype' field of the Param and VectorParam classes), it's more 935# flexible to define our own set of types. This gives us more control 936# over how Python expressions are converted to values (via the 937# __init__() constructor) and how these values are printed out (via 938# the __str__() conversion method). Eventually we'll need these types 939# to correspond to distinct C++ types as well. 940# 941##################################################################### 942 943# superclass for "numeric" parameter values, to emulate math 944# operations in a type-safe way. e.g., a Latency times an int returns 945# a new Latency object. 946class NumericParamValue(ParamValue): 947 def __str__(self): 948 return str(self.value) 949 950 def __float__(self): 951 return float(self.value) 952 953 # hook for bounds checking 954 def _check(self): 955 return 956 957 def __mul__(self, other): 958 newobj = self.__class__(self) 959 newobj.value *= other 960 newobj._check() 961 return newobj 962 963 __rmul__ = __mul__ 964 965 def __div__(self, other): 966 newobj = self.__class__(self) 967 newobj.value /= other 968 newobj._check() 969 return newobj 970 971 def __sub__(self, other): 972 newobj = self.__class__(self) 973 newobj.value -= other 974 newobj._check() 975 return newobj 976 977class Range(ParamValue): 978 type = int # default; can be overridden in subclasses 979 def __init__(self, *args, **kwargs): 980 981 def handle_kwargs(self, kwargs): 982 if 'end' in kwargs: 983 self.second = self.type(kwargs.pop('end')) 984 elif 'size' in kwargs: 985 self.second = self.first + self.type(kwargs.pop('size')) - 1 986 else: 987 raise TypeError, "Either end or size must be specified" 988 989 if len(args) == 0: 990 self.first = self.type(kwargs.pop('start')) 991 handle_kwargs(self, kwargs) 992 993 elif len(args) == 1: 994 if kwargs: 995 self.first = self.type(args[0]) 996 handle_kwargs(self, kwargs) 997 elif isinstance(args[0], Range): 998 self.first = self.type(args[0].first) 999 self.second = self.type(args[0].second) 1000 else: 1001 self.first = self.type(0) 1002 self.second = self.type(args[0]) - 1 1003 1004 elif len(args) == 2: 1005 self.first = self.type(args[0]) 1006 self.second = self.type(args[1]) 1007 else: 1008 raise TypeError, "Too many arguments specified" 1009 1010 if kwargs: 1011 raise TypeError, "too many keywords: %s" % kwargs.keys() 1012 1013 def __str__(self): 1014 return '%s:%s' % (self.first, self.second) 1015 1016# Metaclass for bounds-checked integer parameters. See CheckedInt. 1017class CheckedIntType(type): 1018 def __init__(cls, name, bases, dict): 1019 super(CheckedIntType, cls).__init__(name, bases, dict) 1020 1021 # CheckedInt is an abstract base class, so we actually don't 1022 # want to do any processing on it... the rest of this code is 1023 # just for classes that derive from CheckedInt. 1024 if name == 'CheckedInt': 1025 return 1026 1027 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 1028 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 1029 panic("CheckedInt subclass %s must define either\n" \ 1030 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 1031 % name); 1032 if cls.unsigned: 1033 cls.min = 0 1034 cls.max = 2 ** cls.size - 1 1035 else: 1036 cls.min = -(2 ** (cls.size - 1)) 1037 cls.max = (2 ** (cls.size - 1)) - 1 1038 1039# Abstract superclass for bounds-checked integer parameters. This 1040# class is subclassed to generate parameter classes with specific 1041# bounds. Initialization of the min and max bounds is done in the 1042# metaclass CheckedIntType.__init__. 1043class CheckedInt(NumericParamValue): 1044 __metaclass__ = CheckedIntType 1045 1046 def _check(self): 1047 if not self.min <= self.value <= self.max: 1048 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 1049 (self.min, self.value, self.max) 1050 1051 def __init__(self, value): 1052 if isinstance(value, str): 1053 self.value = toInteger(value) 1054 elif isinstance(value, (int, long, float)): 1055 self.value = long(value) 1056 self._check() 1057 1058class Int(CheckedInt): size = 32; unsigned = False 1059class Unsigned(CheckedInt): size = 32; unsigned = True 1060 1061class Int8(CheckedInt): size = 8; unsigned = False 1062class UInt8(CheckedInt): size = 8; unsigned = True 1063class Int16(CheckedInt): size = 16; unsigned = False 1064class UInt16(CheckedInt): size = 16; unsigned = True 1065class Int32(CheckedInt): size = 32; unsigned = False 1066class UInt32(CheckedInt): size = 32; unsigned = True 1067class Int64(CheckedInt): size = 64; unsigned = False 1068class UInt64(CheckedInt): size = 64; unsigned = True 1069 1070class Counter(CheckedInt): size = 64; unsigned = True 1071class Tick(CheckedInt): size = 64; unsigned = True 1072class TcpPort(CheckedInt): size = 16; unsigned = True 1073class UdpPort(CheckedInt): size = 16; unsigned = True 1074 1075class Percent(CheckedInt): min = 0; max = 100 1076 1077class Float(ParamValue, float): 1078 pass 1079 1080class MemorySize(CheckedInt): 1081 size = 64 1082 unsigned = True 1083 def __init__(self, value): 1084 if isinstance(value, MemorySize): 1085 self.value = value.value 1086 else: 1087 self.value = toMemorySize(value) 1088 self._check() 1089 1090class MemorySize32(CheckedInt): 1091 size = 32 1092 unsigned = True 1093 def __init__(self, value): 1094 if isinstance(value, MemorySize): 1095 self.value = value.value 1096 else: 1097 self.value = toMemorySize(value) 1098 self._check() 1099 1100class Addr(CheckedInt): 1101 size = 64 1102 unsigned = True 1103 def __init__(self, value): 1104 if isinstance(value, Addr): 1105 self.value = value.value 1106 else: 1107 try: 1108 self.value = toMemorySize(value) 1109 except TypeError: 1110 self.value = long(value) 1111 self._check() 1112 1113class AddrRange(Range): 1114 type = Addr 1115 1116# String-valued parameter. Just mixin the ParamValue class 1117# with the built-in str class. 1118class String(ParamValue,str): 1119 pass 1120 1121# Boolean parameter type. Python doesn't let you subclass bool, since 1122# it doesn't want to let you create multiple instances of True and 1123# False. Thus this is a little more complicated than String. 1124class Bool(ParamValue): 1125 def __init__(self, value): 1126 try: 1127 self.value = toBool(value) 1128 except TypeError: 1129 self.value = bool(value) 1130 1131 def __str__(self): 1132 return str(self.value) 1133 1134 def ini_str(self): 1135 if self.value: 1136 return 'true' 1137 return 'false' 1138 1139def IncEthernetAddr(addr, val = 1): 1140 bytes = map(lambda x: int(x, 16), addr.split(':')) 1141 bytes[5] += val 1142 for i in (5, 4, 3, 2, 1): 1143 val,rem = divmod(bytes[i], 256) 1144 bytes[i] = rem 1145 if val == 0: 1146 break 1147 bytes[i - 1] += val 1148 assert(bytes[0] <= 255) 1149 return ':'.join(map(lambda x: '%02x' % x, bytes)) 1150 1151class NextEthernetAddr(object): 1152 addr = "00:90:00:00:00:01" 1153 1154 def __init__(self, inc = 1): 1155 self.value = NextEthernetAddr.addr 1156 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) 1157 1158class EthernetAddr(ParamValue): 1159 def __init__(self, value): 1160 if value == NextEthernetAddr: 1161 self.value = value 1162 return 1163 1164 if not isinstance(value, str): 1165 raise TypeError, "expected an ethernet address and didn't get one" 1166 1167 bytes = value.split(':') 1168 if len(bytes) != 6: 1169 raise TypeError, 'invalid ethernet address %s' % value 1170 1171 for byte in bytes: 1172 if not 0 <= int(byte) <= 256: 1173 raise TypeError, 'invalid ethernet address %s' % value 1174 1175 self.value = value 1176 1177 def unproxy(self, base): 1178 if self.value == NextEthernetAddr: 1179 self.addr = self.value().value 1180 return self 1181 1182 def __str__(self): 1183 if self.value == NextEthernetAddr: 1184 if hasattr(self, 'addr'): 1185 return self.addr 1186 else: 1187 return "NextEthernetAddr (unresolved)" 1188 else: 1189 return self.value 1190 1191# Special class for NULL pointers. Note the special check in 1192# make_param_value() above that lets these be assigned where a 1193# SimObject is required. 1194# only one copy of a particular node 1195class NullSimObject(object): 1196 __metaclass__ = Singleton 1197 1198 def __call__(cls): 1199 return cls 1200 1201 def _instantiate(self, parent = None, path = ''): 1202 pass 1203 1204 def ini_str(self): 1205 return 'Null' 1206 1207 def unproxy(self, base): 1208 return self 1209 1210 def set_path(self, parent, name): 1211 pass 1212 def __str__(self): 1213 return 'Null' 1214 1215# The only instance you'll ever need... 1216Null = NULL = NullSimObject() 1217 1218# Enumerated types are a little more complex. The user specifies the 1219# type as Enum(foo) where foo is either a list or dictionary of 1220# alternatives (typically strings, but not necessarily so). (In the 1221# long run, the integer value of the parameter will be the list index 1222# or the corresponding dictionary value. For now, since we only check 1223# that the alternative is valid and then spit it into a .ini file, 1224# there's not much point in using the dictionary.) 1225 1226# What Enum() must do is generate a new type encapsulating the 1227# provided list/dictionary so that specific values of the parameter 1228# can be instances of that type. We define two hidden internal 1229# classes (_ListEnum and _DictEnum) to serve as base classes, then 1230# derive the new type from the appropriate base class on the fly. 1231 1232 1233# Metaclass for Enum types 1234class MetaEnum(type): 1235 def __init__(cls, name, bases, init_dict): 1236 if init_dict.has_key('map'): 1237 if not isinstance(cls.map, dict): 1238 raise TypeError, "Enum-derived class attribute 'map' " \ 1239 "must be of type dict" 1240 # build list of value strings from map 1241 cls.vals = cls.map.keys() 1242 cls.vals.sort() 1243 elif init_dict.has_key('vals'): 1244 if not isinstance(cls.vals, list): 1245 raise TypeError, "Enum-derived class attribute 'vals' " \ 1246 "must be of type list" 1247 # build string->value map from vals sequence 1248 cls.map = {} 1249 for idx,val in enumerate(cls.vals): 1250 cls.map[val] = idx 1251 else: 1252 raise TypeError, "Enum-derived class must define "\ 1253 "attribute 'map' or 'vals'" 1254 1255 super(MetaEnum, cls).__init__(name, bases, init_dict) 1256 1257 def cpp_declare(cls): 1258 s = 'enum %s {\n ' % cls.__name__ 1259 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 1260 s += '\n};\n' 1261 return s 1262 1263# Base class for enum types. 1264class Enum(ParamValue): 1265 __metaclass__ = MetaEnum 1266 vals = [] 1267 1268 def __init__(self, value): 1269 if value not in self.map: 1270 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 1271 % (value, self.vals) 1272 self.value = value 1273 1274 def __str__(self): 1275 return self.value 1276 1277ticks_per_sec = None 1278 1279# how big does a rounding error need to be before we warn about it? 1280frequency_tolerance = 0.001 # 0.1% 1281 1282# convert a floting-point # of ticks to integer, and warn if rounding 1283# discards too much precision 1284def tick_check(float_ticks): 1285 if float_ticks == 0: 1286 return 0 1287 int_ticks = int(round(float_ticks)) 1288 err = (float_ticks - int_ticks) / float_ticks 1289 if err > frequency_tolerance: 1290 print >> sys.stderr, "Warning: rounding error > tolerance" 1291 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) 1292 #raise ValueError 1293 return int_ticks 1294 1295def getLatency(value): 1296 if isinstance(value, Latency) or isinstance(value, Clock): 1297 return value.value 1298 elif isinstance(value, Frequency) or isinstance(value, RootClock): 1299 return 1 / value.value 1300 elif isinstance(value, str): 1301 try: 1302 return toLatency(value) 1303 except ValueError: 1304 try: 1305 return 1 / toFrequency(value) 1306 except ValueError: 1307 pass # fall through 1308 raise ValueError, "Invalid Frequency/Latency value '%s'" % value 1309 1310 1311class Latency(NumericParamValue): 1312 def __init__(self, value): 1313 self.value = getLatency(value) 1314 1315 def __getattr__(self, attr): 1316 if attr in ('latency', 'period'): 1317 return self 1318 if attr == 'frequency': 1319 return Frequency(self) 1320 raise AttributeError, "Latency object has no attribute '%s'" % attr 1321 1322 # convert latency to ticks 1323 def ini_str(self): 1324 return str(tick_check(self.value * ticks_per_sec)) 1325 1326class Frequency(NumericParamValue): 1327 def __init__(self, value): 1328 self.value = 1 / getLatency(value) 1329 1330 def __getattr__(self, attr): 1331 if attr == 'frequency': 1332 return self 1333 if attr in ('latency', 'period'): 1334 return Latency(self) 1335 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1336 1337 # convert frequency to ticks per period 1338 def ini_str(self): 1339 return self.period.ini_str() 1340 1341# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). 1342# We can't inherit from Frequency because we don't want it to be directly 1343# assignable to a regular Frequency parameter. 1344class RootClock(ParamValue): 1345 def __init__(self, value): 1346 self.value = 1 / getLatency(value) 1347 1348 def __getattr__(self, attr): 1349 if attr == 'frequency': 1350 return Frequency(self) 1351 if attr in ('latency', 'period'): 1352 return Latency(self) 1353 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1354 1355 def ini_str(self): 1356 return str(tick_check(self.value)) 1357 1358# A generic frequency and/or Latency value. Value is stored as a latency, 1359# but to avoid ambiguity this object does not support numeric ops (* or /). 1360# An explicit conversion to a Latency or Frequency must be made first. 1361class Clock(ParamValue): 1362 def __init__(self, value): 1363 self.value = getLatency(value) 1364 1365 def __getattr__(self, attr): 1366 if attr == 'frequency': 1367 return Frequency(self) 1368 if attr in ('latency', 'period'): 1369 return Latency(self) 1370 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1371 1372 def ini_str(self): 1373 return self.period.ini_str() 1374 1375class NetworkBandwidth(float,ParamValue): 1376 def __new__(cls, value): 1377 val = toNetworkBandwidth(value) / 8.0 1378 return super(cls, NetworkBandwidth).__new__(cls, val) 1379 1380 def __str__(self): 1381 return str(self.val) 1382 1383 def ini_str(self): 1384 return '%f' % (ticks_per_sec / float(self)) 1385 1386class MemoryBandwidth(float,ParamValue): 1387 def __new__(self, value): 1388 val = toMemoryBandwidth(value) 1389 return super(cls, MemoryBandwidth).__new__(cls, val) 1390 1391 def __str__(self): 1392 return str(self.val) 1393 1394 def ini_str(self): 1395 return '%f' % (ticks_per_sec / float(self)) 1396 1397# 1398# "Constants"... handy aliases for various values. 1399# 1400 1401# Some memory range specifications use this as a default upper bound. 1402MaxAddr = Addr.max 1403MaxTick = Tick.max 1404AllMemory = AddrRange(0, MaxAddr) 1405 1406 1407##################################################################### 1408# 1409# Port objects 1410# 1411# Ports are used to interconnect objects in the memory system. 1412# 1413##################################################################### 1414 1415# Port reference: encapsulates a reference to a particular port on a 1416# particular SimObject. 1417class PortRef(object): 1418 def __init__(self, simobj, name, isVec): 1419 assert(isSimObject(simobj)) 1420 self.simobj = simobj 1421 self.name = name 1422 self.index = -1 1423 self.isVec = isVec # is this a vector port? 1424 self.peer = None # not associated with another port yet 1425 self.ccConnected = False # C++ port connection done? 1426 1427 # Set peer port reference. Called via __setattr__ as a result of 1428 # a port assignment, e.g., "obj1.port1 = obj2.port2". 1429 def setPeer(self, other): 1430 if self.isVec: 1431 curMap = self.simobj._port_map.get(self.name, []) 1432 self.index = len(curMap) 1433 curMap.append(other) 1434 else: 1435 curMap = self.simobj._port_map.get(self.name) 1436 if curMap and not self.isVec: 1437 print "warning: overwriting port", self.simobj, self.name 1438 curMap = other 1439 self.simobj._port_map[self.name] = curMap 1440 self.peer = other 1441 1442 def clone(self, memo): 1443 newRef = copy.copy(self) 1444 assert(isSimObject(newRef.simobj)) 1445 newRef.simobj = newRef.simobj(_memo=memo) 1446 # Tricky: if I'm the *second* PortRef in the pair to be 1447 # cloned, then my peer is still in the middle of its clone 1448 # method, and thus hasn't returned to its owner's 1449 # SimObject.__init__ to get installed in _port_map. As a 1450 # result I have no way of finding the *new* peer object. So I 1451 # mark myself as "waiting" for my peer, and I let the *first* 1452 # PortRef clone call set up both peer pointers after I return. 1453 newPeer = newRef.simobj._port_map.get(self.name) 1454 if newPeer: 1455 if self.isVec: 1456 assert(self.index != -1) 1457 newPeer = newPeer[self.index] 1458 # other guy is all set up except for his peer pointer 1459 assert(newPeer.peer == -1) # peer must be waiting for handshake 1460 newPeer.peer = newRef 1461 newRef.peer = newPeer 1462 else: 1463 # other guy is in clone; just wait for him to do the work 1464 newRef.peer = -1 # mark as waiting for handshake 1465 return newRef 1466 1467 # Call C++ to create corresponding port connection between C++ objects 1468 def ccConnect(self): 1469 if self.ccConnected: # already done this 1470 return 1471 peer = self.peer 1472 cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, 1473 peer.simobj.getCCObject(), peer.name, peer.index) 1474 self.ccConnected = True 1475 peer.ccConnected = True 1476 1477# Port description object. Like a ParamDesc object, this represents a 1478# logical port in the SimObject class, not a particular port on a 1479# SimObject instance. The latter are represented by PortRef objects. 1480class Port(object): 1481 def __init__(self, desc): 1482 self.desc = desc 1483 self.isVec = False 1484 1485 # Generate a PortRef for this port on the given SimObject with the 1486 # given name 1487 def makeRef(self, simobj, name): 1488 return PortRef(simobj, name, self.isVec) 1489 1490 # Connect an instance of this port (on the given SimObject with 1491 # the given name) with the port described by the supplied PortRef 1492 def connect(self, simobj, name, ref): 1493 if not isinstance(ref, PortRef): 1494 raise TypeError, \ 1495 "assigning non-port reference port '%s'" % name 1496 myRef = self.makeRef(simobj, name) 1497 myRef.setPeer(ref) 1498 ref.setPeer(myRef) 1499 1500# VectorPort description object. Like Port, but represents a vector 1501# of connections (e.g., as on a Bus). 1502class VectorPort(Port): 1503 def __init__(self, desc): 1504 Port.__init__(self, desc) 1505 self.isVec = True 1506 1507##################################################################### 1508 1509# __all__ defines the list of symbols that get exported when 1510# 'from config import *' is invoked. Try to keep this reasonably 1511# short to avoid polluting other namespaces. 1512__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', 1513 'Parent', 'Self', 1514 'Enum', 'Bool', 'String', 'Float', 1515 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1516 'Int32', 'UInt32', 'Int64', 'UInt64', 1517 'Counter', 'Addr', 'Tick', 'Percent', 1518 'TcpPort', 'UdpPort', 'EthernetAddr', 1519 'MemorySize', 'MemorySize32', 1520 'Latency', 'Frequency', 'RootClock', 'Clock', 1521 'NetworkBandwidth', 'MemoryBandwidth', 1522 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 1523 'Null', 'NULL', 1524 'NextEthernetAddr', 1525 'Port', 'VectorPort'] 1526 1527