| # 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 logging |
| import shlex |
| |
| Import('*') |
| |
| from grammar import Grammar |
| |
| from gem5_scons import Transform, warning, error |
| |
| import os.path |
| |
| if env['CONF']['USE_ARM_FASTMODEL']: |
| if env['CONF']['USE_SYSTEMC']: |
| env.TagImplies('arm fastmodel', 'arm isa') |
| else: |
| warning('ARM Fast Models require systemc support') |
| env['CONF']['USE_ARM_FASTMODEL'] = False |
| |
| |
| systemc_home = Dir('#/src/systemc/ext/systemc_home') |
| env['ENV']['SYSTEMC_HOME'] = systemc_home.abspath |
| |
| def extract_var(name): |
| val = env['CONF'].get(name, None) |
| if val is None: |
| error(f'{name} is not set') |
| if env['CONF']['USE_ARM_FASTMODEL']: |
| print(f'{name} = {val}') |
| # Make sure the value of this variable shows up as an environment variable |
| # for commands scons runs. |
| env['ENV'][name] = val |
| return val |
| |
| 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) |
| |
| pvlib_flavor = env['CONF']['PVLIB_FLAVOR'] |
| pvlib_lib_dir = pvlib_home.Dir('lib').Dir(pvlib_flavor) |
| |
| simulation_engine_name = 'libMAXCOREInitSimulationEngine.3.so' |
| simulation_engine_lib = env.Command( |
| Dir(env['BUILDDIR']).File(simulation_engine_name), |
| pvlib_lib_dir.File(simulation_engine_name), |
| MakeAction("cp ${SOURCE} ${TARGET}", Transform('COPY'))) |
| |
| arm_singleton_registry_name = 'arm_singleton_registry.so' |
| arm_singleton_registry_lib = env.Command( |
| Dir(env['BUILDDIR']).File(arm_singleton_registry_name), |
| pvlib_lib_dir.File(arm_singleton_registry_name), |
| MakeAction("cp ${SOURCE} ${TARGET}", Transform('COPY'))) |
| |
| |
| 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) |
| if env['CONF']['USE_ARM_FASTMODEL']: |
| warning("Failed to find FM static lib: " + name) |
| return name |
| |
| # Adjust the build environment to support building in Fast Models. |
| |
| env.Append(CCFLAGS='-pthread') |
| |
| cpppaths = ( |
| pvlib_home.Dir('examples/SystemCExport/Common/include'), |
| pvlib_home.Dir('include'), |
| 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) |
| |
| # Per ARM's 11.16 release note, a platform build with simgen automatically |
| # copies libraries into the build target directory along with the other |
| # dependencies. Therefore, we only need to add each simgen result into rpath and |
| # no other shared librarires are required here. |
| fm_static_libs = ( |
| 'components', |
| 'pvbus', |
| 'armctmodel', |
| 'fmruntime', |
| 'IrisSupport', |
| ) |
| for lib in fm_static_libs: |
| SourceLib(staticify(env, lib), tags='arm fastmodel') |
| |
| SourceLib('atomic', tags='arm fastmodel') |
| SourceLib('dl', tags='arm fastmodel') |
| SourceLib('rt', tags='arm fastmodel') |
| |
| class ProjectFileParser(Grammar): |
| def __init__(self): |
| self.log = logging.getLogger('fm_proj_ply') |
| if GetOption('verbose'): |
| self.log.setLevel(logging.DEBUG) |
| |
| self.yacc_kwargs['write_tables'] = False |
| |
| self.yacc_kwargs['debuglog'] = self.log |
| self.yacc_kwargs['errorlog'] = self.log |
| |
| self.lex_kwargs['debuglog'] = self.log |
| self.lex_kwargs['errorlog'] = self.log |
| |
| 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().__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().__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] |
| |
| |
| # If fast model is disabled, ARMLMD_LICENSE_COUNT will be 0 which will break |
| # the cycle() iterator below. The fast model components won't be built, but |
| # they still need to be set up successfully with valid license slots. |
| license_count = max(int(env['CONF']['ARMLMD_LICENSE_COUNT']), 1) |
| arm_licenses = list((Value(object()) for i in range(license_count))) |
| license_cycle = cycle(arm_licenses) |
| |
| # 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}')) |
| # Since we are copying the source files to a different directory, Scons's |
| # dependency scanner does not pick up the dependency between these files. |
| # Specify them manually. |
| env.Depends(gic_protocol_dest.File('gicv3_comms_base.h'), |
| gic_protocol_dest.File('gicv3_comms_if.h')) |
| env.Depends(gic_protocol_dest.File('gicv3_comms_sockets.h'), |
| gic_protocol_dest.File('gicv3_comms_if.h')) |
| |
| 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}')) |
| |
| class ArmFastModelComponent(object): |
| def __init__(self, project_file, *extra_deps, tags=None): |
| if not tags: |
| tags = ['arm fastmodel'] |
| self.tags = tags |
| 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 |
| # simgen_arg |
| simgen_command_line = '' |
| |
| # 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 |
| |
| for param in proj.params: |
| if param.name == config_name: |
| for sub_param in param.params: |
| if sub_param.name == 'SIMGEN_COMMAND_LINE': |
| simgen_command_line = sub_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] |
| # Simgen also puts required share library under the project folder. |
| self.rpaths = [simgen_dir, project_file_dir] |
| self.log = gen_dir.File('build_%s.log' % tlc) |
| self.simgen_cmd = env.subst('${CONF["SIMGEN"]} -p %s ' |
| '--configuration %s -b --verbose off --num-build-cpus %d %s ' |
| '--build-dir %s >%s') % \ |
| (shlex.quote(project_file.srcnode().abspath), |
| shlex.quote(config_name), |
| GetOption('num_jobs'), |
| simgen_command_line, |
| shlex.quote(simgen_dir.abspath), |
| shlex.quote(self.log.abspath)) |
| |
| sources = [project_file] |
| sources.extend(extra_deps) |
| # The simgen-generated .lisa files may #include these gicv3 files, but |
| # SCons does not detect this dependency since they are generated files. |
| # Add the dependencies manually. |
| sources.extend([gic_protocol_dest.File('gicv3_comms_sockets.h'), |
| gic_protocol_dest.File('gicv3_comms_base.h')]) |
| 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]) |
| |
| # We need a copy of the simulation engine lib and arm_singleton_registry |
| # (introduced in 11.16) alongside the executable. |
| Depends(lib_nodes[0], simulation_engine_lib) |
| Depends(lib_nodes[0], arm_singleton_registry_lib) |
| |
| def prepare_env(self, env): |
| env.Append(LIBPATH=self.libpaths) |
| env.AddLocalRPATH(*self.rpaths) |
| env.Append(CPPPATH=self.headerpaths) |
| # Put these libraries earlier in the list by setting priority. |
| for lib in self.libs: |
| SourceLib(lib, priority=1, tags=self.tags) |
| |
| 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 |
| |
| |
| Export('ArmFastModelComponent') |
| |
| PySource('m5', 'arm_fast_model.py', tags='arm fastmodel') |
| Source('fastmodel.cc', tags='arm fastmodel') |
| |
| SimObject('FastModel.py', sim_objects=[ |
| 'AmbaToTlmBridge64', 'AmbaFromTlmBridge64'], tags='arm fastmodel') |
| Source('amba_to_tlm_bridge.cc', tags='arm fastmodel') |
| Source('amba_from_tlm_bridge.cc', tags='arm fastmodel') |
| Source('remote_gdb.cc', tags='arm fastmodel') |