# 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 grammar import Grammar from gem5_scons import Transform, warning, error import os.path if env['CONF']['USE_ARM_FASTMODEL']: if not env['CONF']['USE_SYSTEMC']: warning('ARM Fast Models require systemc support') env['CONF']['USE_ARM_FASTMODEL'] = False Return() if not env['CONF']['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['CONF']: 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) 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) 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)) SourceLib('atomic') SourceLib('dl') SourceLib('rt') 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().__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] license_count = int(env['CONF']['ARMLMD_LICENSE_COUNT']) 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): 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] # 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 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) # 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) 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 Export('ArmFastModelComponent') PySource('m5', 'arm_fast_model.py') Source('fastmodel.cc') SimObject('FastModel.py', sim_objects=[ 'AmbaToTlmBridge64', 'AmbaFromTlmBridge64']) Source('amba_to_tlm_bridge.cc') Source('amba_from_tlm_bridge.cc')