params.py revision 13783
110259SAndrew.Bardsley@arm.com# Copyright (c) 2012-2014, 2017, 2018 ARM Limited 210259SAndrew.Bardsley@arm.com# All rights reserved. 310259SAndrew.Bardsley@arm.com# 410259SAndrew.Bardsley@arm.com# The license below extends only to copyright in the software and shall 510259SAndrew.Bardsley@arm.com# not be construed as granting a license to any other intellectual 610259SAndrew.Bardsley@arm.com# property including but not limited to intellectual property relating 710259SAndrew.Bardsley@arm.com# to a hardware implementation of the functionality of the software 810259SAndrew.Bardsley@arm.com# licensed hereunder. You may use the software subject to the license 910259SAndrew.Bardsley@arm.com# terms below provided that you ensure that this notice is replicated 1010259SAndrew.Bardsley@arm.com# unmodified and in its entirety in all distributions of the software, 1110259SAndrew.Bardsley@arm.com# modified or unmodified, in source code or in binary form. 1210259SAndrew.Bardsley@arm.com# 1310259SAndrew.Bardsley@arm.com# Copyright (c) 2004-2006 The Regents of The University of Michigan 1410259SAndrew.Bardsley@arm.com# Copyright (c) 2010-2011 Advanced Micro Devices, Inc. 1510259SAndrew.Bardsley@arm.com# All rights reserved. 1610259SAndrew.Bardsley@arm.com# 1710259SAndrew.Bardsley@arm.com# Redistribution and use in source and binary forms, with or without 1810259SAndrew.Bardsley@arm.com# modification, are permitted provided that the following conditions are 1910259SAndrew.Bardsley@arm.com# met: redistributions of source code must retain the above copyright 2010259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer; 2110259SAndrew.Bardsley@arm.com# redistributions in binary form must reproduce the above copyright 2210259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer in the 2310259SAndrew.Bardsley@arm.com# documentation and/or other materials provided with the distribution; 2410259SAndrew.Bardsley@arm.com# neither the name of the copyright holders nor the names of its 2510259SAndrew.Bardsley@arm.com# contributors may be used to endorse or promote products derived from 2610259SAndrew.Bardsley@arm.com# this software without specific prior written permission. 2710259SAndrew.Bardsley@arm.com# 2810259SAndrew.Bardsley@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2910259SAndrew.Bardsley@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3010259SAndrew.Bardsley@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3110259SAndrew.Bardsley@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3210259SAndrew.Bardsley@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3310259SAndrew.Bardsley@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3410259SAndrew.Bardsley@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3510259SAndrew.Bardsley@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3610259SAndrew.Bardsley@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3710259SAndrew.Bardsley@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3810259SAndrew.Bardsley@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3910259SAndrew.Bardsley@arm.com# 4010259SAndrew.Bardsley@arm.com# Authors: Steve Reinhardt 4110259SAndrew.Bardsley@arm.com# Nathan Binkert 4210259SAndrew.Bardsley@arm.com# Gabe Black 4310259SAndrew.Bardsley@arm.com# Andreas Hansson 4410259SAndrew.Bardsley@arm.com 4510259SAndrew.Bardsley@arm.com##################################################################### 4610259SAndrew.Bardsley@arm.com# 4710259SAndrew.Bardsley@arm.com# Parameter description classes 4810259SAndrew.Bardsley@arm.com# 4910259SAndrew.Bardsley@arm.com# The _params dictionary in each class maps parameter names to either 5010259SAndrew.Bardsley@arm.com# a Param or a VectorParam object. These objects contain the 5110259SAndrew.Bardsley@arm.com# parameter description string, the parameter type, and the default 5210259SAndrew.Bardsley@arm.com# value (if any). The convert() method on these objects is used to 5310259SAndrew.Bardsley@arm.com# force whatever value is assigned to the parameter to the appropriate 5410259SAndrew.Bardsley@arm.com# type. 5510259SAndrew.Bardsley@arm.com# 5610259SAndrew.Bardsley@arm.com# Note that the default values are loaded into the class's attribute 5710259SAndrew.Bardsley@arm.com# space when the parameter dictionary is initialized (in 5810259SAndrew.Bardsley@arm.com# MetaSimObject._new_param()); after that point they aren't used. 5910259SAndrew.Bardsley@arm.com# 6010259SAndrew.Bardsley@arm.com##################################################################### 6110259SAndrew.Bardsley@arm.com 6210259SAndrew.Bardsley@arm.comfrom __future__ import print_function 6310259SAndrew.Bardsley@arm.comimport six 6410259SAndrew.Bardsley@arm.comif six.PY3: 6510259SAndrew.Bardsley@arm.com long = int 6610259SAndrew.Bardsley@arm.com 6710259SAndrew.Bardsley@arm.comimport copy 6810259SAndrew.Bardsley@arm.comimport datetime 6910259SAndrew.Bardsley@arm.comimport re 7010259SAndrew.Bardsley@arm.comimport sys 7110259SAndrew.Bardsley@arm.comimport time 7210259SAndrew.Bardsley@arm.comimport math 7310259SAndrew.Bardsley@arm.com 7410259SAndrew.Bardsley@arm.comfrom . import proxy 7510259SAndrew.Bardsley@arm.comfrom . import ticks 7610259SAndrew.Bardsley@arm.comfrom .util import * 7710259SAndrew.Bardsley@arm.com 7810259SAndrew.Bardsley@arm.comdef isSimObject(*args, **kwargs): 7910259SAndrew.Bardsley@arm.com from . import SimObject 8010259SAndrew.Bardsley@arm.com return SimObject.isSimObject(*args, **kwargs) 8110259SAndrew.Bardsley@arm.com 8210259SAndrew.Bardsley@arm.comdef isSimObjectSequence(*args, **kwargs): 8310259SAndrew.Bardsley@arm.com from . import SimObject 8410259SAndrew.Bardsley@arm.com return SimObject.isSimObjectSequence(*args, **kwargs) 8510259SAndrew.Bardsley@arm.com 8610259SAndrew.Bardsley@arm.comdef isSimObjectClass(*args, **kwargs): 8710259SAndrew.Bardsley@arm.com from . import SimObject 8810259SAndrew.Bardsley@arm.com return SimObject.isSimObjectClass(*args, **kwargs) 8910259SAndrew.Bardsley@arm.com 9010259SAndrew.Bardsley@arm.comallParams = {} 9110259SAndrew.Bardsley@arm.com 9210259SAndrew.Bardsley@arm.comclass MetaParamValue(type): 9310259SAndrew.Bardsley@arm.com def __new__(mcls, name, bases, dct): 9410259SAndrew.Bardsley@arm.com cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct) 9510259SAndrew.Bardsley@arm.com assert name not in allParams 9610259SAndrew.Bardsley@arm.com allParams[name] = cls 9710259SAndrew.Bardsley@arm.com return cls 9810259SAndrew.Bardsley@arm.com 9910259SAndrew.Bardsley@arm.com 10010259SAndrew.Bardsley@arm.com# Dummy base class to identify types that are legitimate for SimObject 10110259SAndrew.Bardsley@arm.com# parameters. 10210259SAndrew.Bardsley@arm.comclass ParamValue(object): 10310259SAndrew.Bardsley@arm.com __metaclass__ = MetaParamValue 10410259SAndrew.Bardsley@arm.com cmd_line_settable = False 10510259SAndrew.Bardsley@arm.com 10610259SAndrew.Bardsley@arm.com # Generate the code needed as a prerequisite for declaring a C++ 10710259SAndrew.Bardsley@arm.com # object of this type. Typically generates one or more #include 10810259SAndrew.Bardsley@arm.com # statements. Used when declaring parameters of this type. 10910259SAndrew.Bardsley@arm.com @classmethod 11010259SAndrew.Bardsley@arm.com def cxx_predecls(cls, code): 11110259SAndrew.Bardsley@arm.com pass 11210259SAndrew.Bardsley@arm.com 11310259SAndrew.Bardsley@arm.com @classmethod 11410259SAndrew.Bardsley@arm.com def pybind_predecls(cls, code): 11510259SAndrew.Bardsley@arm.com cls.cxx_predecls(code) 11610259SAndrew.Bardsley@arm.com 11710259SAndrew.Bardsley@arm.com # default for printing to .ini file is regular string conversion. 11810259SAndrew.Bardsley@arm.com # will be overridden in some cases 11910259SAndrew.Bardsley@arm.com def ini_str(self): 12010259SAndrew.Bardsley@arm.com return str(self) 12110259SAndrew.Bardsley@arm.com 12210259SAndrew.Bardsley@arm.com # default for printing to .json file is regular string conversion. 12310259SAndrew.Bardsley@arm.com # will be overridden in some cases, mostly to use native Python 12410259SAndrew.Bardsley@arm.com # types where there are similar JSON types 12510259SAndrew.Bardsley@arm.com def config_value(self): 12610259SAndrew.Bardsley@arm.com return str(self) 12710259SAndrew.Bardsley@arm.com 12810259SAndrew.Bardsley@arm.com # Prerequisites for .ini parsing with cxx_ini_parse 12910259SAndrew.Bardsley@arm.com @classmethod 13010259SAndrew.Bardsley@arm.com def cxx_ini_predecls(cls, code): 13110259SAndrew.Bardsley@arm.com pass 13210259SAndrew.Bardsley@arm.com 13310259SAndrew.Bardsley@arm.com # parse a .ini file entry for this param from string expression 13410259SAndrew.Bardsley@arm.com # src into lvalue dest (of the param's C++ type) 13510259SAndrew.Bardsley@arm.com @classmethod 13610259SAndrew.Bardsley@arm.com def cxx_ini_parse(cls, code, src, dest, ret): 13710259SAndrew.Bardsley@arm.com code('// Unhandled param type: %s' % cls.__name__) 13810259SAndrew.Bardsley@arm.com code('%s false;' % ret) 13910259SAndrew.Bardsley@arm.com 14010259SAndrew.Bardsley@arm.com # allows us to blithely call unproxy() on things without checking 14110259SAndrew.Bardsley@arm.com # if they're really proxies or not 14210259SAndrew.Bardsley@arm.com def unproxy(self, base): 14310259SAndrew.Bardsley@arm.com return self 14410259SAndrew.Bardsley@arm.com 14510259SAndrew.Bardsley@arm.com # Produce a human readable version of the stored value 14610259SAndrew.Bardsley@arm.com def pretty_print(self, value): 14710259SAndrew.Bardsley@arm.com return str(value) 14810259SAndrew.Bardsley@arm.com 14910259SAndrew.Bardsley@arm.com# Regular parameter description. 15010259SAndrew.Bardsley@arm.comclass ParamDesc(object): 15110259SAndrew.Bardsley@arm.com def __init__(self, ptype_str, ptype, *args, **kwargs): 15210259SAndrew.Bardsley@arm.com self.ptype_str = ptype_str 15310259SAndrew.Bardsley@arm.com # remember ptype only if it is provided 15410259SAndrew.Bardsley@arm.com if ptype != None: 15510259SAndrew.Bardsley@arm.com self.ptype = ptype 15610259SAndrew.Bardsley@arm.com 15710259SAndrew.Bardsley@arm.com if args: 15810259SAndrew.Bardsley@arm.com if len(args) == 1: 15910259SAndrew.Bardsley@arm.com self.desc = args[0] 16010259SAndrew.Bardsley@arm.com elif len(args) == 2: 16110259SAndrew.Bardsley@arm.com self.default = args[0] 16210259SAndrew.Bardsley@arm.com self.desc = args[1] 16310259SAndrew.Bardsley@arm.com else: 16410259SAndrew.Bardsley@arm.com raise TypeError('too many arguments') 16510259SAndrew.Bardsley@arm.com 16610259SAndrew.Bardsley@arm.com if 'desc' in kwargs: 16710259SAndrew.Bardsley@arm.com assert(not hasattr(self, 'desc')) 16810259SAndrew.Bardsley@arm.com self.desc = kwargs['desc'] 16910259SAndrew.Bardsley@arm.com del kwargs['desc'] 17010259SAndrew.Bardsley@arm.com 17110259SAndrew.Bardsley@arm.com if 'default' in kwargs: 17210259SAndrew.Bardsley@arm.com assert(not hasattr(self, 'default')) 17310259SAndrew.Bardsley@arm.com self.default = kwargs['default'] 17410259SAndrew.Bardsley@arm.com del kwargs['default'] 17510259SAndrew.Bardsley@arm.com 17610259SAndrew.Bardsley@arm.com if kwargs: 17710259SAndrew.Bardsley@arm.com raise TypeError('extra unknown kwargs %s' % kwargs) 17810259SAndrew.Bardsley@arm.com 17910259SAndrew.Bardsley@arm.com if not hasattr(self, 'desc'): 18010259SAndrew.Bardsley@arm.com raise TypeError('desc attribute missing') 18110259SAndrew.Bardsley@arm.com 18210259SAndrew.Bardsley@arm.com def __getattr__(self, attr): 18310259SAndrew.Bardsley@arm.com if attr == 'ptype': 18410259SAndrew.Bardsley@arm.com from . import SimObject 18510259SAndrew.Bardsley@arm.com ptype = SimObject.allClasses[self.ptype_str] 18610259SAndrew.Bardsley@arm.com assert isSimObjectClass(ptype) 18710259SAndrew.Bardsley@arm.com self.ptype = ptype 18810259SAndrew.Bardsley@arm.com return ptype 18910259SAndrew.Bardsley@arm.com 19010259SAndrew.Bardsley@arm.com raise AttributeError("'%s' object has no attribute '%s'" % \ 19110259SAndrew.Bardsley@arm.com (type(self).__name__, attr)) 19210259SAndrew.Bardsley@arm.com 19310259SAndrew.Bardsley@arm.com def example_str(self): 19410259SAndrew.Bardsley@arm.com if hasattr(self.ptype, "ex_str"): 19510259SAndrew.Bardsley@arm.com return self.ptype.ex_str 19610259SAndrew.Bardsley@arm.com else: 19710259SAndrew.Bardsley@arm.com return self.ptype_str 19810259SAndrew.Bardsley@arm.com 19910259SAndrew.Bardsley@arm.com # Is the param available to be exposed on the command line 20010259SAndrew.Bardsley@arm.com def isCmdLineSettable(self): 20110259SAndrew.Bardsley@arm.com if hasattr(self.ptype, "cmd_line_settable"): 20210259SAndrew.Bardsley@arm.com return self.ptype.cmd_line_settable 20310259SAndrew.Bardsley@arm.com else: 20410259SAndrew.Bardsley@arm.com return False 20510259SAndrew.Bardsley@arm.com 20610259SAndrew.Bardsley@arm.com def convert(self, value): 20710259SAndrew.Bardsley@arm.com if isinstance(value, proxy.BaseProxy): 20810259SAndrew.Bardsley@arm.com value.set_param_desc(self) 20910259SAndrew.Bardsley@arm.com return value 21010259SAndrew.Bardsley@arm.com if 'ptype' not in self.__dict__ and isNullPointer(value): 21110259SAndrew.Bardsley@arm.com # deferred evaluation of SimObject; continue to defer if 21210259SAndrew.Bardsley@arm.com # we're just assigning a null pointer 21310259SAndrew.Bardsley@arm.com return value 21410259SAndrew.Bardsley@arm.com if isinstance(value, self.ptype): 21510259SAndrew.Bardsley@arm.com return value 21610259SAndrew.Bardsley@arm.com if isNullPointer(value) and isSimObjectClass(self.ptype): 21710259SAndrew.Bardsley@arm.com return value 21810259SAndrew.Bardsley@arm.com return self.ptype(value) 21910259SAndrew.Bardsley@arm.com 22010259SAndrew.Bardsley@arm.com def pretty_print(self, value): 22110259SAndrew.Bardsley@arm.com if isinstance(value, proxy.BaseProxy): 22212104Snathanael.premillieu@arm.com return str(value) 22310259SAndrew.Bardsley@arm.com if isNullPointer(value): 22410259SAndrew.Bardsley@arm.com return NULL 22510259SAndrew.Bardsley@arm.com return self.ptype(value).pretty_print(value) 22610259SAndrew.Bardsley@arm.com 22710259SAndrew.Bardsley@arm.com def cxx_predecls(self, code): 22810259SAndrew.Bardsley@arm.com code('#include <cstddef>') 22910259SAndrew.Bardsley@arm.com self.ptype.cxx_predecls(code) 23010259SAndrew.Bardsley@arm.com 23110259SAndrew.Bardsley@arm.com def pybind_predecls(self, code): 23210259SAndrew.Bardsley@arm.com self.ptype.pybind_predecls(code) 23310259SAndrew.Bardsley@arm.com 23410259SAndrew.Bardsley@arm.com def cxx_decl(self, code): 23510259SAndrew.Bardsley@arm.com code('${{self.ptype.cxx_type}} ${{self.name}};') 23610259SAndrew.Bardsley@arm.com 23710259SAndrew.Bardsley@arm.com# Vector-valued parameter description. Just like ParamDesc, except 23810259SAndrew.Bardsley@arm.com# that the value is a vector (list) of the specified type instead of a 23910259SAndrew.Bardsley@arm.com# single value. 24010259SAndrew.Bardsley@arm.com 24110259SAndrew.Bardsley@arm.comclass VectorParamValue(list): 24210259SAndrew.Bardsley@arm.com __metaclass__ = MetaParamValue 24310259SAndrew.Bardsley@arm.com def __setattr__(self, attr, value): 24410259SAndrew.Bardsley@arm.com raise AttributeError("Not allowed to set %s on '%s'" % \ 24510259SAndrew.Bardsley@arm.com (attr, type(self).__name__)) 24610259SAndrew.Bardsley@arm.com 24710259SAndrew.Bardsley@arm.com def config_value(self): 24810259SAndrew.Bardsley@arm.com return [v.config_value() for v in self] 24910259SAndrew.Bardsley@arm.com 25010259SAndrew.Bardsley@arm.com def ini_str(self): 25110259SAndrew.Bardsley@arm.com return ' '.join([v.ini_str() for v in self]) 25210259SAndrew.Bardsley@arm.com 25310259SAndrew.Bardsley@arm.com def getValue(self): 25410259SAndrew.Bardsley@arm.com return [ v.getValue() for v in self ] 25510259SAndrew.Bardsley@arm.com 25610259SAndrew.Bardsley@arm.com def unproxy(self, base): 25710259SAndrew.Bardsley@arm.com if len(self) == 1 and isinstance(self[0], proxy.BaseProxy): 25810259SAndrew.Bardsley@arm.com # The value is a proxy (e.g. Parent.any, Parent.all or 25910259SAndrew.Bardsley@arm.com # Parent.x) therefore try resolve it 26010259SAndrew.Bardsley@arm.com return self[0].unproxy(base) 26110259SAndrew.Bardsley@arm.com else: 26210259SAndrew.Bardsley@arm.com return [v.unproxy(base) for v in self] 26310259SAndrew.Bardsley@arm.com 26410259SAndrew.Bardsley@arm.comclass SimObjectVector(VectorParamValue): 26510259SAndrew.Bardsley@arm.com # support clone operation 26610259SAndrew.Bardsley@arm.com def __call__(self, **kwargs): 26710259SAndrew.Bardsley@arm.com return SimObjectVector([v(**kwargs) for v in self]) 26810259SAndrew.Bardsley@arm.com 26910259SAndrew.Bardsley@arm.com def clear_parent(self, old_parent): 27010259SAndrew.Bardsley@arm.com for v in self: 27110259SAndrew.Bardsley@arm.com v.clear_parent(old_parent) 27210259SAndrew.Bardsley@arm.com 27310259SAndrew.Bardsley@arm.com def set_parent(self, parent, name): 27410259SAndrew.Bardsley@arm.com if len(self) == 1: 27510259SAndrew.Bardsley@arm.com self[0].set_parent(parent, name) 27610259SAndrew.Bardsley@arm.com else: 27710259SAndrew.Bardsley@arm.com width = int(math.ceil(math.log(len(self))/math.log(10))) 27810259SAndrew.Bardsley@arm.com for i,v in enumerate(self): 27910259SAndrew.Bardsley@arm.com v.set_parent(parent, "%s%0*d" % (name, width, i)) 28010259SAndrew.Bardsley@arm.com 28110259SAndrew.Bardsley@arm.com def has_parent(self): 282 return any([e.has_parent() for e in self if not isNullPointer(e)]) 283 284 # return 'cpu0 cpu1' etc. for print_ini() 285 def get_name(self): 286 return ' '.join([v._name for v in self]) 287 288 # By iterating through the constituent members of the vector here 289 # we can nicely handle iterating over all a SimObject's children 290 # without having to provide lots of special functions on 291 # SimObjectVector directly. 292 def descendants(self): 293 for v in self: 294 for obj in v.descendants(): 295 yield obj 296 297 def get_config_as_dict(self): 298 a = [] 299 for v in self: 300 a.append(v.get_config_as_dict()) 301 return a 302 303 # If we are replacing an item in the vector, make sure to set the 304 # parent reference of the new SimObject to be the same as the parent 305 # of the SimObject being replaced. Useful to have if we created 306 # a SimObjectVector of temporary objects that will be modified later in 307 # configuration scripts. 308 def __setitem__(self, key, value): 309 val = self[key] 310 if value.has_parent(): 311 warn("SimObject %s already has a parent" % value.get_name() +\ 312 " that is being overwritten by a SimObjectVector") 313 value.set_parent(val.get_parent(), val._name) 314 super(SimObjectVector, self).__setitem__(key, value) 315 316 # Enumerate the params of each member of the SimObject vector. Creates 317 # strings that will allow indexing into the vector by the python code and 318 # allow it to be specified on the command line. 319 def enumerateParams(self, flags_dict = {}, 320 cmd_line_str = "", 321 access_str = ""): 322 if hasattr(self, "_paramEnumed"): 323 print("Cycle detected enumerating params at %s?!" % (cmd_line_str)) 324 else: 325 x = 0 326 for vals in self: 327 # Each entry in the SimObjectVector should be an 328 # instance of a SimObject 329 flags_dict = vals.enumerateParams(flags_dict, 330 cmd_line_str + "%d." % x, 331 access_str + "[%d]." % x) 332 x = x + 1 333 334 return flags_dict 335 336class VectorParamDesc(ParamDesc): 337 # Convert assigned value to appropriate type. If the RHS is not a 338 # list or tuple, it generates a single-element list. 339 def convert(self, value): 340 if isinstance(value, (list, tuple)): 341 # list: coerce each element into new list 342 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 343 elif isinstance(value, str): 344 # If input is a csv string 345 tmp_list = [ ParamDesc.convert(self, v) \ 346 for v in value.strip('[').strip(']').split(',') ] 347 else: 348 # singleton: coerce to a single-element list 349 tmp_list = [ ParamDesc.convert(self, value) ] 350 351 if isSimObjectSequence(tmp_list): 352 return SimObjectVector(tmp_list) 353 else: 354 return VectorParamValue(tmp_list) 355 356 # Produce a human readable example string that describes 357 # how to set this vector parameter in the absence of a default 358 # value. 359 def example_str(self): 360 s = super(VectorParamDesc, self).example_str() 361 help_str = "[" + s + "," + s + ", ...]" 362 return help_str 363 364 # Produce a human readable representation of the value of this vector param. 365 def pretty_print(self, value): 366 if isinstance(value, (list, tuple)): 367 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ] 368 elif isinstance(value, str): 369 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ] 370 else: 371 tmp_list = [ ParamDesc.pretty_print(self, value) ] 372 373 return tmp_list 374 375 # This is a helper function for the new config system 376 def __call__(self, value): 377 if isinstance(value, (list, tuple)): 378 # list: coerce each element into new list 379 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 380 elif isinstance(value, str): 381 # If input is a csv string 382 tmp_list = [ ParamDesc.convert(self, v) \ 383 for v in value.strip('[').strip(']').split(',') ] 384 else: 385 # singleton: coerce to a single-element list 386 tmp_list = [ ParamDesc.convert(self, value) ] 387 388 return VectorParamValue(tmp_list) 389 390 def cxx_predecls(self, code): 391 code('#include <vector>') 392 self.ptype.cxx_predecls(code) 393 394 def pybind_predecls(self, code): 395 code('#include <vector>') 396 self.ptype.pybind_predecls(code) 397 398 def cxx_decl(self, code): 399 code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};') 400 401class ParamFactory(object): 402 def __init__(self, param_desc_class, ptype_str = None): 403 self.param_desc_class = param_desc_class 404 self.ptype_str = ptype_str 405 406 def __getattr__(self, attr): 407 if self.ptype_str: 408 attr = self.ptype_str + '.' + attr 409 return ParamFactory(self.param_desc_class, attr) 410 411 # E.g., Param.Int(5, "number of widgets") 412 def __call__(self, *args, **kwargs): 413 ptype = None 414 try: 415 ptype = allParams[self.ptype_str] 416 except KeyError: 417 # if name isn't defined yet, assume it's a SimObject, and 418 # try to resolve it later 419 pass 420 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 421 422Param = ParamFactory(ParamDesc) 423VectorParam = ParamFactory(VectorParamDesc) 424 425##################################################################### 426# 427# Parameter Types 428# 429# Though native Python types could be used to specify parameter types 430# (the 'ptype' field of the Param and VectorParam classes), it's more 431# flexible to define our own set of types. This gives us more control 432# over how Python expressions are converted to values (via the 433# __init__() constructor) and how these values are printed out (via 434# the __str__() conversion method). 435# 436##################################################################### 437 438# String-valued parameter. Just mixin the ParamValue class with the 439# built-in str class. 440class String(ParamValue,str): 441 cxx_type = 'std::string' 442 cmd_line_settable = True 443 444 @classmethod 445 def cxx_predecls(self, code): 446 code('#include <string>') 447 448 def __call__(self, value): 449 self = value 450 return value 451 452 @classmethod 453 def cxx_ini_parse(self, code, src, dest, ret): 454 code('%s = %s;' % (dest, src)) 455 code('%s true;' % ret) 456 457 def getValue(self): 458 return self 459 460# superclass for "numeric" parameter values, to emulate math 461# operations in a type-safe way. e.g., a Latency times an int returns 462# a new Latency object. 463class NumericParamValue(ParamValue): 464 @staticmethod 465 def unwrap(v): 466 return v.value if isinstance(v, NumericParamValue) else v 467 468 def __str__(self): 469 return str(self.value) 470 471 def __float__(self): 472 return float(self.value) 473 474 def __long__(self): 475 return long(self.value) 476 477 def __int__(self): 478 return int(self.value) 479 480 # hook for bounds checking 481 def _check(self): 482 return 483 484 def __mul__(self, other): 485 newobj = self.__class__(self) 486 newobj.value *= NumericParamValue.unwrap(other) 487 newobj._check() 488 return newobj 489 490 __rmul__ = __mul__ 491 492 def __truediv__(self, other): 493 newobj = self.__class__(self) 494 newobj.value /= NumericParamValue.unwrap(other) 495 newobj._check() 496 return newobj 497 498 def __floordiv__(self, other): 499 newobj = self.__class__(self) 500 newobj.value //= NumericParamValue.unwrap(other) 501 newobj._check() 502 return newobj 503 504 505 def __add__(self, other): 506 newobj = self.__class__(self) 507 newobj.value += NumericParamValue.unwrap(other) 508 newobj._check() 509 return newobj 510 511 def __sub__(self, other): 512 newobj = self.__class__(self) 513 newobj.value -= NumericParamValue.unwrap(other) 514 newobj._check() 515 return newobj 516 517 def __iadd__(self, other): 518 self.value += NumericParamValue.unwrap(other) 519 self._check() 520 return self 521 522 def __isub__(self, other): 523 self.value -= NumericParamValue.unwrap(other) 524 self._check() 525 return self 526 527 def __imul__(self, other): 528 self.value *= NumericParamValue.unwrap(other) 529 self._check() 530 return self 531 532 def __itruediv__(self, other): 533 self.value /= NumericParamValue.unwrap(other) 534 self._check() 535 return self 536 537 def __ifloordiv__(self, other): 538 self.value //= NumericParamValue.unwrap(other) 539 self._check() 540 return self 541 542 def __lt__(self, other): 543 return self.value < NumericParamValue.unwrap(other) 544 545 # Python 2.7 pre __future__.division operators 546 # TODO: Remove these when after "import division from __future__" 547 __div__ = __truediv__ 548 __idiv__ = __itruediv__ 549 550 def config_value(self): 551 return self.value 552 553 @classmethod 554 def cxx_ini_predecls(cls, code): 555 # Assume that base/str.hh will be included anyway 556 # code('#include "base/str.hh"') 557 pass 558 559 # The default for parsing PODs from an .ini entry is to extract from an 560 # istringstream and let overloading choose the right type according to 561 # the dest type. 562 @classmethod 563 def cxx_ini_parse(self, code, src, dest, ret): 564 code('%s to_number(%s, %s);' % (ret, src, dest)) 565 566# Metaclass for bounds-checked integer parameters. See CheckedInt. 567class CheckedIntType(MetaParamValue): 568 def __init__(cls, name, bases, dict): 569 super(CheckedIntType, cls).__init__(name, bases, dict) 570 571 # CheckedInt is an abstract base class, so we actually don't 572 # want to do any processing on it... the rest of this code is 573 # just for classes that derive from CheckedInt. 574 if name == 'CheckedInt': 575 return 576 577 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 578 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 579 panic("CheckedInt subclass %s must define either\n" \ 580 " 'min' and 'max' or 'size' and 'unsigned'\n", 581 name); 582 if cls.unsigned: 583 cls.min = 0 584 cls.max = 2 ** cls.size - 1 585 else: 586 cls.min = -(2 ** (cls.size - 1)) 587 cls.max = (2 ** (cls.size - 1)) - 1 588 589# Abstract superclass for bounds-checked integer parameters. This 590# class is subclassed to generate parameter classes with specific 591# bounds. Initialization of the min and max bounds is done in the 592# metaclass CheckedIntType.__init__. 593class CheckedInt(NumericParamValue): 594 __metaclass__ = CheckedIntType 595 cmd_line_settable = True 596 597 def _check(self): 598 if not self.min <= self.value <= self.max: 599 raise TypeError('Integer param out of bounds %d < %d < %d' % \ 600 (self.min, self.value, self.max)) 601 602 def __init__(self, value): 603 if isinstance(value, str): 604 self.value = convert.toInteger(value) 605 elif isinstance(value, (int, long, float, NumericParamValue)): 606 self.value = long(value) 607 else: 608 raise TypeError("Can't convert object of type %s to CheckedInt" \ 609 % type(value).__name__) 610 self._check() 611 612 def __call__(self, value): 613 self.__init__(value) 614 return value 615 616 def __index__(self): 617 return int(self.value) 618 619 @classmethod 620 def cxx_predecls(cls, code): 621 # most derived types require this, so we just do it here once 622 code('#include "base/types.hh"') 623 624 def getValue(self): 625 return long(self.value) 626 627class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 628class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 629 630class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 631class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 632class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 633class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 634class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 635class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 636class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 637class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 638 639class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 640class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 641class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 642class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 643 644class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 645 646class Cycles(CheckedInt): 647 cxx_type = 'Cycles' 648 size = 64 649 unsigned = True 650 651 def getValue(self): 652 from _m5.core import Cycles 653 return Cycles(self.value) 654 655 @classmethod 656 def cxx_ini_predecls(cls, code): 657 # Assume that base/str.hh will be included anyway 658 # code('#include "base/str.hh"') 659 pass 660 661 @classmethod 662 def cxx_ini_parse(cls, code, src, dest, ret): 663 code('uint64_t _temp;') 664 code('bool _ret = to_number(%s, _temp);' % src) 665 code('if (_ret)') 666 code(' %s = Cycles(_temp);' % dest) 667 code('%s _ret;' % ret) 668 669class Float(ParamValue, float): 670 cxx_type = 'double' 671 cmd_line_settable = True 672 673 def __init__(self, value): 674 if isinstance(value, (int, long, float, NumericParamValue, Float, str)): 675 self.value = float(value) 676 else: 677 raise TypeError("Can't convert object of type %s to Float" \ 678 % type(value).__name__) 679 680 def __call__(self, value): 681 self.__init__(value) 682 return value 683 684 def getValue(self): 685 return float(self.value) 686 687 def config_value(self): 688 return self 689 690 @classmethod 691 def cxx_ini_predecls(cls, code): 692 code('#include <sstream>') 693 694 @classmethod 695 def cxx_ini_parse(self, code, src, dest, ret): 696 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 697 698class MemorySize(CheckedInt): 699 cxx_type = 'uint64_t' 700 ex_str = '512MB' 701 size = 64 702 unsigned = True 703 def __init__(self, value): 704 if isinstance(value, MemorySize): 705 self.value = value.value 706 else: 707 self.value = convert.toMemorySize(value) 708 self._check() 709 710class MemorySize32(CheckedInt): 711 cxx_type = 'uint32_t' 712 ex_str = '512MB' 713 size = 32 714 unsigned = True 715 def __init__(self, value): 716 if isinstance(value, MemorySize): 717 self.value = value.value 718 else: 719 self.value = convert.toMemorySize(value) 720 self._check() 721 722class Addr(CheckedInt): 723 cxx_type = 'Addr' 724 size = 64 725 unsigned = True 726 def __init__(self, value): 727 if isinstance(value, Addr): 728 self.value = value.value 729 else: 730 try: 731 # Often addresses are referred to with sizes. Ex: A device 732 # base address is at "512MB". Use toMemorySize() to convert 733 # these into addresses. If the address is not specified with a 734 # "size", an exception will occur and numeric translation will 735 # proceed below. 736 self.value = convert.toMemorySize(value) 737 except (TypeError, ValueError): 738 # Convert number to string and use long() to do automatic 739 # base conversion (requires base=0 for auto-conversion) 740 self.value = long(str(value), base=0) 741 742 self._check() 743 def __add__(self, other): 744 if isinstance(other, Addr): 745 return self.value + other.value 746 else: 747 return self.value + other 748 def pretty_print(self, value): 749 try: 750 val = convert.toMemorySize(value) 751 except TypeError: 752 val = long(value) 753 return "0x%x" % long(val) 754 755class AddrRange(ParamValue): 756 cxx_type = 'AddrRange' 757 758 def __init__(self, *args, **kwargs): 759 # Disable interleaving and hashing by default 760 self.intlvHighBit = 0 761 self.xorHighBit = 0 762 self.intlvBits = 0 763 self.intlvMatch = 0 764 765 def handle_kwargs(self, kwargs): 766 # An address range needs to have an upper limit, specified 767 # either explicitly with an end, or as an offset using the 768 # size keyword. 769 if 'end' in kwargs: 770 self.end = Addr(kwargs.pop('end')) 771 elif 'size' in kwargs: 772 self.end = self.start + Addr(kwargs.pop('size')) - 1 773 else: 774 raise TypeError("Either end or size must be specified") 775 776 # Now on to the optional bit 777 if 'intlvHighBit' in kwargs: 778 self.intlvHighBit = int(kwargs.pop('intlvHighBit')) 779 if 'xorHighBit' in kwargs: 780 self.xorHighBit = int(kwargs.pop('xorHighBit')) 781 if 'intlvBits' in kwargs: 782 self.intlvBits = int(kwargs.pop('intlvBits')) 783 if 'intlvMatch' in kwargs: 784 self.intlvMatch = int(kwargs.pop('intlvMatch')) 785 786 if len(args) == 0: 787 self.start = Addr(kwargs.pop('start')) 788 handle_kwargs(self, kwargs) 789 790 elif len(args) == 1: 791 if kwargs: 792 self.start = Addr(args[0]) 793 handle_kwargs(self, kwargs) 794 elif isinstance(args[0], (list, tuple)): 795 self.start = Addr(args[0][0]) 796 self.end = Addr(args[0][1]) 797 else: 798 self.start = Addr(0) 799 self.end = Addr(args[0]) - 1 800 801 elif len(args) == 2: 802 self.start = Addr(args[0]) 803 self.end = Addr(args[1]) 804 else: 805 raise TypeError("Too many arguments specified") 806 807 if kwargs: 808 raise TypeError("Too many keywords: %s" % list(kwargs.keys())) 809 810 def __str__(self): 811 return '%s:%s:%s:%s:%s:%s' \ 812 % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\ 813 self.intlvBits, self.intlvMatch) 814 815 def size(self): 816 # Divide the size by the size of the interleaving slice 817 return (long(self.end) - long(self.start) + 1) >> self.intlvBits 818 819 @classmethod 820 def cxx_predecls(cls, code): 821 Addr.cxx_predecls(code) 822 code('#include "base/addr_range.hh"') 823 824 @classmethod 825 def pybind_predecls(cls, code): 826 Addr.pybind_predecls(code) 827 code('#include "base/addr_range.hh"') 828 829 @classmethod 830 def cxx_ini_predecls(cls, code): 831 code('#include <sstream>') 832 833 @classmethod 834 def cxx_ini_parse(cls, code, src, dest, ret): 835 code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;') 836 code('uint64_t _intlvBits = 0, _intlvMatch = 0;') 837 code('char _sep;') 838 code('std::istringstream _stream(${src});') 839 code('_stream >> _start;') 840 code('_stream.get(_sep);') 841 code('_stream >> _end;') 842 code('if (!_stream.fail() && !_stream.eof()) {') 843 code(' _stream.get(_sep);') 844 code(' _stream >> _intlvHighBit;') 845 code(' _stream.get(_sep);') 846 code(' _stream >> _xorHighBit;') 847 code(' _stream.get(_sep);') 848 code(' _stream >> _intlvBits;') 849 code(' _stream.get(_sep);') 850 code(' _stream >> _intlvMatch;') 851 code('}') 852 code('bool _ret = !_stream.fail() &&' 853 '_stream.eof() && _sep == \':\';') 854 code('if (_ret)') 855 code(' ${dest} = AddrRange(_start, _end, _intlvHighBit, \ 856 _xorHighBit, _intlvBits, _intlvMatch);') 857 code('${ret} _ret;') 858 859 def getValue(self): 860 # Go from the Python class to the wrapped C++ class 861 from _m5.range import AddrRange 862 863 return AddrRange(long(self.start), long(self.end), 864 int(self.intlvHighBit), int(self.xorHighBit), 865 int(self.intlvBits), int(self.intlvMatch)) 866 867# Boolean parameter type. Python doesn't let you subclass bool, since 868# it doesn't want to let you create multiple instances of True and 869# False. Thus this is a little more complicated than String. 870class Bool(ParamValue): 871 cxx_type = 'bool' 872 cmd_line_settable = True 873 874 def __init__(self, value): 875 try: 876 self.value = convert.toBool(value) 877 except TypeError: 878 self.value = bool(value) 879 880 def __call__(self, value): 881 self.__init__(value) 882 return value 883 884 def getValue(self): 885 return bool(self.value) 886 887 def __str__(self): 888 return str(self.value) 889 890 # implement truth value testing for Bool parameters so that these params 891 # evaluate correctly during the python configuration phase 892 def __bool__(self): 893 return bool(self.value) 894 895 # Python 2.7 uses __nonzero__ instead of __bool__ 896 __nonzero__ = __bool__ 897 898 def ini_str(self): 899 if self.value: 900 return 'true' 901 return 'false' 902 903 def config_value(self): 904 return self.value 905 906 @classmethod 907 def cxx_ini_predecls(cls, code): 908 # Assume that base/str.hh will be included anyway 909 # code('#include "base/str.hh"') 910 pass 911 912 @classmethod 913 def cxx_ini_parse(cls, code, src, dest, ret): 914 code('%s to_bool(%s, %s);' % (ret, src, dest)) 915 916def IncEthernetAddr(addr, val = 1): 917 bytes = [ int(x, 16) for x in addr.split(':') ] 918 bytes[5] += val 919 for i in (5, 4, 3, 2, 1): 920 val,rem = divmod(bytes[i], 256) 921 bytes[i] = rem 922 if val == 0: 923 break 924 bytes[i - 1] += val 925 assert(bytes[0] <= 255) 926 return ':'.join(map(lambda x: '%02x' % x, bytes)) 927 928_NextEthernetAddr = "00:90:00:00:00:01" 929def NextEthernetAddr(): 930 global _NextEthernetAddr 931 932 value = _NextEthernetAddr 933 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 934 return value 935 936class EthernetAddr(ParamValue): 937 cxx_type = 'Net::EthAddr' 938 ex_str = "00:90:00:00:00:01" 939 cmd_line_settable = True 940 941 @classmethod 942 def cxx_predecls(cls, code): 943 code('#include "base/inet.hh"') 944 945 def __init__(self, value): 946 if value == NextEthernetAddr: 947 self.value = value 948 return 949 950 if not isinstance(value, str): 951 raise TypeError("expected an ethernet address and didn't get one") 952 953 bytes = value.split(':') 954 if len(bytes) != 6: 955 raise TypeError('invalid ethernet address %s' % value) 956 957 for byte in bytes: 958 if not 0 <= int(byte, base=16) <= 0xff: 959 raise TypeError('invalid ethernet address %s' % value) 960 961 self.value = value 962 963 def __call__(self, value): 964 self.__init__(value) 965 return value 966 967 def unproxy(self, base): 968 if self.value == NextEthernetAddr: 969 return EthernetAddr(self.value()) 970 return self 971 972 def getValue(self): 973 from _m5.net import EthAddr 974 return EthAddr(self.value) 975 976 def __str__(self): 977 return self.value 978 979 def ini_str(self): 980 return self.value 981 982 @classmethod 983 def cxx_ini_parse(self, code, src, dest, ret): 984 code('%s = Net::EthAddr(%s);' % (dest, src)) 985 code('%s true;' % ret) 986 987# When initializing an IpAddress, pass in an existing IpAddress, a string of 988# the form "a.b.c.d", or an integer representing an IP. 989class IpAddress(ParamValue): 990 cxx_type = 'Net::IpAddress' 991 ex_str = "127.0.0.1" 992 cmd_line_settable = True 993 994 @classmethod 995 def cxx_predecls(cls, code): 996 code('#include "base/inet.hh"') 997 998 def __init__(self, value): 999 if isinstance(value, IpAddress): 1000 self.ip = value.ip 1001 else: 1002 try: 1003 self.ip = convert.toIpAddress(value) 1004 except TypeError: 1005 self.ip = long(value) 1006 self.verifyIp() 1007 1008 def __call__(self, value): 1009 self.__init__(value) 1010 return value 1011 1012 def __str__(self): 1013 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)] 1014 return '%d.%d.%d.%d' % tuple(tup) 1015 1016 def __eq__(self, other): 1017 if isinstance(other, IpAddress): 1018 return self.ip == other.ip 1019 elif isinstance(other, str): 1020 try: 1021 return self.ip == convert.toIpAddress(other) 1022 except: 1023 return False 1024 else: 1025 return self.ip == other 1026 1027 def __ne__(self, other): 1028 return not (self == other) 1029 1030 def verifyIp(self): 1031 if self.ip < 0 or self.ip >= (1 << 32): 1032 raise TypeError("invalid ip address %#08x" % self.ip) 1033 1034 def getValue(self): 1035 from _m5.net import IpAddress 1036 return IpAddress(self.ip) 1037 1038# When initializing an IpNetmask, pass in an existing IpNetmask, a string of 1039# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as 1040# positional or keyword arguments. 1041class IpNetmask(IpAddress): 1042 cxx_type = 'Net::IpNetmask' 1043 ex_str = "127.0.0.0/24" 1044 cmd_line_settable = True 1045 1046 @classmethod 1047 def cxx_predecls(cls, code): 1048 code('#include "base/inet.hh"') 1049 1050 def __init__(self, *args, **kwargs): 1051 def handle_kwarg(self, kwargs, key, elseVal = None): 1052 if key in kwargs: 1053 setattr(self, key, kwargs.pop(key)) 1054 elif elseVal: 1055 setattr(self, key, elseVal) 1056 else: 1057 raise TypeError("No value set for %s" % key) 1058 1059 if len(args) == 0: 1060 handle_kwarg(self, kwargs, 'ip') 1061 handle_kwarg(self, kwargs, 'netmask') 1062 1063 elif len(args) == 1: 1064 if kwargs: 1065 if not 'ip' in kwargs and not 'netmask' in kwargs: 1066 raise TypeError("Invalid arguments") 1067 handle_kwarg(self, kwargs, 'ip', args[0]) 1068 handle_kwarg(self, kwargs, 'netmask', args[0]) 1069 elif isinstance(args[0], IpNetmask): 1070 self.ip = args[0].ip 1071 self.netmask = args[0].netmask 1072 else: 1073 (self.ip, self.netmask) = convert.toIpNetmask(args[0]) 1074 1075 elif len(args) == 2: 1076 self.ip = args[0] 1077 self.netmask = args[1] 1078 else: 1079 raise TypeError("Too many arguments specified") 1080 1081 if kwargs: 1082 raise TypeError("Too many keywords: %s" % list(kwargs.keys())) 1083 1084 self.verify() 1085 1086 def __call__(self, value): 1087 self.__init__(value) 1088 return value 1089 1090 def __str__(self): 1091 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask) 1092 1093 def __eq__(self, other): 1094 if isinstance(other, IpNetmask): 1095 return self.ip == other.ip and self.netmask == other.netmask 1096 elif isinstance(other, str): 1097 try: 1098 return (self.ip, self.netmask) == convert.toIpNetmask(other) 1099 except: 1100 return False 1101 else: 1102 return False 1103 1104 def verify(self): 1105 self.verifyIp() 1106 if self.netmask < 0 or self.netmask > 32: 1107 raise TypeError("invalid netmask %d" % netmask) 1108 1109 def getValue(self): 1110 from _m5.net import IpNetmask 1111 return IpNetmask(self.ip, self.netmask) 1112 1113# When initializing an IpWithPort, pass in an existing IpWithPort, a string of 1114# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments. 1115class IpWithPort(IpAddress): 1116 cxx_type = 'Net::IpWithPort' 1117 ex_str = "127.0.0.1:80" 1118 cmd_line_settable = True 1119 1120 @classmethod 1121 def cxx_predecls(cls, code): 1122 code('#include "base/inet.hh"') 1123 1124 def __init__(self, *args, **kwargs): 1125 def handle_kwarg(self, kwargs, key, elseVal = None): 1126 if key in kwargs: 1127 setattr(self, key, kwargs.pop(key)) 1128 elif elseVal: 1129 setattr(self, key, elseVal) 1130 else: 1131 raise TypeError("No value set for %s" % key) 1132 1133 if len(args) == 0: 1134 handle_kwarg(self, kwargs, 'ip') 1135 handle_kwarg(self, kwargs, 'port') 1136 1137 elif len(args) == 1: 1138 if kwargs: 1139 if not 'ip' in kwargs and not 'port' in kwargs: 1140 raise TypeError("Invalid arguments") 1141 handle_kwarg(self, kwargs, 'ip', args[0]) 1142 handle_kwarg(self, kwargs, 'port', args[0]) 1143 elif isinstance(args[0], IpWithPort): 1144 self.ip = args[0].ip 1145 self.port = args[0].port 1146 else: 1147 (self.ip, self.port) = convert.toIpWithPort(args[0]) 1148 1149 elif len(args) == 2: 1150 self.ip = args[0] 1151 self.port = args[1] 1152 else: 1153 raise TypeError("Too many arguments specified") 1154 1155 if kwargs: 1156 raise TypeError("Too many keywords: %s" % list(kwargs.keys())) 1157 1158 self.verify() 1159 1160 def __call__(self, value): 1161 self.__init__(value) 1162 return value 1163 1164 def __str__(self): 1165 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port) 1166 1167 def __eq__(self, other): 1168 if isinstance(other, IpWithPort): 1169 return self.ip == other.ip and self.port == other.port 1170 elif isinstance(other, str): 1171 try: 1172 return (self.ip, self.port) == convert.toIpWithPort(other) 1173 except: 1174 return False 1175 else: 1176 return False 1177 1178 def verify(self): 1179 self.verifyIp() 1180 if self.port < 0 or self.port > 0xffff: 1181 raise TypeError("invalid port %d" % self.port) 1182 1183 def getValue(self): 1184 from _m5.net import IpWithPort 1185 return IpWithPort(self.ip, self.port) 1186 1187time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 1188 "%a %b %d %H:%M:%S %Y", 1189 "%Y/%m/%d %H:%M:%S", 1190 "%Y/%m/%d %H:%M", 1191 "%Y/%m/%d", 1192 "%m/%d/%Y %H:%M:%S", 1193 "%m/%d/%Y %H:%M", 1194 "%m/%d/%Y", 1195 "%m/%d/%y %H:%M:%S", 1196 "%m/%d/%y %H:%M", 1197 "%m/%d/%y"] 1198 1199 1200def parse_time(value): 1201 from time import gmtime, strptime, struct_time, time 1202 from datetime import datetime, date 1203 1204 if isinstance(value, struct_time): 1205 return value 1206 1207 if isinstance(value, (int, long)): 1208 return gmtime(value) 1209 1210 if isinstance(value, (datetime, date)): 1211 return value.timetuple() 1212 1213 if isinstance(value, str): 1214 if value in ('Now', 'Today'): 1215 return time.gmtime(time.time()) 1216 1217 for format in time_formats: 1218 try: 1219 return strptime(value, format) 1220 except ValueError: 1221 pass 1222 1223 raise ValueError("Could not parse '%s' as a time" % value) 1224 1225class Time(ParamValue): 1226 cxx_type = 'tm' 1227 1228 @classmethod 1229 def cxx_predecls(cls, code): 1230 code('#include <time.h>') 1231 1232 def __init__(self, value): 1233 self.value = parse_time(value) 1234 1235 def __call__(self, value): 1236 self.__init__(value) 1237 return value 1238 1239 def getValue(self): 1240 from _m5.core import tm 1241 import calendar 1242 1243 return tm.gmtime(calendar.timegm(self.value)) 1244 1245 def __str__(self): 1246 return time.asctime(self.value) 1247 1248 def ini_str(self): 1249 return str(self) 1250 1251 def get_config_as_dict(self): 1252 assert false 1253 return str(self) 1254 1255 @classmethod 1256 def cxx_ini_predecls(cls, code): 1257 code('#include <time.h>') 1258 1259 @classmethod 1260 def cxx_ini_parse(cls, code, src, dest, ret): 1261 code('char *_parse_ret = strptime((${src}).c_str(),') 1262 code(' "%a %b %d %H:%M:%S %Y", &(${dest}));') 1263 code('${ret} _parse_ret && *_parse_ret == \'\\0\';'); 1264 1265# Enumerated types are a little more complex. The user specifies the 1266# type as Enum(foo) where foo is either a list or dictionary of 1267# alternatives (typically strings, but not necessarily so). (In the 1268# long run, the integer value of the parameter will be the list index 1269# or the corresponding dictionary value. For now, since we only check 1270# that the alternative is valid and then spit it into a .ini file, 1271# there's not much point in using the dictionary.) 1272 1273# What Enum() must do is generate a new type encapsulating the 1274# provided list/dictionary so that specific values of the parameter 1275# can be instances of that type. We define two hidden internal 1276# classes (_ListEnum and _DictEnum) to serve as base classes, then 1277# derive the new type from the appropriate base class on the fly. 1278 1279allEnums = {} 1280# Metaclass for Enum types 1281class MetaEnum(MetaParamValue): 1282 def __new__(mcls, name, bases, dict): 1283 assert name not in allEnums 1284 1285 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 1286 allEnums[name] = cls 1287 return cls 1288 1289 def __init__(cls, name, bases, init_dict): 1290 if 'map' in init_dict: 1291 if not isinstance(cls.map, dict): 1292 raise TypeError("Enum-derived class attribute 'map' " \ 1293 "must be of type dict") 1294 # build list of value strings from map 1295 cls.vals = list(cls.map.keys()) 1296 cls.vals.sort() 1297 elif 'vals' in init_dict: 1298 if not isinstance(cls.vals, list): 1299 raise TypeError("Enum-derived class attribute 'vals' " \ 1300 "must be of type list") 1301 # build string->value map from vals sequence 1302 cls.map = {} 1303 for idx,val in enumerate(cls.vals): 1304 cls.map[val] = idx 1305 else: 1306 raise TypeError("Enum-derived class must define "\ 1307 "attribute 'map' or 'vals'") 1308 1309 if cls.is_class: 1310 cls.cxx_type = '%s' % name 1311 else: 1312 cls.cxx_type = 'Enums::%s' % name 1313 1314 super(MetaEnum, cls).__init__(name, bases, init_dict) 1315 1316 # Generate C++ class declaration for this enum type. 1317 # Note that we wrap the enum in a class/struct to act as a namespace, 1318 # so that the enum strings can be brief w/o worrying about collisions. 1319 def cxx_decl(cls, code): 1320 wrapper_name = cls.wrapper_name 1321 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace' 1322 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1323 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name) 1324 1325 code('''\ 1326#ifndef $idem_macro 1327#define $idem_macro 1328 1329''') 1330 if cls.is_class: 1331 code('''\ 1332enum class $name { 1333''') 1334 else: 1335 code('''\ 1336$wrapper $wrapper_name { 1337 enum $name { 1338''') 1339 code.indent(1) 1340 code.indent(1) 1341 for val in cls.vals: 1342 code('$val = ${{cls.map[val]}},') 1343 code('Num_$name = ${{len(cls.vals)}}') 1344 code.dedent(1) 1345 code('};') 1346 1347 if cls.is_class: 1348 code('''\ 1349extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})]; 1350''') 1351 elif cls.wrapper_is_struct: 1352 code('static const char *${name}Strings[Num_${name}];') 1353 else: 1354 code('extern const char *${name}Strings[Num_${name}];') 1355 1356 if not cls.is_class: 1357 code.dedent(1) 1358 code('};') 1359 1360 code() 1361 code('#endif // $idem_macro') 1362 1363 def cxx_def(cls, code): 1364 wrapper_name = cls.wrapper_name 1365 file_name = cls.__name__ 1366 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1367 1368 code('#include "enums/$file_name.hh"') 1369 if cls.wrapper_is_struct: 1370 code('const char *${wrapper_name}::${name}Strings' 1371 '[Num_${name}] =') 1372 else: 1373 if cls.is_class: 1374 code('''\ 1375const char *${name}Strings[static_cast<int>(${name}::Num_${name})] = 1376''') 1377 else: 1378 code('namespace Enums {') 1379 code.indent(1) 1380 code('const char *${name}Strings[Num_${name}] =') 1381 1382 code('{') 1383 code.indent(1) 1384 for val in cls.vals: 1385 code('"$val",') 1386 code.dedent(1) 1387 code('};') 1388 1389 if not cls.wrapper_is_struct and not cls.is_class: 1390 code.dedent(1) 1391 code('} // namespace $wrapper_name') 1392 1393 1394 def pybind_def(cls, code): 1395 name = cls.__name__ 1396 enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name 1397 wrapper_name = enum_name if cls.is_class else cls.wrapper_name 1398 1399 code('''#include "pybind11/pybind11.h" 1400#include "pybind11/stl.h" 1401 1402#include <sim/init.hh> 1403 1404namespace py = pybind11; 1405 1406static void 1407module_init(py::module &m_internal) 1408{ 1409 py::module m = m_internal.def_submodule("enum_${name}"); 1410 1411''') 1412 if cls.is_class: 1413 code('py::enum_<${enum_name}>(m, "enum_${name}")') 1414 else: 1415 code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")') 1416 1417 code.indent() 1418 code.indent() 1419 for val in cls.vals: 1420 code('.value("${val}", ${wrapper_name}::${val})') 1421 code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})') 1422 code('.export_values()') 1423 code(';') 1424 code.dedent() 1425 1426 code('}') 1427 code.dedent() 1428 code() 1429 code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);') 1430 1431 1432# Base class for enum types. 1433class Enum(ParamValue): 1434 __metaclass__ = MetaEnum 1435 vals = [] 1436 cmd_line_settable = True 1437 1438 # The name of the wrapping namespace or struct 1439 wrapper_name = 'Enums' 1440 1441 # If true, the enum is wrapped in a struct rather than a namespace 1442 wrapper_is_struct = False 1443 1444 is_class = False 1445 1446 # If not None, use this as the enum name rather than this class name 1447 enum_name = None 1448 1449 def __init__(self, value): 1450 if value not in self.map: 1451 raise TypeError("Enum param got bad value '%s' (not in %s)" \ 1452 % (value, self.vals)) 1453 self.value = value 1454 1455 def __call__(self, value): 1456 self.__init__(value) 1457 return value 1458 1459 @classmethod 1460 def cxx_predecls(cls, code): 1461 code('#include "enums/$0.hh"', cls.__name__) 1462 1463 @classmethod 1464 def cxx_ini_parse(cls, code, src, dest, ret): 1465 code('if (false) {') 1466 for elem_name in cls.map.keys(): 1467 code('} else if (%s == "%s") {' % (src, elem_name)) 1468 code.indent() 1469 code('%s = Enums::%s;' % (dest, elem_name)) 1470 code('%s true;' % ret) 1471 code.dedent() 1472 code('} else {') 1473 code(' %s false;' % ret) 1474 code('}') 1475 1476 def getValue(self): 1477 import m5.internal.params 1478 e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__) 1479 return e(self.map[self.value]) 1480 1481 def __str__(self): 1482 return self.value 1483 1484# This param will generate a scoped c++ enum and its python bindings. 1485class ScopedEnum(Enum): 1486 __metaclass__ = MetaEnum 1487 vals = [] 1488 cmd_line_settable = True 1489 1490 # The name of the wrapping namespace or struct 1491 wrapper_name = None 1492 1493 # If true, the enum is wrapped in a struct rather than a namespace 1494 wrapper_is_struct = False 1495 1496 # If true, the generated enum is a scoped enum 1497 is_class = True 1498 1499 # If not None, use this as the enum name rather than this class name 1500 enum_name = None 1501 1502# how big does a rounding error need to be before we warn about it? 1503frequency_tolerance = 0.001 # 0.1% 1504 1505class TickParamValue(NumericParamValue): 1506 cxx_type = 'Tick' 1507 ex_str = "1MHz" 1508 cmd_line_settable = True 1509 1510 @classmethod 1511 def cxx_predecls(cls, code): 1512 code('#include "base/types.hh"') 1513 1514 def __call__(self, value): 1515 self.__init__(value) 1516 return value 1517 1518 def getValue(self): 1519 return long(self.value) 1520 1521 @classmethod 1522 def cxx_ini_predecls(cls, code): 1523 code('#include <sstream>') 1524 1525 # Ticks are expressed in seconds in JSON files and in plain 1526 # Ticks in .ini files. Switch based on a config flag 1527 @classmethod 1528 def cxx_ini_parse(self, code, src, dest, ret): 1529 code('${ret} to_number(${src}, ${dest});') 1530 1531class Latency(TickParamValue): 1532 ex_str = "100ns" 1533 1534 def __init__(self, value): 1535 if isinstance(value, (Latency, Clock)): 1536 self.ticks = value.ticks 1537 self.value = value.value 1538 elif isinstance(value, Frequency): 1539 self.ticks = value.ticks 1540 self.value = 1.0 / value.value 1541 elif value.endswith('t'): 1542 self.ticks = True 1543 self.value = int(value[:-1]) 1544 else: 1545 self.ticks = False 1546 self.value = convert.toLatency(value) 1547 1548 def __call__(self, value): 1549 self.__init__(value) 1550 return value 1551 1552 def __getattr__(self, attr): 1553 if attr in ('latency', 'period'): 1554 return self 1555 if attr == 'frequency': 1556 return Frequency(self) 1557 raise AttributeError("Latency object has no attribute '%s'" % attr) 1558 1559 def getValue(self): 1560 if self.ticks or self.value == 0: 1561 value = self.value 1562 else: 1563 value = ticks.fromSeconds(self.value) 1564 return long(value) 1565 1566 def config_value(self): 1567 return self.getValue() 1568 1569 # convert latency to ticks 1570 def ini_str(self): 1571 return '%d' % self.getValue() 1572 1573class Frequency(TickParamValue): 1574 ex_str = "1GHz" 1575 1576 def __init__(self, value): 1577 if isinstance(value, (Latency, Clock)): 1578 if value.value == 0: 1579 self.value = 0 1580 else: 1581 self.value = 1.0 / value.value 1582 self.ticks = value.ticks 1583 elif isinstance(value, Frequency): 1584 self.value = value.value 1585 self.ticks = value.ticks 1586 else: 1587 self.ticks = False 1588 self.value = convert.toFrequency(value) 1589 1590 def __call__(self, value): 1591 self.__init__(value) 1592 return value 1593 1594 def __getattr__(self, attr): 1595 if attr == 'frequency': 1596 return self 1597 if attr in ('latency', 'period'): 1598 return Latency(self) 1599 raise AttributeError("Frequency object has no attribute '%s'" % attr) 1600 1601 # convert latency to ticks 1602 def getValue(self): 1603 if self.ticks or self.value == 0: 1604 value = self.value 1605 else: 1606 value = ticks.fromSeconds(1.0 / self.value) 1607 return long(value) 1608 1609 def config_value(self): 1610 return self.getValue() 1611 1612 def ini_str(self): 1613 return '%d' % self.getValue() 1614 1615# A generic Frequency and/or Latency value. Value is stored as a 1616# latency, just like Latency and Frequency. 1617class Clock(TickParamValue): 1618 def __init__(self, value): 1619 if isinstance(value, (Latency, Clock)): 1620 self.ticks = value.ticks 1621 self.value = value.value 1622 elif isinstance(value, Frequency): 1623 self.ticks = value.ticks 1624 self.value = 1.0 / value.value 1625 elif value.endswith('t'): 1626 self.ticks = True 1627 self.value = int(value[:-1]) 1628 else: 1629 self.ticks = False 1630 self.value = convert.anyToLatency(value) 1631 1632 def __call__(self, value): 1633 self.__init__(value) 1634 return value 1635 1636 def __str__(self): 1637 return "%s" % Latency(self) 1638 1639 def __getattr__(self, attr): 1640 if attr == 'frequency': 1641 return Frequency(self) 1642 if attr in ('latency', 'period'): 1643 return Latency(self) 1644 raise AttributeError("Frequency object has no attribute '%s'" % attr) 1645 1646 def getValue(self): 1647 return self.period.getValue() 1648 1649 def config_value(self): 1650 return self.period.config_value() 1651 1652 def ini_str(self): 1653 return self.period.ini_str() 1654 1655class Voltage(Float): 1656 ex_str = "1V" 1657 1658 def __new__(cls, value): 1659 value = convert.toVoltage(value) 1660 return super(cls, Voltage).__new__(cls, value) 1661 1662 def __init__(self, value): 1663 value = convert.toVoltage(value) 1664 super(Voltage, self).__init__(value) 1665 1666class Current(Float): 1667 ex_str = "1mA" 1668 1669 def __new__(cls, value): 1670 value = convert.toCurrent(value) 1671 return super(cls, Current).__new__(cls, value) 1672 1673 def __init__(self, value): 1674 value = convert.toCurrent(value) 1675 super(Current, self).__init__(value) 1676 1677class Energy(Float): 1678 ex_str = "1pJ" 1679 1680 def __new__(cls, value): 1681 value = convert.toEnergy(value) 1682 return super(cls, Energy).__new__(cls, value) 1683 1684 def __init__(self, value): 1685 value = convert.toEnergy(value) 1686 super(Energy, self).__init__(value) 1687 1688class NetworkBandwidth(float,ParamValue): 1689 cxx_type = 'float' 1690 ex_str = "1Gbps" 1691 cmd_line_settable = True 1692 1693 def __new__(cls, value): 1694 # convert to bits per second 1695 val = convert.toNetworkBandwidth(value) 1696 return super(cls, NetworkBandwidth).__new__(cls, val) 1697 1698 def __str__(self): 1699 return str(self.val) 1700 1701 def __call__(self, value): 1702 val = convert.toNetworkBandwidth(value) 1703 self.__init__(val) 1704 return value 1705 1706 def getValue(self): 1707 # convert to seconds per byte 1708 value = 8.0 / float(self) 1709 # convert to ticks per byte 1710 value = ticks.fromSeconds(value) 1711 return float(value) 1712 1713 def ini_str(self): 1714 return '%f' % self.getValue() 1715 1716 def config_value(self): 1717 return '%f' % self.getValue() 1718 1719 @classmethod 1720 def cxx_ini_predecls(cls, code): 1721 code('#include <sstream>') 1722 1723 @classmethod 1724 def cxx_ini_parse(self, code, src, dest, ret): 1725 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1726 1727class MemoryBandwidth(float,ParamValue): 1728 cxx_type = 'float' 1729 ex_str = "1GB/s" 1730 cmd_line_settable = True 1731 1732 def __new__(cls, value): 1733 # convert to bytes per second 1734 val = convert.toMemoryBandwidth(value) 1735 return super(cls, MemoryBandwidth).__new__(cls, val) 1736 1737 def __call__(self, value): 1738 val = convert.toMemoryBandwidth(value) 1739 self.__init__(val) 1740 return value 1741 1742 def getValue(self): 1743 # convert to seconds per byte 1744 value = float(self) 1745 if value: 1746 value = 1.0 / float(self) 1747 # convert to ticks per byte 1748 value = ticks.fromSeconds(value) 1749 return float(value) 1750 1751 def ini_str(self): 1752 return '%f' % self.getValue() 1753 1754 def config_value(self): 1755 return '%f' % self.getValue() 1756 1757 @classmethod 1758 def cxx_ini_predecls(cls, code): 1759 code('#include <sstream>') 1760 1761 @classmethod 1762 def cxx_ini_parse(self, code, src, dest, ret): 1763 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1764 1765# 1766# "Constants"... handy aliases for various values. 1767# 1768 1769# Special class for NULL pointers. Note the special check in 1770# make_param_value() above that lets these be assigned where a 1771# SimObject is required. 1772# only one copy of a particular node 1773class NullSimObject(object): 1774 __metaclass__ = Singleton 1775 _name = 'Null' 1776 1777 def __call__(cls): 1778 return cls 1779 1780 def _instantiate(self, parent = None, path = ''): 1781 pass 1782 1783 def ini_str(self): 1784 return 'Null' 1785 1786 def unproxy(self, base): 1787 return self 1788 1789 def set_path(self, parent, name): 1790 pass 1791 1792 def set_parent(self, parent, name): 1793 pass 1794 1795 def clear_parent(self, old_parent): 1796 pass 1797 1798 def descendants(self): 1799 return 1800 yield None 1801 1802 def get_config_as_dict(self): 1803 return {} 1804 1805 def __str__(self): 1806 return self._name 1807 1808 def config_value(self): 1809 return None 1810 1811 def getValue(self): 1812 return None 1813 1814# The only instance you'll ever need... 1815NULL = NullSimObject() 1816 1817def isNullPointer(value): 1818 return isinstance(value, NullSimObject) 1819 1820# Some memory range specifications use this as a default upper bound. 1821MaxAddr = Addr.max 1822MaxTick = Tick.max 1823AllMemory = AddrRange(0, MaxAddr) 1824 1825 1826##################################################################### 1827# 1828# Port objects 1829# 1830# Ports are used to interconnect objects in the memory system. 1831# 1832##################################################################### 1833 1834# Port reference: encapsulates a reference to a particular port on a 1835# particular SimObject. 1836class PortRef(object): 1837 def __init__(self, simobj, name, role): 1838 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1839 self.simobj = simobj 1840 self.name = name 1841 self.role = role 1842 self.peer = None # not associated with another port yet 1843 self.ccConnected = False # C++ port connection done? 1844 self.index = -1 # always -1 for non-vector ports 1845 1846 def __str__(self): 1847 return '%s.%s' % (self.simobj, self.name) 1848 1849 def __len__(self): 1850 # Return the number of connected ports, i.e. 0 is we have no 1851 # peer and 1 if we do. 1852 return int(self.peer != None) 1853 1854 # for config.ini, print peer's name (not ours) 1855 def ini_str(self): 1856 return str(self.peer) 1857 1858 # for config.json 1859 def get_config_as_dict(self): 1860 return {'role' : self.role, 'peer' : str(self.peer)} 1861 1862 def __getattr__(self, attr): 1863 if attr == 'peerObj': 1864 # shorthand for proxies 1865 return self.peer.simobj 1866 raise AttributeError("'%s' object has no attribute '%s'" % \ 1867 (self.__class__.__name__, attr)) 1868 1869 # Full connection is symmetric (both ways). Called via 1870 # SimObject.__setattr__ as a result of a port assignment, e.g., 1871 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 1872 # e.g., "obj1.portA[3] = obj2.portB". 1873 def connect(self, other): 1874 if isinstance(other, VectorPortRef): 1875 # reference to plain VectorPort is implicit append 1876 other = other._get_next() 1877 if self.peer and not proxy.isproxy(self.peer): 1878 fatal("Port %s is already connected to %s, cannot connect %s\n", 1879 self, self.peer, other); 1880 self.peer = other 1881 if proxy.isproxy(other): 1882 other.set_param_desc(PortParamDesc()) 1883 elif isinstance(other, PortRef): 1884 if other.peer is not self: 1885 other.connect(self) 1886 else: 1887 raise TypeError("assigning non-port reference '%s' to port '%s'" \ 1888 % (other, self)) 1889 1890 # Allow a master/slave port pair to be spliced between 1891 # a port and its connected peer. Useful operation for connecting 1892 # instrumentation structures into a system when it is necessary 1893 # to connect the instrumentation after the full system has been 1894 # constructed. 1895 def splice(self, new_master_peer, new_slave_peer): 1896 if not self.peer or proxy.isproxy(self.peer): 1897 fatal("Port %s not connected, cannot splice in new peers\n", self) 1898 1899 if not isinstance(new_master_peer, PortRef) or \ 1900 not isinstance(new_slave_peer, PortRef): 1901 raise TypeError( 1902 "Splicing non-port references '%s','%s' to port '%s'" % \ 1903 (new_master_peer, new_slave_peer, self)) 1904 1905 old_peer = self.peer 1906 if self.role == 'SLAVE': 1907 self.peer = new_master_peer 1908 old_peer.peer = new_slave_peer 1909 new_master_peer.connect(self) 1910 new_slave_peer.connect(old_peer) 1911 elif self.role == 'MASTER': 1912 self.peer = new_slave_peer 1913 old_peer.peer = new_master_peer 1914 new_slave_peer.connect(self) 1915 new_master_peer.connect(old_peer) 1916 else: 1917 panic("Port %s has unknown role, "+\ 1918 "cannot splice in new peers\n", self) 1919 1920 def clone(self, simobj, memo): 1921 if self in memo: 1922 return memo[self] 1923 newRef = copy.copy(self) 1924 memo[self] = newRef 1925 newRef.simobj = simobj 1926 assert(isSimObject(newRef.simobj)) 1927 if self.peer and not proxy.isproxy(self.peer): 1928 peerObj = self.peer.simobj(_memo=memo) 1929 newRef.peer = self.peer.clone(peerObj, memo) 1930 assert(not isinstance(newRef.peer, VectorPortRef)) 1931 return newRef 1932 1933 def unproxy(self, simobj): 1934 assert(simobj is self.simobj) 1935 if proxy.isproxy(self.peer): 1936 try: 1937 realPeer = self.peer.unproxy(self.simobj) 1938 except: 1939 print("Error in unproxying port '%s' of %s" % 1940 (self.name, self.simobj.path())) 1941 raise 1942 self.connect(realPeer) 1943 1944 # Call C++ to create corresponding port connection between C++ objects 1945 def ccConnect(self): 1946 if self.ccConnected: # already done this 1947 return 1948 1949 peer = self.peer 1950 if not self.peer: # nothing to connect to 1951 return 1952 1953 port = self.simobj.getPort(self.name, self.index) 1954 peer_port = peer.simobj.getPort(peer.name, peer.index) 1955 port.bind(peer_port) 1956 1957 self.ccConnected = True 1958 1959# A reference to an individual element of a VectorPort... much like a 1960# PortRef, but has an index. 1961class VectorPortElementRef(PortRef): 1962 def __init__(self, simobj, name, role, index): 1963 PortRef.__init__(self, simobj, name, role) 1964 self.index = index 1965 1966 def __str__(self): 1967 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1968 1969# A reference to a complete vector-valued port (not just a single element). 1970# Can be indexed to retrieve individual VectorPortElementRef instances. 1971class VectorPortRef(object): 1972 def __init__(self, simobj, name, role): 1973 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1974 self.simobj = simobj 1975 self.name = name 1976 self.role = role 1977 self.elements = [] 1978 1979 def __str__(self): 1980 return '%s.%s[:]' % (self.simobj, self.name) 1981 1982 def __len__(self): 1983 # Return the number of connected peers, corresponding the the 1984 # length of the elements. 1985 return len(self.elements) 1986 1987 # for config.ini, print peer's name (not ours) 1988 def ini_str(self): 1989 return ' '.join([el.ini_str() for el in self.elements]) 1990 1991 # for config.json 1992 def get_config_as_dict(self): 1993 return {'role' : self.role, 1994 'peer' : [el.ini_str() for el in self.elements]} 1995 1996 def __getitem__(self, key): 1997 if not isinstance(key, int): 1998 raise TypeError("VectorPort index must be integer") 1999 if key >= len(self.elements): 2000 # need to extend list 2001 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i) 2002 for i in range(len(self.elements), key+1)] 2003 self.elements.extend(ext) 2004 return self.elements[key] 2005 2006 def _get_next(self): 2007 return self[len(self.elements)] 2008 2009 def __setitem__(self, key, value): 2010 if not isinstance(key, int): 2011 raise TypeError("VectorPort index must be integer") 2012 self[key].connect(value) 2013 2014 def connect(self, other): 2015 if isinstance(other, (list, tuple)): 2016 # Assign list of port refs to vector port. 2017 # For now, append them... not sure if that's the right semantics 2018 # or if it should replace the current vector. 2019 for ref in other: 2020 self._get_next().connect(ref) 2021 else: 2022 # scalar assignment to plain VectorPort is implicit append 2023 self._get_next().connect(other) 2024 2025 def clone(self, simobj, memo): 2026 if self in memo: 2027 return memo[self] 2028 newRef = copy.copy(self) 2029 memo[self] = newRef 2030 newRef.simobj = simobj 2031 assert(isSimObject(newRef.simobj)) 2032 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 2033 return newRef 2034 2035 def unproxy(self, simobj): 2036 [el.unproxy(simobj) for el in self.elements] 2037 2038 def ccConnect(self): 2039 [el.ccConnect() for el in self.elements] 2040 2041# Port description object. Like a ParamDesc object, this represents a 2042# logical port in the SimObject class, not a particular port on a 2043# SimObject instance. The latter are represented by PortRef objects. 2044class Port(object): 2045 # Generate a PortRef for this port on the given SimObject with the 2046 # given name 2047 def makeRef(self, simobj): 2048 return PortRef(simobj, self.name, self.role) 2049 2050 # Connect an instance of this port (on the given SimObject with 2051 # the given name) with the port described by the supplied PortRef 2052 def connect(self, simobj, ref): 2053 self.makeRef(simobj).connect(ref) 2054 2055 # No need for any pre-declarations at the moment as we merely rely 2056 # on an unsigned int. 2057 def cxx_predecls(self, code): 2058 pass 2059 2060 def pybind_predecls(self, code): 2061 cls.cxx_predecls(self, code) 2062 2063 # Declare an unsigned int with the same name as the port, that 2064 # will eventually hold the number of connected ports (and thus the 2065 # number of elements for a VectorPort). 2066 def cxx_decl(self, code): 2067 code('unsigned int port_${{self.name}}_connection_count;') 2068 2069class MasterPort(Port): 2070 # MasterPort("description") 2071 def __init__(self, *args): 2072 if len(args) == 1: 2073 self.desc = args[0] 2074 self.role = 'MASTER' 2075 else: 2076 raise TypeError('wrong number of arguments') 2077 2078class SlavePort(Port): 2079 # SlavePort("description") 2080 def __init__(self, *args): 2081 if len(args) == 1: 2082 self.desc = args[0] 2083 self.role = 'SLAVE' 2084 else: 2085 raise TypeError('wrong number of arguments') 2086 2087# VectorPort description object. Like Port, but represents a vector 2088# of connections (e.g., as on a XBar). 2089class VectorPort(Port): 2090 def __init__(self, *args): 2091 self.isVec = True 2092 2093 def makeRef(self, simobj): 2094 return VectorPortRef(simobj, self.name, self.role) 2095 2096class VectorMasterPort(VectorPort): 2097 # VectorMasterPort("description") 2098 def __init__(self, *args): 2099 if len(args) == 1: 2100 self.desc = args[0] 2101 self.role = 'MASTER' 2102 VectorPort.__init__(self, *args) 2103 else: 2104 raise TypeError('wrong number of arguments') 2105 2106class VectorSlavePort(VectorPort): 2107 # VectorSlavePort("description") 2108 def __init__(self, *args): 2109 if len(args) == 1: 2110 self.desc = args[0] 2111 self.role = 'SLAVE' 2112 VectorPort.__init__(self, *args) 2113 else: 2114 raise TypeError('wrong number of arguments') 2115 2116# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 2117# proxy objects (via set_param_desc()) so that proxy error messages 2118# make sense. 2119class PortParamDesc(object): 2120 __metaclass__ = Singleton 2121 2122 ptype_str = 'Port' 2123 ptype = Port 2124 2125baseEnums = allEnums.copy() 2126baseParams = allParams.copy() 2127 2128def clear(): 2129 global allEnums, allParams 2130 2131 allEnums = baseEnums.copy() 2132 allParams = baseParams.copy() 2133 2134__all__ = ['Param', 'VectorParam', 2135 'Enum', 'ScopedEnum', 'Bool', 'String', 'Float', 2136 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 2137 'Int32', 'UInt32', 'Int64', 'UInt64', 2138 'Counter', 'Addr', 'Tick', 'Percent', 2139 'TcpPort', 'UdpPort', 'EthernetAddr', 2140 'IpAddress', 'IpNetmask', 'IpWithPort', 2141 'MemorySize', 'MemorySize32', 2142 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy', 2143 'NetworkBandwidth', 'MemoryBandwidth', 2144 'AddrRange', 2145 'MaxAddr', 'MaxTick', 'AllMemory', 2146 'Time', 2147 'NextEthernetAddr', 'NULL', 2148 'MasterPort', 'SlavePort', 2149 'VectorMasterPort', 'VectorSlavePort'] 2150