| # Copyright (c) 2020 ARM Limited |
| # All rights reserved |
| # |
| # The license below extends only to copyright in the software and shall |
| # not be construed as granting a license to any other intellectual |
| # property including but not limited to intellectual property relating |
| # to a hardware implementation of the functionality of the software |
| # licensed hereunder. You may use the software subject to the license |
| # terms below provided that you ensure that this notice is replicated |
| # unmodified and in its entirety in all distributions of the software, |
| # modified or unmodified, in source code or in binary form. |
| # |
| # Copyright 2019 Google, Inc. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer; |
| # redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution; |
| # neither the name of the copyright holders nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| from itertools import cycle |
| import shlex |
| |
| Import('*') |
| |
| from m5.util.grammar import Grammar |
| |
| from gem5_scons import Transform, warning, error |
| |
| import os.path |
| |
| if env['USE_ARM_FASTMODEL']: |
| if not env['USE_SYSTEMC']: |
| warning('ARM Fast Models require systemc support') |
| env['USE_ARM_FASTMODEL'] = False |
| Return() |
| |
| if not env['USE_ARM_FASTMODEL']: |
| Return() |
| |
| |
| systemc_home = Dir('#/src/systemc/ext/systemc_home') |
| env['ENV']['SYSTEMC_HOME'] = systemc_home.abspath |
| |
| def extract_var(name): |
| if name not in env: |
| error('Error: %s is not set' % name) |
| print('%s = %s' % (name, env[name])) |
| # Make sure the value of this variable shows up as an environment variable |
| # for commands scons runs. |
| env['ENV'][name] = env[name] |
| return env[name] |
| |
| pvlib_home, maxcore_home, armlmd_license_file = \ |
| list(map(extract_var, ('PVLIB_HOME', 'MAXCORE_HOME', |
| 'ARMLMD_LICENSE_FILE'))) |
| |
| pvlib_home = Dir(pvlib_home) |
| maxcore_home = Dir(maxcore_home) |
| armlmd_license_file = File(armlmd_license_file) |
| |
| |
| pvlib_flavor = env['PVLIB_FLAVOR'] |
| pvlib_lib_dir = pvlib_home.Dir('lib').Dir(pvlib_flavor) |
| |
| simulation_engine_name = 'libMAXCOREInitSimulationEngine.3.so' |
| simulation_engine_lib = pvlib_lib_dir.File(simulation_engine_name) |
| |
| |
| def staticify(env, name): |
| ''' Check for a static version of the library named 'name', and if it |
| exists return a File node for it explicitly. Otherwise pass 'name' |
| through for normal processing.''' |
| |
| static_name = env.subst('${LIBPREFIX}' + name + '${LIBSUFFIX}') |
| |
| for path in env.Flatten(env['LIBPATH']): |
| full_name = Dir(path).File(static_name).get_abspath() |
| if os.path.isfile(full_name): |
| return File(full_name) |
| |
| return name |
| |
| |
| # Adjust the build environment to support building in Fast Models. |
| |
| env.Append(CCFLAGS='-pthread') |
| |
| cpppaths = ( |
| pvlib_home.Dir('include/fmruntime'), |
| pvlib_home.Dir('include/fmruntime/eslapi'), |
| pvlib_home.Dir('Iris/include'), |
| |
| systemc_home.Dir('include'), |
| |
| maxcore_home.Dir('AMBA-PV/include'), |
| ) |
| env.Append(CPPPATH=cpppaths) |
| |
| lib_paths = ( |
| pvlib_lib_dir, |
| pvlib_home.Dir('Iris').Dir(pvlib_flavor), |
| ) |
| env.Append(LIBPATH=lib_paths) |
| env.Append(RPATH=lib_paths) |
| |
| libs = ( |
| 'components', |
| 'pvbus', |
| 'armctmodel', |
| 'fmruntime', |
| 'IrisSupport', |
| 'atomic', |
| 'dl', |
| 'rt', |
| ) |
| env.Append(LIBS=list(staticify(env, lib) for lib in libs)) |
| |
| |
| class ProjectFileParser(Grammar): |
| class Param(object): |
| def __init__(self, is_object): |
| self.is_object = is_object |
| |
| def __str__(self): |
| return self._to_str(0) |
| |
| class StringParam(Param): |
| def __init__(self, name, value): |
| super(ProjectFileParser.StringParam, self).__init__( |
| is_object=False) |
| self.name = name |
| self.value = value |
| |
| def _to_str(self, indent): |
| indent_str = " " * indent |
| return indent_str + self.name + ' = \"' + self.value + '\"' |
| |
| class ObjectParam(Param): |
| def __init__(self, type_name, name, params): |
| super(ProjectFileParser.ObjectParam, self).__init__( |
| is_object=True) |
| self.type_name = type_name |
| self.name = name |
| self.params = params |
| |
| def _to_str(self, indent): |
| indent_str = " " * indent |
| val = indent_str + self.type_name |
| if self.name is not None: |
| val += ' "' + self.name + '"' |
| val += "\n" + indent_str + "{\n" |
| for param in self.params: |
| val += param._to_str(indent + 1) + "\n" |
| val += indent_str + "}" |
| return val |
| |
| ######### |
| # Lexer # |
| ######### |
| |
| tokens = ( |
| # identifier |
| 'ID', |
| |
| # string literal |
| 'STRLIT', |
| |
| # = { } ; |
| 'EQUALS', |
| 'LBRACE', |
| 'RBRACE', |
| 'SEMI', |
| ) |
| |
| t_ID = r'[A-Za-z_]\w*' |
| |
| def t_STRLIT(self, t): |
| r'(?m)"([^"])*"' |
| # strip off quotes |
| t.value = t.value[1:-1] |
| t.lexer.lineno += t.value.count('\n') |
| return t |
| |
| t_EQUALS = r'=' |
| t_LBRACE = r'\{' |
| t_RBRACE = r'\}' |
| t_SEMI = r';' |
| |
| def t_NEWLINE(self, t): |
| r'\n+' |
| t.lexer.lineno += t.value.count('\n') |
| |
| t_ignore = ' \t\x0c' |
| |
| ########## |
| # Parser # |
| ########## |
| |
| def p_object(self, t): |
| 'object : object_heading LBRACE params RBRACE' |
| t[0] = self.ObjectParam(t[1][0], t[1][1], t[3]) |
| |
| def p_object_heading_0(self, t): |
| 'object_heading : ID STRLIT' |
| t[0] = (t[1], t[2]) |
| |
| def p_object_heading_1(self, t): |
| 'object_heading : ID' |
| t[0] = (t[1], None) |
| |
| def p_params_0(self, t): |
| 'params : ' |
| t[0] = [] |
| |
| def p_params_1(self, t): |
| 'params : param params' |
| t[0] = [t[1]] + t[2] |
| |
| def p_param_0(self, t): |
| 'param : ID EQUALS STRLIT SEMI' |
| t[0] = self.StringParam(t[1], t[3]) |
| |
| def p_param_1(self, t): |
| 'param : object' |
| t[0] = t[1] |
| |
| |
| license_count = int(env['ARMLMD_LICENSE_COUNT']) |
| arm_licenses = list((Value(object()) for i in range(license_count))) |
| license_cycle = cycle(arm_licenses) |
| |
| class ArmFastModelComponent(object): |
| def __init__(self, project_file, *extra_deps): |
| project_file = File(project_file) |
| project_file_dir = project_file.Dir('.') |
| |
| parser = ProjectFileParser() |
| proj = parser.parse_file(project_file.srcnode().abspath) |
| |
| # Top level component. |
| tlc = None |
| # Config name. |
| config_name = None |
| |
| # Scan for particular properties of the project. |
| for param in proj.params: |
| if not param.is_object: |
| if param.name == 'TOP_LEVEL_COMPONENT': |
| tlc = param.value |
| elif param.name == 'ACTIVE_CONFIG_LINUX': |
| config_name = param.value |
| |
| assert tlc is not None and config_name is not None |
| |
| simgen_dir = project_file_dir.Dir(config_name) |
| |
| def simgen_static(name): |
| return simgen_dir.File(env.subst( |
| '${LIBPREFIX}%s${LIBSUFFIX}' % name)) |
| |
| def simgen_shared(name): |
| return simgen_dir.File(env.subst( |
| '${SHLIBPREFIX}%s${SHLIBSUFFIX}' % name)) |
| |
| static_libs = [ |
| 'scx-%s-%s' % (tlc, config_name), |
| 'scx', |
| ] |
| shared_libs = [ |
| '%s-%s' % (tlc, config_name), |
| ] |
| |
| static_lib_nodes = list(map(simgen_static, static_libs)) |
| shared_lib_nodes = list(map(simgen_shared, shared_libs)) |
| # We need to use the static libraries as files so that the linker |
| # doesn't use the shared versions of them instead. We need to use |
| # the shared libraries by name so that the linker will apply RPATH |
| # and be able to find them at run time. If a shared libary includes |
| # a path, the dynamic linker will apparently ignore RPATH when looking |
| # for it. |
| lib_nodes = static_lib_nodes + shared_lib_nodes |
| |
| gen_dir = simgen_dir.Dir('gen') |
| |
| header = gen_dir.File('scx_evs_%s.h' % tlc) |
| |
| self.headers = [header] |
| self.headerpaths = [gen_dir] |
| self.libs = static_lib_nodes + shared_libs |
| self.libpaths = [simgen_dir] |
| self.rpaths = [simgen_dir] |
| self.log = gen_dir.File('build_%s.log' % tlc) |
| self.simgen_cmd = env.subst('${SIMGEN} -p %s --configuration %s -b ' + |
| '--verbose off --num-build-cpus 100 --build-dir %s >%s') % \ |
| (shlex.quote(project_file.srcnode().abspath), |
| shlex.quote(config_name), |
| shlex.quote(simgen_dir.abspath), |
| shlex.quote(self.log.abspath)) |
| |
| sources = [project_file] |
| sources.extend(extra_deps) |
| env.Command(lib_nodes + self.headers + [self.log], sources, |
| Action(self.simgen_builder, Transform('SIMGEN'))) |
| # Distribute simgen actions among ARM license slots. All actions which |
| # have a given license as a "side effect" will be serialized relative |
| # to each other, meaning the number of licenses being used concurrently |
| # will never be larger than the number of license nodes. |
| # |
| # This allocation is fixed and may not be as optimal as a dynamic one, |
| # but the difference is probably not significant. |
| env.SideEffect(next(license_cycle), lib_nodes[0]) |
| |
| def prepare_env(self, env): |
| env.Append(LIBPATH=self.libpaths) |
| env.AddLocalRPATH(*self.rpaths) |
| env.Append(CPPPATH=self.headerpaths) |
| env.Prepend(LIBS=self.libs) |
| |
| def simgen_builder(self, target, source, env): |
| cmd = self.simgen_cmd |
| if not GetOption('verbose'): |
| cmd = "@" + cmd |
| res = env.Execute(cmd) |
| # Print output when execution return non-zero or in verbose mode. |
| if res or GetOption('verbose'): |
| env.Execute('@cat %s' % self.log.abspath) |
| return res |
| |
| |
| class ArmFastModelBin(Executable): |
| def __init__(self, target, *components_and_sources): |
| def is_component(x): |
| return isinstance(x, ArmFastModelComponent) |
| |
| def not_component(x): |
| return not is_component(x) |
| |
| components = list(filter(is_component, components_and_sources)) |
| sources = list(filter(not_component, components_and_sources)) |
| |
| self.components = components |
| super(ArmFastModelBin, self).__init__(target, *sources) |
| |
| @classmethod |
| def declare_all(cls, env): |
| env = env.Clone() |
| env.Prepend(LIBS=env['STATIC_LIB'][0]) |
| super(ArmFastModelBin, cls).declare_all(env) |
| |
| def declare(self, env): |
| env = env.Clone() |
| |
| sources = list(self.sources) |
| for f in self.filters: |
| sources += Source.all.apply_filter(f) |
| |
| objs = self.srcs_to_objs(env, sources) |
| objs = objs + env['MAIN_OBJS'] |
| |
| for component in self.components: |
| component.prepare_env(env) |
| |
| binary = super(ArmFastModelBin, self).declare(env, objs)[0] |
| |
| # We need a copy of the simulation engine lib alongside the executable |
| # so that the license check works properly. |
| local_engine = binary.File(simulation_engine_name) |
| Depends(binary, local_engine) |
| main.Command(local_engine, simulation_engine_lib, |
| MakeAction("cp ${SOURCE} ${TARGET}", Transform('COPY'))) |
| |
| Export('ArmFastModelComponent') |
| Export('ArmFastModelBin') |
| |
| PySource('m5', 'arm_fast_model.py') |
| Source('fastmodel.cc') |
| |
| SimObject('FastModel.py') |
| Source('amba_to_tlm_bridge.cc') |
| Source('amba_from_tlm_bridge.cc') |
| |
| # HACK: Make sure the gic protocol headers are somewhere we can find them. |
| # These should start out alongside other headers fast model provides which we |
| # are already able to include, but unfortunately they're in the examples |
| # directory. |
| gicv3_comms_headers = ( |
| 'gicv3_comms_base.h', 'gicv3_comms_if.h', 'gicv3_comms_sockets.h') |
| examples_common_dir = pvlib_home.Dir('examples/SystemCExport/Common') |
| gic_protocol_path = 'Protocols/GICv3Comms' |
| gic_protocol_dest = Dir(env['BUILDDIR']).Dir(gic_protocol_path) |
| gic_protocol_src = examples_common_dir.Dir(gic_protocol_path) |
| |
| for header in gicv3_comms_headers: |
| Command(gic_protocol_dest.File(header), gic_protocol_src.File(header), |
| Copy('${TARGET}', '${SOURCE}')) |
| |
| common_headers = ('lisa_protocol_types.h', 'tlm_has_get_protocol_types.h') |
| for header in common_headers: |
| header_target = gic_protocol_dest.Dir('include').File(header) |
| header_src = examples_common_dir.Dir('include').File(header) |
| Command(header_target, header_src, Copy('${TARGET}', '${SOURCE}')) |