params.py revision 10364
12SN/A# Copyright (c) 2012-2014 ARM Limited 21762SN/A# All rights reserved. 37534Ssteve.reinhardt@amd.com# 42SN/A# The license below extends only to copyright in the software and shall 52SN/A# not be construed as granting a license to any other intellectual 62SN/A# property including but not limited to intellectual property relating 72SN/A# to a hardware implementation of the functionality of the software 82SN/A# licensed hereunder. You may use the software subject to the license 92SN/A# terms below provided that you ensure that this notice is replicated 102SN/A# unmodified and in its entirety in all distributions of the software, 112SN/A# modified or unmodified, in source code or in binary form. 122SN/A# 132SN/A# Copyright (c) 2004-2006 The Regents of The University of Michigan 142SN/A# Copyright (c) 2010-2011 Advanced Micro Devices, Inc. 152SN/A# All rights reserved. 162SN/A# 172SN/A# Redistribution and use in source and binary forms, with or without 182SN/A# modification, are permitted provided that the following conditions are 192SN/A# met: redistributions of source code must retain the above copyright 202SN/A# notice, this list of conditions and the following disclaimer; 212SN/A# redistributions in binary form must reproduce the above copyright 222SN/A# notice, this list of conditions and the following disclaimer in the 232SN/A# documentation and/or other materials provided with the distribution; 242SN/A# neither the name of the copyright holders nor the names of its 252SN/A# contributors may be used to endorse or promote products derived from 262SN/A# this software without specific prior written permission. 272SN/A# 282665Ssaidi@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 292665Ssaidi@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 302665Ssaidi@eecs.umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 312SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 322SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 332SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 342SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 352SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 362SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 372SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 382SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 392SN/A# 405491Sgblack@eecs.umich.edu# Authors: Steve Reinhardt 415491Sgblack@eecs.umich.edu# Nathan Binkert 422SN/A# Gabe Black 435491Sgblack@eecs.umich.edu# Andreas Hansson 442SN/A 452SN/A##################################################################### 468737Skoansin.tan@gmail.com# 474762Snate@binkert.org# Parameter description classes 485605Snate@binkert.org# 4956SN/A# The _params dictionary in each class maps parameter names to either 502SN/A# a Param or a VectorParam object. These objects contain the 512797Sktlim@umich.edu# parameter description string, the parameter type, and the default 522797Sktlim@umich.edu# value (if any). The convert() method on these objects is used to 532609SN/A# force whatever value is assigned to the parameter to the appropriate 549196SAndreas.Sandberg@arm.com# type. 552SN/A# 562SN/A# Note that the default values are loaded into the class's attribute 572SN/A# space when the parameter dictionary is initialized (in 589196SAndreas.Sandberg@arm.com# MetaSimObject._new_param()); after that point they aren't used. 599196SAndreas.Sandberg@arm.com# 609196SAndreas.Sandberg@arm.com##################################################################### 619196SAndreas.Sandberg@arm.com 629196SAndreas.Sandberg@arm.comimport copy 639196SAndreas.Sandberg@arm.comimport datetime 649196SAndreas.Sandberg@arm.comimport re 659196SAndreas.Sandberg@arm.comimport sys 669196SAndreas.Sandberg@arm.comimport time 679196SAndreas.Sandberg@arm.comimport math 689196SAndreas.Sandberg@arm.com 699196SAndreas.Sandberg@arm.comimport proxy 709196SAndreas.Sandberg@arm.comimport ticks 719196SAndreas.Sandberg@arm.comfrom util import * 729196SAndreas.Sandberg@arm.com 739196SAndreas.Sandberg@arm.comdef isSimObject(*args, **kwargs): 749196SAndreas.Sandberg@arm.com return SimObject.isSimObject(*args, **kwargs) 759196SAndreas.Sandberg@arm.com 769196SAndreas.Sandberg@arm.comdef isSimObjectSequence(*args, **kwargs): 779196SAndreas.Sandberg@arm.com return SimObject.isSimObjectSequence(*args, **kwargs) 789196SAndreas.Sandberg@arm.com 799196SAndreas.Sandberg@arm.comdef isSimObjectClass(*args, **kwargs): 809196SAndreas.Sandberg@arm.com return SimObject.isSimObjectClass(*args, **kwargs) 819196SAndreas.Sandberg@arm.com 829196SAndreas.Sandberg@arm.comallParams = {} 839196SAndreas.Sandberg@arm.com 849196SAndreas.Sandberg@arm.comclass MetaParamValue(type): 859196SAndreas.Sandberg@arm.com def __new__(mcls, name, bases, dct): 869196SAndreas.Sandberg@arm.com cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct) 879196SAndreas.Sandberg@arm.com assert name not in allParams 889196SAndreas.Sandberg@arm.com allParams[name] = cls 899196SAndreas.Sandberg@arm.com return cls 909196SAndreas.Sandberg@arm.com 919196SAndreas.Sandberg@arm.com 929196SAndreas.Sandberg@arm.com# Dummy base class to identify types that are legitimate for SimObject 939196SAndreas.Sandberg@arm.com# parameters. 949196SAndreas.Sandberg@arm.comclass ParamValue(object): 959196SAndreas.Sandberg@arm.com __metaclass__ = MetaParamValue 969196SAndreas.Sandberg@arm.com cmd_line_settable = False 979196SAndreas.Sandberg@arm.com 989196SAndreas.Sandberg@arm.com # Generate the code needed as a prerequisite for declaring a C++ 999196SAndreas.Sandberg@arm.com # object of this type. Typically generates one or more #include 1009196SAndreas.Sandberg@arm.com # statements. Used when declaring parameters of this type. 1019196SAndreas.Sandberg@arm.com @classmethod 1029196SAndreas.Sandberg@arm.com def cxx_predecls(cls, code): 1039196SAndreas.Sandberg@arm.com pass 1049196SAndreas.Sandberg@arm.com 1059196SAndreas.Sandberg@arm.com # Generate the code needed as a prerequisite for including a 1069196SAndreas.Sandberg@arm.com # reference to a C++ object of this type in a SWIG .i file. 1079196SAndreas.Sandberg@arm.com # Typically generates one or more %import or %include statements. 1089196SAndreas.Sandberg@arm.com @classmethod 1099196SAndreas.Sandberg@arm.com def swig_predecls(cls, code): 1109196SAndreas.Sandberg@arm.com pass 1119196SAndreas.Sandberg@arm.com 1129196SAndreas.Sandberg@arm.com # default for printing to .ini file is regular string conversion. 1139196SAndreas.Sandberg@arm.com # will be overridden in some cases 1149196SAndreas.Sandberg@arm.com def ini_str(self): 1159196SAndreas.Sandberg@arm.com return str(self) 1162SN/A 1177492Ssteve.reinhardt@amd.com # allows us to blithely call unproxy() on things without checking 1182SN/A # if they're really proxies or not 1191553SN/A def unproxy(self, base): 1209196SAndreas.Sandberg@arm.com return self 1219196SAndreas.Sandberg@arm.com 1229196SAndreas.Sandberg@arm.com # Produce a human readable version of the stored value 1239196SAndreas.Sandberg@arm.com def pretty_print(self, value): 1249196SAndreas.Sandberg@arm.com return str(value) 1259196SAndreas.Sandberg@arm.com 1269196SAndreas.Sandberg@arm.com# Regular parameter description. 1279196SAndreas.Sandberg@arm.comclass ParamDesc(object): 1289196SAndreas.Sandberg@arm.com def __init__(self, ptype_str, ptype, *args, **kwargs): 1299196SAndreas.Sandberg@arm.com self.ptype_str = ptype_str 1309196SAndreas.Sandberg@arm.com # remember ptype only if it is provided 1319196SAndreas.Sandberg@arm.com if ptype != None: 1329196SAndreas.Sandberg@arm.com self.ptype = ptype 1339196SAndreas.Sandberg@arm.com 1349196SAndreas.Sandberg@arm.com if args: 1359196SAndreas.Sandberg@arm.com if len(args) == 1: 1369196SAndreas.Sandberg@arm.com self.desc = args[0] 1379196SAndreas.Sandberg@arm.com elif len(args) == 2: 1382797Sktlim@umich.edu self.default = args[0] 1399196SAndreas.Sandberg@arm.com self.desc = args[1] 1409196SAndreas.Sandberg@arm.com else: 1419196SAndreas.Sandberg@arm.com raise TypeError, 'too many arguments' 1422797Sktlim@umich.edu 1433202Shsul@eecs.umich.edu if kwargs.has_key('desc'): 1442901Ssaidi@eecs.umich.edu assert(not hasattr(self, 'desc')) 1452901Ssaidi@eecs.umich.edu self.desc = kwargs['desc'] 1462797Sktlim@umich.edu del kwargs['desc'] 147265SN/A 1482797Sktlim@umich.edu if kwargs.has_key('default'): 1491553SN/A assert(not hasattr(self, 'default')) 1501553SN/A self.default = kwargs['default'] 1512797Sktlim@umich.edu del kwargs['default'] 1522797Sktlim@umich.edu 1532SN/A if kwargs: 1542SN/A raise TypeError, 'extra unknown kwargs %s' % kwargs 1552SN/A 1569196SAndreas.Sandberg@arm.com if not hasattr(self, 'desc'): 1572SN/A raise TypeError, 'desc attribute missing' 1582SN/A 1594762Snate@binkert.org def __getattr__(self, attr): 1609196SAndreas.Sandberg@arm.com if attr == 'ptype': 1614762Snate@binkert.org ptype = SimObject.allClasses[self.ptype_str] 1624762Snate@binkert.org assert isSimObjectClass(ptype) 1632SN/A self.ptype = ptype 1644762Snate@binkert.org return ptype 1654762Snate@binkert.org 1664762Snate@binkert.org raise AttributeError, "'%s' object has no attribute '%s'" % \ 1672SN/A (type(self).__name__, attr) 1682SN/A 1695034Smilesck@eecs.umich.edu def example_str(self): 1705034Smilesck@eecs.umich.edu if hasattr(self.ptype, "ex_str"): 1711553SN/A return self.ptype.ex_str 172265SN/A else: 1737532Ssteve.reinhardt@amd.com return self.ptype_str 1747532Ssteve.reinhardt@amd.com 1757532Ssteve.reinhardt@amd.com # Is the param available to be exposed on the command line 1767532Ssteve.reinhardt@amd.com def isCmdLineSettable(self): 1777532Ssteve.reinhardt@amd.com if hasattr(self.ptype, "cmd_line_settable"): 1787532Ssteve.reinhardt@amd.com return self.ptype.cmd_line_settable 179465SN/A else: 180465SN/A return False 1817532Ssteve.reinhardt@amd.com 1827532Ssteve.reinhardt@amd.com def convert(self, value): 1837532Ssteve.reinhardt@amd.com if isinstance(value, proxy.BaseProxy): 1847532Ssteve.reinhardt@amd.com value.set_param_desc(self) 1857532Ssteve.reinhardt@amd.com return value 1867532Ssteve.reinhardt@amd.com if not hasattr(self, 'ptype') and isNullPointer(value): 1877532Ssteve.reinhardt@amd.com # deferred evaluation of SimObject; continue to defer if 1887532Ssteve.reinhardt@amd.com # we're just assigning a null pointer 1899196SAndreas.Sandberg@arm.com return value 1909196SAndreas.Sandberg@arm.com if isinstance(value, self.ptype): 1917532Ssteve.reinhardt@amd.com return value 1927532Ssteve.reinhardt@amd.com if isNullPointer(value) and isSimObjectClass(self.ptype): 1937532Ssteve.reinhardt@amd.com return value 1947532Ssteve.reinhardt@amd.com return self.ptype(value) 1957532Ssteve.reinhardt@amd.com 1967532Ssteve.reinhardt@amd.com def pretty_print(self, value): 1977532Ssteve.reinhardt@amd.com if isinstance(value, proxy.BaseProxy): 1987532Ssteve.reinhardt@amd.com return str(value) 1997532Ssteve.reinhardt@amd.com if isNullPointer(value): 2007532Ssteve.reinhardt@amd.com return NULL 2019196SAndreas.Sandberg@arm.com return self.ptype(value).pretty_print(value) 2029196SAndreas.Sandberg@arm.com 2039196SAndreas.Sandberg@arm.com def cxx_predecls(self, code): 2042SN/A code('#include <cstddef>') 2059196SAndreas.Sandberg@arm.com self.ptype.cxx_predecls(code) 2069196SAndreas.Sandberg@arm.com 2079196SAndreas.Sandberg@arm.com def swig_predecls(self, code): 2089196SAndreas.Sandberg@arm.com self.ptype.swig_predecls(code) 209330SN/A 2102SN/A def cxx_decl(self, code): 2117532Ssteve.reinhardt@amd.com code('${{self.ptype.cxx_type}} ${{self.name}};') 2127532Ssteve.reinhardt@amd.com 2137532Ssteve.reinhardt@amd.com# Vector-valued parameter description. Just like ParamDesc, except 2147823Ssteve.reinhardt@amd.com# that the value is a vector (list) of the specified type instead of a 2157532Ssteve.reinhardt@amd.com# single value. 2167532Ssteve.reinhardt@amd.com 2177492Ssteve.reinhardt@amd.comclass VectorParamValue(list): 218330SN/A __metaclass__ = MetaParamValue 2199196SAndreas.Sandberg@arm.com def __setattr__(self, attr, value): 2209196SAndreas.Sandberg@arm.com raise AttributeError, \ 2219196SAndreas.Sandberg@arm.com "Not allowed to set %s on '%s'" % (attr, type(self).__name__) 2229196SAndreas.Sandberg@arm.com 223938SN/A def ini_str(self): 2249196SAndreas.Sandberg@arm.com return ' '.join([v.ini_str() for v in self]) 2259196SAndreas.Sandberg@arm.com 2269196SAndreas.Sandberg@arm.com def getValue(self): 2279196SAndreas.Sandberg@arm.com return [ v.getValue() for v in self ] 2289196SAndreas.Sandberg@arm.com 2299196SAndreas.Sandberg@arm.com def unproxy(self, base): 2309196SAndreas.Sandberg@arm.com if len(self) == 1 and isinstance(self[0], proxy.AllProxy): 2319196SAndreas.Sandberg@arm.com return self[0].unproxy(base) 2329196SAndreas.Sandberg@arm.com else: 2339196SAndreas.Sandberg@arm.com return [v.unproxy(base) for v in self] 2349196SAndreas.Sandberg@arm.com 2359196SAndreas.Sandberg@arm.comclass SimObjectVector(VectorParamValue): 2369196SAndreas.Sandberg@arm.com # support clone operation 2379196SAndreas.Sandberg@arm.com def __call__(self, **kwargs): 2389196SAndreas.Sandberg@arm.com return SimObjectVector([v(**kwargs) for v in self]) 2399196SAndreas.Sandberg@arm.com 2409196SAndreas.Sandberg@arm.com def clear_parent(self, old_parent): 2419196SAndreas.Sandberg@arm.com for v in self: 2429196SAndreas.Sandberg@arm.com v.clear_parent(old_parent) 2439196SAndreas.Sandberg@arm.com 2449196SAndreas.Sandberg@arm.com def set_parent(self, parent, name): 2459196SAndreas.Sandberg@arm.com if len(self) == 1: 2469196SAndreas.Sandberg@arm.com self[0].set_parent(parent, name) 2479196SAndreas.Sandberg@arm.com else: 2489196SAndreas.Sandberg@arm.com width = int(math.ceil(math.log(len(self))/math.log(10))) 2499196SAndreas.Sandberg@arm.com for i,v in enumerate(self): 2509196SAndreas.Sandberg@arm.com v.set_parent(parent, "%s%0*d" % (name, width, i)) 2512901Ssaidi@eecs.umich.edu 2529196SAndreas.Sandberg@arm.com def has_parent(self): 2539196SAndreas.Sandberg@arm.com return reduce(lambda x,y: x and y, [v.has_parent() for v in self]) 2549196SAndreas.Sandberg@arm.com 2559196SAndreas.Sandberg@arm.com # return 'cpu0 cpu1' etc. for print_ini() 2562797Sktlim@umich.edu def get_name(self): 2579196SAndreas.Sandberg@arm.com return ' '.join([v._name for v in self]) 2589196SAndreas.Sandberg@arm.com 2599196SAndreas.Sandberg@arm.com # By iterating through the constituent members of the vector here 2609196SAndreas.Sandberg@arm.com # we can nicely handle iterating over all a SimObject's children 2619196SAndreas.Sandberg@arm.com # without having to provide lots of special functions on 2629196SAndreas.Sandberg@arm.com # SimObjectVector directly. 2638737Skoansin.tan@gmail.com def descendants(self): 2649196SAndreas.Sandberg@arm.com for v in self: 2659196SAndreas.Sandberg@arm.com for obj in v.descendants(): 2669196SAndreas.Sandberg@arm.com yield obj 2679196SAndreas.Sandberg@arm.com 2689196SAndreas.Sandberg@arm.com def get_config_as_dict(self): 2699196SAndreas.Sandberg@arm.com a = [] 2709196SAndreas.Sandberg@arm.com for v in self: 2712797Sktlim@umich.edu a.append(v.get_config_as_dict()) 2729196SAndreas.Sandberg@arm.com return a 2739196SAndreas.Sandberg@arm.com 2749196SAndreas.Sandberg@arm.com # If we are replacing an item in the vector, make sure to set the 2759196SAndreas.Sandberg@arm.com # parent reference of the new SimObject to be the same as the parent 2769196SAndreas.Sandberg@arm.com # of the SimObject being replaced. Useful to have if we created 2779196SAndreas.Sandberg@arm.com # a SimObjectVector of temporary objects that will be modified later in 2789196SAndreas.Sandberg@arm.com # configuration scripts. 2799196SAndreas.Sandberg@arm.com def __setitem__(self, key, value): 2809196SAndreas.Sandberg@arm.com val = self[key] 2819196SAndreas.Sandberg@arm.com if value.has_parent(): 2829196SAndreas.Sandberg@arm.com warn("SimObject %s already has a parent" % value.get_name() +\ 2839196SAndreas.Sandberg@arm.com " that is being overwritten by a SimObjectVector") 2849196SAndreas.Sandberg@arm.com value.set_parent(val.get_parent(), val._name) 2859196SAndreas.Sandberg@arm.com super(SimObjectVector, self).__setitem__(key, value) 2862797Sktlim@umich.edu 2872609SN/A # Enumerate the params of each member of the SimObject vector. Creates 2881031SN/A # strings that will allow indexing into the vector by the python code and 2891031SN/A # allow it to be specified on the command line. 2901031SN/A def enumerateParams(self, flags_dict = {}, 2911031SN/A cmd_line_str = "", 2921031SN/A access_str = ""): 2931031SN/A if hasattr(self, "_paramEnumed"): 2945314Sstever@gmail.com print "Cycle detected enumerating params at %s?!" % (cmd_line_str) 2955314Sstever@gmail.com else: 2965315Sstever@gmail.com x = 0 2975314Sstever@gmail.com for vals in self: 2985314Sstever@gmail.com # Each entry in the SimObjectVector should be an 2995314Sstever@gmail.com # instance of a SimObject 3002SN/A flags_dict = vals.enumerateParams(flags_dict, 3012SN/A cmd_line_str + "%d." % x, 3022SN/A access_str + "[%d]." % x) 303 x = x + 1 304 305 return flags_dict 306 307class VectorParamDesc(ParamDesc): 308 # Convert assigned value to appropriate type. If the RHS is not a 309 # list or tuple, it generates a single-element list. 310 def convert(self, value): 311 if isinstance(value, (list, tuple)): 312 # list: coerce each element into new list 313 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 314 elif isinstance(value, str): 315 # If input is a csv string 316 tmp_list = [ ParamDesc.convert(self, v) \ 317 for v in value.strip('[').strip(']').split(',') ] 318 else: 319 # singleton: coerce to a single-element list 320 tmp_list = [ ParamDesc.convert(self, value) ] 321 322 if isSimObjectSequence(tmp_list): 323 return SimObjectVector(tmp_list) 324 else: 325 return VectorParamValue(tmp_list) 326 327 # Produce a human readable example string that describes 328 # how to set this vector parameter in the absence of a default 329 # value. 330 def example_str(self): 331 s = super(VectorParamDesc, self).example_str() 332 help_str = "[" + s + "," + s + ", ...]" 333 return help_str 334 335 # Produce a human readable representation of the value of this vector param. 336 def pretty_print(self, value): 337 if isinstance(value, (list, tuple)): 338 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ] 339 elif isinstance(value, str): 340 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ] 341 else: 342 tmp_list = [ ParamDesc.pretty_print(self, value) ] 343 344 return tmp_list 345 346 # This is a helper function for the new config system 347 def __call__(self, value): 348 if isinstance(value, (list, tuple)): 349 # list: coerce each element into new list 350 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 351 elif isinstance(value, str): 352 # If input is a csv string 353 tmp_list = [ ParamDesc.convert(self, v) \ 354 for v in value.strip('[').strip(']').split(',') ] 355 else: 356 # singleton: coerce to a single-element list 357 tmp_list = [ ParamDesc.convert(self, value) ] 358 359 return VectorParamValue(tmp_list) 360 361 def swig_module_name(self): 362 return "%s_vector" % self.ptype_str 363 364 def swig_predecls(self, code): 365 code('%import "${{self.swig_module_name()}}.i"') 366 367 def swig_decl(self, code): 368 code('%module(package="m5.internal") ${{self.swig_module_name()}}') 369 code('%{') 370 self.ptype.cxx_predecls(code) 371 code('%}') 372 code() 373 # Make sure the SWIGPY_SLICE_ARG is defined through this inclusion 374 code('%include "std_container.i"') 375 code() 376 self.ptype.swig_predecls(code) 377 code() 378 code('%include "std_vector.i"') 379 code() 380 381 ptype = self.ptype_str 382 cxx_type = self.ptype.cxx_type 383 384 code('%template(vector_$ptype) std::vector< $cxx_type >;') 385 386 def cxx_predecls(self, code): 387 code('#include <vector>') 388 self.ptype.cxx_predecls(code) 389 390 def cxx_decl(self, code): 391 code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};') 392 393class ParamFactory(object): 394 def __init__(self, param_desc_class, ptype_str = None): 395 self.param_desc_class = param_desc_class 396 self.ptype_str = ptype_str 397 398 def __getattr__(self, attr): 399 if self.ptype_str: 400 attr = self.ptype_str + '.' + attr 401 return ParamFactory(self.param_desc_class, attr) 402 403 # E.g., Param.Int(5, "number of widgets") 404 def __call__(self, *args, **kwargs): 405 ptype = None 406 try: 407 ptype = allParams[self.ptype_str] 408 except KeyError: 409 # if name isn't defined yet, assume it's a SimObject, and 410 # try to resolve it later 411 pass 412 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 413 414Param = ParamFactory(ParamDesc) 415VectorParam = ParamFactory(VectorParamDesc) 416 417##################################################################### 418# 419# Parameter Types 420# 421# Though native Python types could be used to specify parameter types 422# (the 'ptype' field of the Param and VectorParam classes), it's more 423# flexible to define our own set of types. This gives us more control 424# over how Python expressions are converted to values (via the 425# __init__() constructor) and how these values are printed out (via 426# the __str__() conversion method). 427# 428##################################################################### 429 430# String-valued parameter. Just mixin the ParamValue class with the 431# built-in str class. 432class String(ParamValue,str): 433 cxx_type = 'std::string' 434 cmd_line_settable = True 435 436 @classmethod 437 def cxx_predecls(self, code): 438 code('#include <string>') 439 440 @classmethod 441 def swig_predecls(cls, code): 442 code('%include "std_string.i"') 443 444 def __call__(self, value): 445 self = value 446 return value 447 448 def getValue(self): 449 return self 450 451# superclass for "numeric" parameter values, to emulate math 452# operations in a type-safe way. e.g., a Latency times an int returns 453# a new Latency object. 454class NumericParamValue(ParamValue): 455 def __str__(self): 456 return str(self.value) 457 458 def __float__(self): 459 return float(self.value) 460 461 def __long__(self): 462 return long(self.value) 463 464 def __int__(self): 465 return int(self.value) 466 467 # hook for bounds checking 468 def _check(self): 469 return 470 471 def __mul__(self, other): 472 newobj = self.__class__(self) 473 newobj.value *= other 474 newobj._check() 475 return newobj 476 477 __rmul__ = __mul__ 478 479 def __div__(self, other): 480 newobj = self.__class__(self) 481 newobj.value /= other 482 newobj._check() 483 return newobj 484 485 def __sub__(self, other): 486 newobj = self.__class__(self) 487 newobj.value -= other 488 newobj._check() 489 return newobj 490 491# Metaclass for bounds-checked integer parameters. See CheckedInt. 492class CheckedIntType(MetaParamValue): 493 def __init__(cls, name, bases, dict): 494 super(CheckedIntType, cls).__init__(name, bases, dict) 495 496 # CheckedInt is an abstract base class, so we actually don't 497 # want to do any processing on it... the rest of this code is 498 # just for classes that derive from CheckedInt. 499 if name == 'CheckedInt': 500 return 501 502 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 503 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 504 panic("CheckedInt subclass %s must define either\n" \ 505 " 'min' and 'max' or 'size' and 'unsigned'\n", 506 name); 507 if cls.unsigned: 508 cls.min = 0 509 cls.max = 2 ** cls.size - 1 510 else: 511 cls.min = -(2 ** (cls.size - 1)) 512 cls.max = (2 ** (cls.size - 1)) - 1 513 514# Abstract superclass for bounds-checked integer parameters. This 515# class is subclassed to generate parameter classes with specific 516# bounds. Initialization of the min and max bounds is done in the 517# metaclass CheckedIntType.__init__. 518class CheckedInt(NumericParamValue): 519 __metaclass__ = CheckedIntType 520 cmd_line_settable = True 521 522 def _check(self): 523 if not self.min <= self.value <= self.max: 524 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 525 (self.min, self.value, self.max) 526 527 def __init__(self, value): 528 if isinstance(value, str): 529 self.value = convert.toInteger(value) 530 elif isinstance(value, (int, long, float, NumericParamValue)): 531 self.value = long(value) 532 else: 533 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 534 % type(value).__name__ 535 self._check() 536 537 def __call__(self, value): 538 self.__init__(value) 539 return value 540 541 @classmethod 542 def cxx_predecls(cls, code): 543 # most derived types require this, so we just do it here once 544 code('#include "base/types.hh"') 545 546 @classmethod 547 def swig_predecls(cls, code): 548 # most derived types require this, so we just do it here once 549 code('%import "stdint.i"') 550 code('%import "base/types.hh"') 551 552 def getValue(self): 553 return long(self.value) 554 555class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 556class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 557 558class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 559class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 560class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 561class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 562class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 563class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 564class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 565class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 566 567class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 568class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 569class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 570class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 571 572class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 573 574class Cycles(CheckedInt): 575 cxx_type = 'Cycles' 576 size = 64 577 unsigned = True 578 579 def getValue(self): 580 from m5.internal.core import Cycles 581 return Cycles(self.value) 582 583class Float(ParamValue, float): 584 cxx_type = 'double' 585 cmdLineSettable = True 586 587 def __init__(self, value): 588 if isinstance(value, (int, long, float, NumericParamValue, Float, str)): 589 self.value = float(value) 590 else: 591 raise TypeError, "Can't convert object of type %s to Float" \ 592 % type(value).__name__ 593 594 def __call__(self, value): 595 self.__init__(value) 596 return value 597 598 def getValue(self): 599 return float(self.value) 600 601class MemorySize(CheckedInt): 602 cxx_type = 'uint64_t' 603 ex_str = '512MB' 604 size = 64 605 unsigned = True 606 def __init__(self, value): 607 if isinstance(value, MemorySize): 608 self.value = value.value 609 else: 610 self.value = convert.toMemorySize(value) 611 self._check() 612 613class MemorySize32(CheckedInt): 614 cxx_type = 'uint32_t' 615 ex_str = '512MB' 616 size = 32 617 unsigned = True 618 def __init__(self, value): 619 if isinstance(value, MemorySize): 620 self.value = value.value 621 else: 622 self.value = convert.toMemorySize(value) 623 self._check() 624 625class Addr(CheckedInt): 626 cxx_type = 'Addr' 627 size = 64 628 unsigned = True 629 def __init__(self, value): 630 if isinstance(value, Addr): 631 self.value = value.value 632 else: 633 try: 634 # Often addresses are referred to with sizes. Ex: A device 635 # base address is at "512MB". Use toMemorySize() to convert 636 # these into addresses. If the address is not specified with a 637 # "size", an exception will occur and numeric translation will 638 # proceed below. 639 self.value = convert.toMemorySize(value) 640 except (TypeError, ValueError): 641 # Convert number to string and use long() to do automatic 642 # base conversion (requires base=0 for auto-conversion) 643 self.value = long(str(value), base=0) 644 645 self._check() 646 def __add__(self, other): 647 if isinstance(other, Addr): 648 return self.value + other.value 649 else: 650 return self.value + other 651 def pretty_print(self, value): 652 try: 653 val = convert.toMemorySize(value) 654 except TypeError: 655 val = long(value) 656 return "0x%x" % long(val) 657 658class AddrRange(ParamValue): 659 cxx_type = 'AddrRange' 660 661 def __init__(self, *args, **kwargs): 662 # Disable interleaving by default 663 self.intlvHighBit = 0 664 self.intlvBits = 0 665 self.intlvMatch = 0 666 667 def handle_kwargs(self, kwargs): 668 # An address range needs to have an upper limit, specified 669 # either explicitly with an end, or as an offset using the 670 # size keyword. 671 if 'end' in kwargs: 672 self.end = Addr(kwargs.pop('end')) 673 elif 'size' in kwargs: 674 self.end = self.start + Addr(kwargs.pop('size')) - 1 675 else: 676 raise TypeError, "Either end or size must be specified" 677 678 # Now on to the optional bit 679 if 'intlvHighBit' in kwargs: 680 self.intlvHighBit = int(kwargs.pop('intlvHighBit')) 681 if 'intlvBits' in kwargs: 682 self.intlvBits = int(kwargs.pop('intlvBits')) 683 if 'intlvMatch' in kwargs: 684 self.intlvMatch = int(kwargs.pop('intlvMatch')) 685 686 if len(args) == 0: 687 self.start = Addr(kwargs.pop('start')) 688 handle_kwargs(self, kwargs) 689 690 elif len(args) == 1: 691 if kwargs: 692 self.start = Addr(args[0]) 693 handle_kwargs(self, kwargs) 694 elif isinstance(args[0], (list, tuple)): 695 self.start = Addr(args[0][0]) 696 self.end = Addr(args[0][1]) 697 else: 698 self.start = Addr(0) 699 self.end = Addr(args[0]) - 1 700 701 elif len(args) == 2: 702 self.start = Addr(args[0]) 703 self.end = Addr(args[1]) 704 else: 705 raise TypeError, "Too many arguments specified" 706 707 if kwargs: 708 raise TypeError, "Too many keywords: %s" % kwargs.keys() 709 710 def __str__(self): 711 return '%s:%s' % (self.start, self.end) 712 713 def size(self): 714 # Divide the size by the size of the interleaving slice 715 return (long(self.end) - long(self.start) + 1) >> self.intlvBits 716 717 @classmethod 718 def cxx_predecls(cls, code): 719 Addr.cxx_predecls(code) 720 code('#include "base/addr_range.hh"') 721 722 @classmethod 723 def swig_predecls(cls, code): 724 Addr.swig_predecls(code) 725 726 def getValue(self): 727 # Go from the Python class to the wrapped C++ class generated 728 # by swig 729 from m5.internal.range import AddrRange 730 731 return AddrRange(long(self.start), long(self.end), 732 int(self.intlvHighBit), int(self.intlvBits), 733 int(self.intlvMatch)) 734 735# Boolean parameter type. Python doesn't let you subclass bool, since 736# it doesn't want to let you create multiple instances of True and 737# False. Thus this is a little more complicated than String. 738class Bool(ParamValue): 739 cxx_type = 'bool' 740 cmd_line_settable = True 741 742 def __init__(self, value): 743 try: 744 self.value = convert.toBool(value) 745 except TypeError: 746 self.value = bool(value) 747 748 def __call__(self, value): 749 self.__init__(value) 750 return value 751 752 def getValue(self): 753 return bool(self.value) 754 755 def __str__(self): 756 return str(self.value) 757 758 # implement truth value testing for Bool parameters so that these params 759 # evaluate correctly during the python configuration phase 760 def __nonzero__(self): 761 return bool(self.value) 762 763 def ini_str(self): 764 if self.value: 765 return 'true' 766 return 'false' 767 768def IncEthernetAddr(addr, val = 1): 769 bytes = map(lambda x: int(x, 16), addr.split(':')) 770 bytes[5] += val 771 for i in (5, 4, 3, 2, 1): 772 val,rem = divmod(bytes[i], 256) 773 bytes[i] = rem 774 if val == 0: 775 break 776 bytes[i - 1] += val 777 assert(bytes[0] <= 255) 778 return ':'.join(map(lambda x: '%02x' % x, bytes)) 779 780_NextEthernetAddr = "00:90:00:00:00:01" 781def NextEthernetAddr(): 782 global _NextEthernetAddr 783 784 value = _NextEthernetAddr 785 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 786 return value 787 788class EthernetAddr(ParamValue): 789 cxx_type = 'Net::EthAddr' 790 ex_str = "00:90:00:00:00:01" 791 cmd_line_settable = True 792 793 @classmethod 794 def cxx_predecls(cls, code): 795 code('#include "base/inet.hh"') 796 797 @classmethod 798 def swig_predecls(cls, code): 799 code('%include "python/swig/inet.i"') 800 801 def __init__(self, value): 802 if value == NextEthernetAddr: 803 self.value = value 804 return 805 806 if not isinstance(value, str): 807 raise TypeError, "expected an ethernet address and didn't get one" 808 809 bytes = value.split(':') 810 if len(bytes) != 6: 811 raise TypeError, 'invalid ethernet address %s' % value 812 813 for byte in bytes: 814 if not 0 <= int(byte, base=16) <= 0xff: 815 raise TypeError, 'invalid ethernet address %s' % value 816 817 self.value = value 818 819 def __call__(self, value): 820 self.__init__(value) 821 return value 822 823 def unproxy(self, base): 824 if self.value == NextEthernetAddr: 825 return EthernetAddr(self.value()) 826 return self 827 828 def getValue(self): 829 from m5.internal.params import EthAddr 830 return EthAddr(self.value) 831 832 def ini_str(self): 833 return self.value 834 835# When initializing an IpAddress, pass in an existing IpAddress, a string of 836# the form "a.b.c.d", or an integer representing an IP. 837class IpAddress(ParamValue): 838 cxx_type = 'Net::IpAddress' 839 ex_str = "127.0.0.1" 840 cmd_line_settable = True 841 842 @classmethod 843 def cxx_predecls(cls, code): 844 code('#include "base/inet.hh"') 845 846 @classmethod 847 def swig_predecls(cls, code): 848 code('%include "python/swig/inet.i"') 849 850 def __init__(self, value): 851 if isinstance(value, IpAddress): 852 self.ip = value.ip 853 else: 854 try: 855 self.ip = convert.toIpAddress(value) 856 except TypeError: 857 self.ip = long(value) 858 self.verifyIp() 859 860 def __call__(self, value): 861 self.__init__(value) 862 return value 863 864 def __str__(self): 865 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)] 866 return '%d.%d.%d.%d' % tuple(tup) 867 868 def __eq__(self, other): 869 if isinstance(other, IpAddress): 870 return self.ip == other.ip 871 elif isinstance(other, str): 872 try: 873 return self.ip == convert.toIpAddress(other) 874 except: 875 return False 876 else: 877 return self.ip == other 878 879 def __ne__(self, other): 880 return not (self == other) 881 882 def verifyIp(self): 883 if self.ip < 0 or self.ip >= (1 << 32): 884 raise TypeError, "invalid ip address %#08x" % self.ip 885 886 def getValue(self): 887 from m5.internal.params import IpAddress 888 return IpAddress(self.ip) 889 890# When initializing an IpNetmask, pass in an existing IpNetmask, a string of 891# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as 892# positional or keyword arguments. 893class IpNetmask(IpAddress): 894 cxx_type = 'Net::IpNetmask' 895 ex_str = "127.0.0.0/24" 896 cmd_line_settable = True 897 898 @classmethod 899 def cxx_predecls(cls, code): 900 code('#include "base/inet.hh"') 901 902 @classmethod 903 def swig_predecls(cls, code): 904 code('%include "python/swig/inet.i"') 905 906 def __init__(self, *args, **kwargs): 907 def handle_kwarg(self, kwargs, key, elseVal = None): 908 if key in kwargs: 909 setattr(self, key, kwargs.pop(key)) 910 elif elseVal: 911 setattr(self, key, elseVal) 912 else: 913 raise TypeError, "No value set for %s" % key 914 915 if len(args) == 0: 916 handle_kwarg(self, kwargs, 'ip') 917 handle_kwarg(self, kwargs, 'netmask') 918 919 elif len(args) == 1: 920 if kwargs: 921 if not 'ip' in kwargs and not 'netmask' in kwargs: 922 raise TypeError, "Invalid arguments" 923 handle_kwarg(self, kwargs, 'ip', args[0]) 924 handle_kwarg(self, kwargs, 'netmask', args[0]) 925 elif isinstance(args[0], IpNetmask): 926 self.ip = args[0].ip 927 self.netmask = args[0].netmask 928 else: 929 (self.ip, self.netmask) = convert.toIpNetmask(args[0]) 930 931 elif len(args) == 2: 932 self.ip = args[0] 933 self.netmask = args[1] 934 else: 935 raise TypeError, "Too many arguments specified" 936 937 if kwargs: 938 raise TypeError, "Too many keywords: %s" % kwargs.keys() 939 940 self.verify() 941 942 def __call__(self, value): 943 self.__init__(value) 944 return value 945 946 def __str__(self): 947 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask) 948 949 def __eq__(self, other): 950 if isinstance(other, IpNetmask): 951 return self.ip == other.ip and self.netmask == other.netmask 952 elif isinstance(other, str): 953 try: 954 return (self.ip, self.netmask) == convert.toIpNetmask(other) 955 except: 956 return False 957 else: 958 return False 959 960 def verify(self): 961 self.verifyIp() 962 if self.netmask < 0 or self.netmask > 32: 963 raise TypeError, "invalid netmask %d" % netmask 964 965 def getValue(self): 966 from m5.internal.params import IpNetmask 967 return IpNetmask(self.ip, self.netmask) 968 969# When initializing an IpWithPort, pass in an existing IpWithPort, a string of 970# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments. 971class IpWithPort(IpAddress): 972 cxx_type = 'Net::IpWithPort' 973 ex_str = "127.0.0.1:80" 974 cmd_line_settable = True 975 976 @classmethod 977 def cxx_predecls(cls, code): 978 code('#include "base/inet.hh"') 979 980 @classmethod 981 def swig_predecls(cls, code): 982 code('%include "python/swig/inet.i"') 983 984 def __init__(self, *args, **kwargs): 985 def handle_kwarg(self, kwargs, key, elseVal = None): 986 if key in kwargs: 987 setattr(self, key, kwargs.pop(key)) 988 elif elseVal: 989 setattr(self, key, elseVal) 990 else: 991 raise TypeError, "No value set for %s" % key 992 993 if len(args) == 0: 994 handle_kwarg(self, kwargs, 'ip') 995 handle_kwarg(self, kwargs, 'port') 996 997 elif len(args) == 1: 998 if kwargs: 999 if not 'ip' in kwargs and not 'port' in kwargs: 1000 raise TypeError, "Invalid arguments" 1001 handle_kwarg(self, kwargs, 'ip', args[0]) 1002 handle_kwarg(self, kwargs, 'port', args[0]) 1003 elif isinstance(args[0], IpWithPort): 1004 self.ip = args[0].ip 1005 self.port = args[0].port 1006 else: 1007 (self.ip, self.port) = convert.toIpWithPort(args[0]) 1008 1009 elif len(args) == 2: 1010 self.ip = args[0] 1011 self.port = args[1] 1012 else: 1013 raise TypeError, "Too many arguments specified" 1014 1015 if kwargs: 1016 raise TypeError, "Too many keywords: %s" % kwargs.keys() 1017 1018 self.verify() 1019 1020 def __call__(self, value): 1021 self.__init__(value) 1022 return value 1023 1024 def __str__(self): 1025 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port) 1026 1027 def __eq__(self, other): 1028 if isinstance(other, IpWithPort): 1029 return self.ip == other.ip and self.port == other.port 1030 elif isinstance(other, str): 1031 try: 1032 return (self.ip, self.port) == convert.toIpWithPort(other) 1033 except: 1034 return False 1035 else: 1036 return False 1037 1038 def verify(self): 1039 self.verifyIp() 1040 if self.port < 0 or self.port > 0xffff: 1041 raise TypeError, "invalid port %d" % self.port 1042 1043 def getValue(self): 1044 from m5.internal.params import IpWithPort 1045 return IpWithPort(self.ip, self.port) 1046 1047time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 1048 "%a %b %d %H:%M:%S %Z %Y", 1049 "%Y/%m/%d %H:%M:%S", 1050 "%Y/%m/%d %H:%M", 1051 "%Y/%m/%d", 1052 "%m/%d/%Y %H:%M:%S", 1053 "%m/%d/%Y %H:%M", 1054 "%m/%d/%Y", 1055 "%m/%d/%y %H:%M:%S", 1056 "%m/%d/%y %H:%M", 1057 "%m/%d/%y"] 1058 1059 1060def parse_time(value): 1061 from time import gmtime, strptime, struct_time, time 1062 from datetime import datetime, date 1063 1064 if isinstance(value, struct_time): 1065 return value 1066 1067 if isinstance(value, (int, long)): 1068 return gmtime(value) 1069 1070 if isinstance(value, (datetime, date)): 1071 return value.timetuple() 1072 1073 if isinstance(value, str): 1074 if value in ('Now', 'Today'): 1075 return time.gmtime(time.time()) 1076 1077 for format in time_formats: 1078 try: 1079 return strptime(value, format) 1080 except ValueError: 1081 pass 1082 1083 raise ValueError, "Could not parse '%s' as a time" % value 1084 1085class Time(ParamValue): 1086 cxx_type = 'tm' 1087 1088 @classmethod 1089 def cxx_predecls(cls, code): 1090 code('#include <time.h>') 1091 1092 @classmethod 1093 def swig_predecls(cls, code): 1094 code('%include "python/swig/time.i"') 1095 1096 def __init__(self, value): 1097 self.value = parse_time(value) 1098 1099 def __call__(self, value): 1100 self.__init__(value) 1101 return value 1102 1103 def getValue(self): 1104 from m5.internal.params import tm 1105 1106 c_time = tm() 1107 py_time = self.value 1108 1109 # UNIX is years since 1900 1110 c_time.tm_year = py_time.tm_year - 1900; 1111 1112 # Python starts at 1, UNIX starts at 0 1113 c_time.tm_mon = py_time.tm_mon - 1; 1114 c_time.tm_mday = py_time.tm_mday; 1115 c_time.tm_hour = py_time.tm_hour; 1116 c_time.tm_min = py_time.tm_min; 1117 c_time.tm_sec = py_time.tm_sec; 1118 1119 # Python has 0 as Monday, UNIX is 0 as sunday 1120 c_time.tm_wday = py_time.tm_wday + 1 1121 if c_time.tm_wday > 6: 1122 c_time.tm_wday -= 7; 1123 1124 # Python starts at 1, Unix starts at 0 1125 c_time.tm_yday = py_time.tm_yday - 1; 1126 1127 return c_time 1128 1129 def __str__(self): 1130 return time.asctime(self.value) 1131 1132 def ini_str(self): 1133 return str(self) 1134 1135 def get_config_as_dict(self): 1136 return str(self) 1137 1138# Enumerated types are a little more complex. The user specifies the 1139# type as Enum(foo) where foo is either a list or dictionary of 1140# alternatives (typically strings, but not necessarily so). (In the 1141# long run, the integer value of the parameter will be the list index 1142# or the corresponding dictionary value. For now, since we only check 1143# that the alternative is valid and then spit it into a .ini file, 1144# there's not much point in using the dictionary.) 1145 1146# What Enum() must do is generate a new type encapsulating the 1147# provided list/dictionary so that specific values of the parameter 1148# can be instances of that type. We define two hidden internal 1149# classes (_ListEnum and _DictEnum) to serve as base classes, then 1150# derive the new type from the appropriate base class on the fly. 1151 1152allEnums = {} 1153# Metaclass for Enum types 1154class MetaEnum(MetaParamValue): 1155 def __new__(mcls, name, bases, dict): 1156 assert name not in allEnums 1157 1158 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 1159 allEnums[name] = cls 1160 return cls 1161 1162 def __init__(cls, name, bases, init_dict): 1163 if init_dict.has_key('map'): 1164 if not isinstance(cls.map, dict): 1165 raise TypeError, "Enum-derived class attribute 'map' " \ 1166 "must be of type dict" 1167 # build list of value strings from map 1168 cls.vals = cls.map.keys() 1169 cls.vals.sort() 1170 elif init_dict.has_key('vals'): 1171 if not isinstance(cls.vals, list): 1172 raise TypeError, "Enum-derived class attribute 'vals' " \ 1173 "must be of type list" 1174 # build string->value map from vals sequence 1175 cls.map = {} 1176 for idx,val in enumerate(cls.vals): 1177 cls.map[val] = idx 1178 else: 1179 raise TypeError, "Enum-derived class must define "\ 1180 "attribute 'map' or 'vals'" 1181 1182 cls.cxx_type = 'Enums::%s' % name 1183 1184 super(MetaEnum, cls).__init__(name, bases, init_dict) 1185 1186 # Generate C++ class declaration for this enum type. 1187 # Note that we wrap the enum in a class/struct to act as a namespace, 1188 # so that the enum strings can be brief w/o worrying about collisions. 1189 def cxx_decl(cls, code): 1190 wrapper_name = cls.wrapper_name 1191 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace' 1192 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1193 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name) 1194 1195 code('''\ 1196#ifndef $idem_macro 1197#define $idem_macro 1198 1199$wrapper $wrapper_name { 1200 enum $name { 1201''') 1202 code.indent(2) 1203 for val in cls.vals: 1204 code('$val = ${{cls.map[val]}},') 1205 code('Num_$name = ${{len(cls.vals)}}') 1206 code.dedent(2) 1207 code(' };') 1208 1209 if cls.wrapper_is_struct: 1210 code(' static const char *${name}Strings[Num_${name}];') 1211 code('};') 1212 else: 1213 code('extern const char *${name}Strings[Num_${name}];') 1214 code('}') 1215 1216 code() 1217 code('#endif // $idem_macro') 1218 1219 def cxx_def(cls, code): 1220 wrapper_name = cls.wrapper_name 1221 file_name = cls.__name__ 1222 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1223 1224 code('#include "enums/$file_name.hh"') 1225 if cls.wrapper_is_struct: 1226 code('const char *${wrapper_name}::${name}Strings' 1227 '[Num_${name}] =') 1228 else: 1229 code('namespace Enums {') 1230 code.indent(1) 1231 code(' const char *${name}Strings[Num_${name}] =') 1232 1233 code('{') 1234 code.indent(1) 1235 for val in cls.vals: 1236 code('"$val",') 1237 code.dedent(1) 1238 code('};') 1239 1240 if not cls.wrapper_is_struct: 1241 code('} // namespace $wrapper_name') 1242 code.dedent(1) 1243 1244 def swig_decl(cls, code): 1245 name = cls.__name__ 1246 code('''\ 1247%module(package="m5.internal") enum_$name 1248 1249%{ 1250#include "enums/$name.hh" 1251%} 1252 1253%include "enums/$name.hh" 1254''') 1255 1256 1257# Base class for enum types. 1258class Enum(ParamValue): 1259 __metaclass__ = MetaEnum 1260 vals = [] 1261 cmd_line_settable = True 1262 1263 # The name of the wrapping namespace or struct 1264 wrapper_name = 'Enums' 1265 1266 # If true, the enum is wrapped in a struct rather than a namespace 1267 wrapper_is_struct = False 1268 1269 # If not None, use this as the enum name rather than this class name 1270 enum_name = None 1271 1272 def __init__(self, value): 1273 if value not in self.map: 1274 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 1275 % (value, self.vals) 1276 self.value = value 1277 1278 def __call__(self, value): 1279 self.__init__(value) 1280 return value 1281 1282 @classmethod 1283 def cxx_predecls(cls, code): 1284 code('#include "enums/$0.hh"', cls.__name__) 1285 1286 @classmethod 1287 def swig_predecls(cls, code): 1288 code('%import "python/m5/internal/enum_$0.i"', cls.__name__) 1289 1290 def getValue(self): 1291 return int(self.map[self.value]) 1292 1293 def __str__(self): 1294 return self.value 1295 1296# how big does a rounding error need to be before we warn about it? 1297frequency_tolerance = 0.001 # 0.1% 1298 1299class TickParamValue(NumericParamValue): 1300 cxx_type = 'Tick' 1301 ex_str = "1MHz" 1302 cmd_line_settable = True 1303 1304 @classmethod 1305 def cxx_predecls(cls, code): 1306 code('#include "base/types.hh"') 1307 1308 @classmethod 1309 def swig_predecls(cls, code): 1310 code('%import "stdint.i"') 1311 code('%import "base/types.hh"') 1312 1313 def __call__(self, value): 1314 self.__init__(value) 1315 return value 1316 1317 def getValue(self): 1318 return long(self.value) 1319 1320class Latency(TickParamValue): 1321 ex_str = "100ns" 1322 1323 def __init__(self, value): 1324 if isinstance(value, (Latency, Clock)): 1325 self.ticks = value.ticks 1326 self.value = value.value 1327 elif isinstance(value, Frequency): 1328 self.ticks = value.ticks 1329 self.value = 1.0 / value.value 1330 elif value.endswith('t'): 1331 self.ticks = True 1332 self.value = int(value[:-1]) 1333 else: 1334 self.ticks = False 1335 self.value = convert.toLatency(value) 1336 1337 def __call__(self, value): 1338 self.__init__(value) 1339 return value 1340 1341 def __getattr__(self, attr): 1342 if attr in ('latency', 'period'): 1343 return self 1344 if attr == 'frequency': 1345 return Frequency(self) 1346 raise AttributeError, "Latency object has no attribute '%s'" % attr 1347 1348 def getValue(self): 1349 if self.ticks or self.value == 0: 1350 value = self.value 1351 else: 1352 value = ticks.fromSeconds(self.value) 1353 return long(value) 1354 1355 # convert latency to ticks 1356 def ini_str(self): 1357 return '%d' % self.getValue() 1358 1359class Frequency(TickParamValue): 1360 ex_str = "1GHz" 1361 1362 def __init__(self, value): 1363 if isinstance(value, (Latency, Clock)): 1364 if value.value == 0: 1365 self.value = 0 1366 else: 1367 self.value = 1.0 / value.value 1368 self.ticks = value.ticks 1369 elif isinstance(value, Frequency): 1370 self.value = value.value 1371 self.ticks = value.ticks 1372 else: 1373 self.ticks = False 1374 self.value = convert.toFrequency(value) 1375 1376 def __call__(self, value): 1377 self.__init__(value) 1378 return value 1379 1380 def __getattr__(self, attr): 1381 if attr == 'frequency': 1382 return self 1383 if attr in ('latency', 'period'): 1384 return Latency(self) 1385 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1386 1387 # convert latency to ticks 1388 def getValue(self): 1389 if self.ticks or self.value == 0: 1390 value = self.value 1391 else: 1392 value = ticks.fromSeconds(1.0 / self.value) 1393 return long(value) 1394 1395 def ini_str(self): 1396 return '%d' % self.getValue() 1397 1398# A generic Frequency and/or Latency value. Value is stored as a 1399# latency, just like Latency and Frequency. 1400class Clock(TickParamValue): 1401 def __init__(self, value): 1402 if isinstance(value, (Latency, Clock)): 1403 self.ticks = value.ticks 1404 self.value = value.value 1405 elif isinstance(value, Frequency): 1406 self.ticks = value.ticks 1407 self.value = 1.0 / value.value 1408 elif value.endswith('t'): 1409 self.ticks = True 1410 self.value = int(value[:-1]) 1411 else: 1412 self.ticks = False 1413 self.value = convert.anyToLatency(value) 1414 1415 def __call__(self, value): 1416 self.__init__(value) 1417 return value 1418 1419 def __str__(self): 1420 return "%s" % Latency(self) 1421 1422 def __getattr__(self, attr): 1423 if attr == 'frequency': 1424 return Frequency(self) 1425 if attr in ('latency', 'period'): 1426 return Latency(self) 1427 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1428 1429 def getValue(self): 1430 return self.period.getValue() 1431 1432 def ini_str(self): 1433 return self.period.ini_str() 1434 1435class Voltage(float,ParamValue): 1436 cxx_type = 'double' 1437 ex_str = "1V" 1438 cmd_line_settable = False 1439 1440 def __new__(cls, value): 1441 # convert to voltage 1442 val = convert.toVoltage(value) 1443 return super(cls, Voltage).__new__(cls, val) 1444 1445 def __call__(self, value): 1446 val = convert.toVoltage(value) 1447 self.__init__(val) 1448 return value 1449 1450 def __str__(self): 1451 return str(self.getValue()) 1452 1453 def getValue(self): 1454 value = float(self) 1455 return value 1456 1457 def ini_str(self): 1458 return '%f' % self.getValue() 1459 1460class NetworkBandwidth(float,ParamValue): 1461 cxx_type = 'float' 1462 ex_str = "1Gbps" 1463 cmd_line_settable = True 1464 1465 def __new__(cls, value): 1466 # convert to bits per second 1467 val = convert.toNetworkBandwidth(value) 1468 return super(cls, NetworkBandwidth).__new__(cls, val) 1469 1470 def __str__(self): 1471 return str(self.val) 1472 1473 def __call__(self, value): 1474 val = convert.toNetworkBandwidth(value) 1475 self.__init__(val) 1476 return value 1477 1478 def getValue(self): 1479 # convert to seconds per byte 1480 value = 8.0 / float(self) 1481 # convert to ticks per byte 1482 value = ticks.fromSeconds(value) 1483 return float(value) 1484 1485 def ini_str(self): 1486 return '%f' % self.getValue() 1487 1488class MemoryBandwidth(float,ParamValue): 1489 cxx_type = 'float' 1490 ex_str = "1GB/s" 1491 cmd_line_settable = True 1492 1493 def __new__(cls, value): 1494 # convert to bytes per second 1495 val = convert.toMemoryBandwidth(value) 1496 return super(cls, MemoryBandwidth).__new__(cls, val) 1497 1498 def __call__(self, value): 1499 val = convert.toMemoryBandwidth(value) 1500 self.__init__(val) 1501 return value 1502 1503 def getValue(self): 1504 # convert to seconds per byte 1505 value = float(self) 1506 if value: 1507 value = 1.0 / float(self) 1508 # convert to ticks per byte 1509 value = ticks.fromSeconds(value) 1510 return float(value) 1511 1512 def ini_str(self): 1513 return '%f' % self.getValue() 1514 1515# 1516# "Constants"... handy aliases for various values. 1517# 1518 1519# Special class for NULL pointers. Note the special check in 1520# make_param_value() above that lets these be assigned where a 1521# SimObject is required. 1522# only one copy of a particular node 1523class NullSimObject(object): 1524 __metaclass__ = Singleton 1525 1526 def __call__(cls): 1527 return cls 1528 1529 def _instantiate(self, parent = None, path = ''): 1530 pass 1531 1532 def ini_str(self): 1533 return 'Null' 1534 1535 def unproxy(self, base): 1536 return self 1537 1538 def set_path(self, parent, name): 1539 pass 1540 1541 def __str__(self): 1542 return 'Null' 1543 1544 def getValue(self): 1545 return None 1546 1547# The only instance you'll ever need... 1548NULL = NullSimObject() 1549 1550def isNullPointer(value): 1551 return isinstance(value, NullSimObject) 1552 1553# Some memory range specifications use this as a default upper bound. 1554MaxAddr = Addr.max 1555MaxTick = Tick.max 1556AllMemory = AddrRange(0, MaxAddr) 1557 1558 1559##################################################################### 1560# 1561# Port objects 1562# 1563# Ports are used to interconnect objects in the memory system. 1564# 1565##################################################################### 1566 1567# Port reference: encapsulates a reference to a particular port on a 1568# particular SimObject. 1569class PortRef(object): 1570 def __init__(self, simobj, name, role): 1571 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1572 self.simobj = simobj 1573 self.name = name 1574 self.role = role 1575 self.peer = None # not associated with another port yet 1576 self.ccConnected = False # C++ port connection done? 1577 self.index = -1 # always -1 for non-vector ports 1578 1579 def __str__(self): 1580 return '%s.%s' % (self.simobj, self.name) 1581 1582 def __len__(self): 1583 # Return the number of connected ports, i.e. 0 is we have no 1584 # peer and 1 if we do. 1585 return int(self.peer != None) 1586 1587 # for config.ini, print peer's name (not ours) 1588 def ini_str(self): 1589 return str(self.peer) 1590 1591 # for config.json 1592 def get_config_as_dict(self): 1593 return {'role' : self.role, 'peer' : str(self.peer)} 1594 1595 def __getattr__(self, attr): 1596 if attr == 'peerObj': 1597 # shorthand for proxies 1598 return self.peer.simobj 1599 raise AttributeError, "'%s' object has no attribute '%s'" % \ 1600 (self.__class__.__name__, attr) 1601 1602 # Full connection is symmetric (both ways). Called via 1603 # SimObject.__setattr__ as a result of a port assignment, e.g., 1604 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 1605 # e.g., "obj1.portA[3] = obj2.portB". 1606 def connect(self, other): 1607 if isinstance(other, VectorPortRef): 1608 # reference to plain VectorPort is implicit append 1609 other = other._get_next() 1610 if self.peer and not proxy.isproxy(self.peer): 1611 fatal("Port %s is already connected to %s, cannot connect %s\n", 1612 self, self.peer, other); 1613 self.peer = other 1614 if proxy.isproxy(other): 1615 other.set_param_desc(PortParamDesc()) 1616 elif isinstance(other, PortRef): 1617 if other.peer is not self: 1618 other.connect(self) 1619 else: 1620 raise TypeError, \ 1621 "assigning non-port reference '%s' to port '%s'" \ 1622 % (other, self) 1623 1624 # Allow a master/slave port pair to be spliced between 1625 # a port and its connected peer. Useful operation for connecting 1626 # instrumentation structures into a system when it is necessary 1627 # to connect the instrumentation after the full system has been 1628 # constructed. 1629 def splice(self, new_master_peer, new_slave_peer): 1630 if self.peer and not proxy.isproxy(self.peer): 1631 if isinstance(new_master_peer, PortRef) and \ 1632 isinstance(new_slave_peer, PortRef): 1633 old_peer = self.peer 1634 if self.role == 'SLAVE': 1635 self.peer = new_master_peer 1636 old_peer.peer = new_slave_peer 1637 new_master_peer.connect(self) 1638 new_slave_peer.connect(old_peer) 1639 elif self.role == 'MASTER': 1640 self.peer = new_slave_peer 1641 old_peer.peer = new_master_peer 1642 new_slave_peer.connect(self) 1643 new_master_peer.connect(old_peer) 1644 else: 1645 panic("Port %s has unknown role, "+\ 1646 "cannot splice in new peers\n", self) 1647 else: 1648 raise TypeError, \ 1649 "Splicing non-port references '%s','%s' to port '%s'"\ 1650 % (new_peer, peers_new_peer, self) 1651 else: 1652 fatal("Port %s not connected, cannot splice in new peers\n", self) 1653 1654 def clone(self, simobj, memo): 1655 if memo.has_key(self): 1656 return memo[self] 1657 newRef = copy.copy(self) 1658 memo[self] = newRef 1659 newRef.simobj = simobj 1660 assert(isSimObject(newRef.simobj)) 1661 if self.peer and not proxy.isproxy(self.peer): 1662 peerObj = self.peer.simobj(_memo=memo) 1663 newRef.peer = self.peer.clone(peerObj, memo) 1664 assert(not isinstance(newRef.peer, VectorPortRef)) 1665 return newRef 1666 1667 def unproxy(self, simobj): 1668 assert(simobj is self.simobj) 1669 if proxy.isproxy(self.peer): 1670 try: 1671 realPeer = self.peer.unproxy(self.simobj) 1672 except: 1673 print "Error in unproxying port '%s' of %s" % \ 1674 (self.name, self.simobj.path()) 1675 raise 1676 self.connect(realPeer) 1677 1678 # Call C++ to create corresponding port connection between C++ objects 1679 def ccConnect(self): 1680 from m5.internal.pyobject import connectPorts 1681 1682 if self.role == 'SLAVE': 1683 # do nothing and let the master take care of it 1684 return 1685 1686 if self.ccConnected: # already done this 1687 return 1688 peer = self.peer 1689 if not self.peer: # nothing to connect to 1690 return 1691 1692 # check that we connect a master to a slave 1693 if self.role == peer.role: 1694 raise TypeError, \ 1695 "cannot connect '%s' and '%s' due to identical role '%s'" \ 1696 % (peer, self, self.role) 1697 1698 try: 1699 # self is always the master and peer the slave 1700 connectPorts(self.simobj.getCCObject(), self.name, self.index, 1701 peer.simobj.getCCObject(), peer.name, peer.index) 1702 except: 1703 print "Error connecting port %s.%s to %s.%s" % \ 1704 (self.simobj.path(), self.name, 1705 peer.simobj.path(), peer.name) 1706 raise 1707 self.ccConnected = True 1708 peer.ccConnected = True 1709 1710# A reference to an individual element of a VectorPort... much like a 1711# PortRef, but has an index. 1712class VectorPortElementRef(PortRef): 1713 def __init__(self, simobj, name, role, index): 1714 PortRef.__init__(self, simobj, name, role) 1715 self.index = index 1716 1717 def __str__(self): 1718 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1719 1720# A reference to a complete vector-valued port (not just a single element). 1721# Can be indexed to retrieve individual VectorPortElementRef instances. 1722class VectorPortRef(object): 1723 def __init__(self, simobj, name, role): 1724 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1725 self.simobj = simobj 1726 self.name = name 1727 self.role = role 1728 self.elements = [] 1729 1730 def __str__(self): 1731 return '%s.%s[:]' % (self.simobj, self.name) 1732 1733 def __len__(self): 1734 # Return the number of connected peers, corresponding the the 1735 # length of the elements. 1736 return len(self.elements) 1737 1738 # for config.ini, print peer's name (not ours) 1739 def ini_str(self): 1740 return ' '.join([el.ini_str() for el in self.elements]) 1741 1742 # for config.json 1743 def get_config_as_dict(self): 1744 return {'role' : self.role, 1745 'peer' : [el.ini_str() for el in self.elements]} 1746 1747 def __getitem__(self, key): 1748 if not isinstance(key, int): 1749 raise TypeError, "VectorPort index must be integer" 1750 if key >= len(self.elements): 1751 # need to extend list 1752 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i) 1753 for i in range(len(self.elements), key+1)] 1754 self.elements.extend(ext) 1755 return self.elements[key] 1756 1757 def _get_next(self): 1758 return self[len(self.elements)] 1759 1760 def __setitem__(self, key, value): 1761 if not isinstance(key, int): 1762 raise TypeError, "VectorPort index must be integer" 1763 self[key].connect(value) 1764 1765 def connect(self, other): 1766 if isinstance(other, (list, tuple)): 1767 # Assign list of port refs to vector port. 1768 # For now, append them... not sure if that's the right semantics 1769 # or if it should replace the current vector. 1770 for ref in other: 1771 self._get_next().connect(ref) 1772 else: 1773 # scalar assignment to plain VectorPort is implicit append 1774 self._get_next().connect(other) 1775 1776 def clone(self, simobj, memo): 1777 if memo.has_key(self): 1778 return memo[self] 1779 newRef = copy.copy(self) 1780 memo[self] = newRef 1781 newRef.simobj = simobj 1782 assert(isSimObject(newRef.simobj)) 1783 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 1784 return newRef 1785 1786 def unproxy(self, simobj): 1787 [el.unproxy(simobj) for el in self.elements] 1788 1789 def ccConnect(self): 1790 [el.ccConnect() for el in self.elements] 1791 1792# Port description object. Like a ParamDesc object, this represents a 1793# logical port in the SimObject class, not a particular port on a 1794# SimObject instance. The latter are represented by PortRef objects. 1795class Port(object): 1796 # Generate a PortRef for this port on the given SimObject with the 1797 # given name 1798 def makeRef(self, simobj): 1799 return PortRef(simobj, self.name, self.role) 1800 1801 # Connect an instance of this port (on the given SimObject with 1802 # the given name) with the port described by the supplied PortRef 1803 def connect(self, simobj, ref): 1804 self.makeRef(simobj).connect(ref) 1805 1806 # No need for any pre-declarations at the moment as we merely rely 1807 # on an unsigned int. 1808 def cxx_predecls(self, code): 1809 pass 1810 1811 # Declare an unsigned int with the same name as the port, that 1812 # will eventually hold the number of connected ports (and thus the 1813 # number of elements for a VectorPort). 1814 def cxx_decl(self, code): 1815 code('unsigned int port_${{self.name}}_connection_count;') 1816 1817class MasterPort(Port): 1818 # MasterPort("description") 1819 def __init__(self, *args): 1820 if len(args) == 1: 1821 self.desc = args[0] 1822 self.role = 'MASTER' 1823 else: 1824 raise TypeError, 'wrong number of arguments' 1825 1826class SlavePort(Port): 1827 # SlavePort("description") 1828 def __init__(self, *args): 1829 if len(args) == 1: 1830 self.desc = args[0] 1831 self.role = 'SLAVE' 1832 else: 1833 raise TypeError, 'wrong number of arguments' 1834 1835# VectorPort description object. Like Port, but represents a vector 1836# of connections (e.g., as on a Bus). 1837class VectorPort(Port): 1838 def __init__(self, *args): 1839 self.isVec = True 1840 1841 def makeRef(self, simobj): 1842 return VectorPortRef(simobj, self.name, self.role) 1843 1844class VectorMasterPort(VectorPort): 1845 # VectorMasterPort("description") 1846 def __init__(self, *args): 1847 if len(args) == 1: 1848 self.desc = args[0] 1849 self.role = 'MASTER' 1850 VectorPort.__init__(self, *args) 1851 else: 1852 raise TypeError, 'wrong number of arguments' 1853 1854class VectorSlavePort(VectorPort): 1855 # VectorSlavePort("description") 1856 def __init__(self, *args): 1857 if len(args) == 1: 1858 self.desc = args[0] 1859 self.role = 'SLAVE' 1860 VectorPort.__init__(self, *args) 1861 else: 1862 raise TypeError, 'wrong number of arguments' 1863 1864# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 1865# proxy objects (via set_param_desc()) so that proxy error messages 1866# make sense. 1867class PortParamDesc(object): 1868 __metaclass__ = Singleton 1869 1870 ptype_str = 'Port' 1871 ptype = Port 1872 1873baseEnums = allEnums.copy() 1874baseParams = allParams.copy() 1875 1876def clear(): 1877 global allEnums, allParams 1878 1879 allEnums = baseEnums.copy() 1880 allParams = baseParams.copy() 1881 1882__all__ = ['Param', 'VectorParam', 1883 'Enum', 'Bool', 'String', 'Float', 1884 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1885 'Int32', 'UInt32', 'Int64', 'UInt64', 1886 'Counter', 'Addr', 'Tick', 'Percent', 1887 'TcpPort', 'UdpPort', 'EthernetAddr', 1888 'IpAddress', 'IpNetmask', 'IpWithPort', 1889 'MemorySize', 'MemorySize32', 1890 'Latency', 'Frequency', 'Clock', 'Voltage', 1891 'NetworkBandwidth', 'MemoryBandwidth', 1892 'AddrRange', 1893 'MaxAddr', 'MaxTick', 'AllMemory', 1894 'Time', 1895 'NextEthernetAddr', 'NULL', 1896 'MasterPort', 'SlavePort', 1897 'VectorMasterPort', 'VectorSlavePort'] 1898 1899import SimObject 1900