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