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 --- 12 unchanged lines hidden (view full) --- 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 34from convert import * 35from multidict import multidict 36 37noDot = False 38try: --- 40 unchanged lines hidden (view full) --- 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 --- 13 unchanged lines hidden (view full) --- 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): --- 10 unchanged lines hidden (view full) --- 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) --- 22 unchanged lines hidden (view full) --- 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() --- 60 unchanged lines hidden (view full) --- 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 = m5.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 --- 82 unchanged lines hidden (view full) --- 628 obj = base 629 done = False 630 631 if self._search_self: 632 result, done = self.find(obj) 633 634 if self._search_up: 635 while not done: |
636 obj = obj._parent 637 if not obj: 638 break |
639 result, done = self.find(obj) 640 641 if not done: 642 raise AttributeError, "Can't resolve proxy '%s' from '%s'" % \ 643 (self.path(), base.path()) 644 645 if isinstance(result, BaseProxy): 646 if result == self: --- 99 unchanged lines hidden (view full) --- 746# global objects for handling proxies 747Parent = ProxyFactory(search_self = False, search_up = True) 748Self = ProxyFactory(search_self = True, search_up = False) 749 750##################################################################### 751# 752# Parameter description classes 753# |
754# The _params dictionary in each class maps parameter names to either 755# a Param or a VectorParam object. These objects contain the |
756# parameter description string, the parameter type, and the default |
757# value (if any). The convert() method on these objects is used to 758# force whatever value is assigned to the parameter to the appropriate 759# type. |
760# 761# Note that the default values are loaded into the class's attribute 762# space when the parameter dictionary is initialized (in |
763# MetaSimObject._new_param()); after that point they aren't used. |
764# 765##################################################################### 766 767# Dummy base class to identify types that are legitimate for SimObject 768# parameters. 769class ParamValue(object): 770 771 # default for printing to .ini file is regular string conversion. --- 613 unchanged lines hidden (view full) --- 1385# Ports are used to interconnect objects in the memory system. 1386# 1387##################################################################### 1388 1389# Port reference: encapsulates a reference to a particular port on a 1390# particular SimObject. 1391class PortRef(object): 1392 def __init__(self, simobj, name, isVec): |
1393 assert(isSimObject(simobj)) |
1394 self.simobj = simobj 1395 self.name = name 1396 self.index = -1 1397 self.isVec = isVec # is this a vector port? 1398 self.peer = None # not associated with another port yet 1399 self.ccConnected = False # C++ port connection done? 1400 1401 # Set peer port reference. Called via __setattr__ as a result of --- 6 unchanged lines hidden (view full) --- 1408 else: 1409 curMap = self.simobj._port_map.get(self.name) 1410 if curMap and not self.isVec: 1411 print "warning: overwriting port", self.simobj, self.name 1412 curMap = other 1413 self.simobj._port_map[self.name] = curMap 1414 self.peer = other 1415 |
1416 def clone(self, memo): 1417 newRef = copy.copy(self) 1418 assert(isSimObject(newRef.simobj)) 1419 newRef.simobj = newRef.simobj(_memo=memo) 1420 # Tricky: if I'm the *second* PortRef in the pair to be 1421 # cloned, then my peer is still in the middle of its clone 1422 # method, and thus hasn't returned to its owner's 1423 # SimObject.__init__ to get installed in _port_map. As a 1424 # result I have no way of finding the *new* peer object. So I 1425 # mark myself as "waiting" for my peer, and I let the *first* 1426 # PortRef clone call set up both peer pointers after I return. 1427 newPeer = newRef.simobj._port_map.get(self.name) 1428 if newPeer: 1429 if self.isVec: 1430 assert(self.index != -1) 1431 newPeer = newPeer[self.index] 1432 # other guy is all set up except for his peer pointer 1433 assert(newPeer.peer == -1) # peer must be waiting for handshake 1434 newPeer.peer = newRef 1435 newRef.peer = newPeer 1436 else: 1437 # other guy is in clone; just wait for him to do the work 1438 newRef.peer = -1 # mark as waiting for handshake 1439 return newRef 1440 |
1441 # Call C++ to create corresponding port connection between C++ objects 1442 def ccConnect(self): 1443 if self.ccConnected: # already done this 1444 return 1445 peer = self.peer |
1446 m5.main.connectPorts(self.simobj.getCCObject(), self.name, self.index, 1447 peer.simobj.getCCObject(), peer.name, peer.index) |
1448 self.ccConnected = True 1449 peer.ccConnected = True 1450 1451# Port description object. Like a ParamDesc object, this represents a 1452# logical port in the SimObject class, not a particular port on a 1453# SimObject instance. The latter are represented by PortRef objects. 1454class Port(object): 1455 def __init__(self, desc): 1456 self.desc = desc 1457 self.isVec = False 1458 1459 # Generate a PortRef for this port on the given SimObject with the 1460 # given name 1461 def makeRef(self, simobj, name): 1462 return PortRef(simobj, name, self.isVec) 1463 1464 # Connect an instance of this port (on the given SimObject with 1465 # the given name) with the port described by the supplied PortRef 1466 def connect(self, simobj, name, ref): |
1467 if not isinstance(ref, PortRef): 1468 raise TypeError, \ 1469 "assigning non-port reference port '%s'" % name |
1470 myRef = self.makeRef(simobj, name) 1471 myRef.setPeer(ref) 1472 ref.setPeer(myRef) 1473 1474# VectorPort description object. Like Port, but represents a vector 1475# of connections (e.g., as on a Bus). 1476class VectorPort(Port): 1477 def __init__(self, desc): --- 23 unchanged lines hidden --- |