blob: 35c0da1361210343c156eebb17c053bfce412a4d [file] [log] [blame]
# 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')