#! /usr/bin/env python
import os
import sys
import logging
from argparse import ArgumentParser
import toolflow
# A straight lift from StackOverflow...
[docs]def shell_source(script):
"""Sometime you want to emulate the action of "source" in bash,
settings some environment variables. Here is a way to do it."""
import subprocess
import os
pipe = subprocess.Popen(". %s > /dev/null; env" % script,
stdout=subprocess.PIPE, shell=True)
output = pipe.communicate()[0]
env = dict((line.split("=", 1) for line in output.splitlines()))
os.environ.update(env)
if __name__ == '__main__':
parser = ArgumentParser(prog=os.path.basename(__file__))
parser.add_argument("--perfile", dest="perfile", action='store_true',
default=False, help="Run Frontend peripheral file generation")
parser.add_argument("--frontend", dest="frontend", action='store_true',
default=False, help="Run Frontend IP compile")
parser.add_argument("--middleware", dest="middleware", action='store_true',
default=False, help="Run Toolflow middle")
parser.add_argument("--backend", dest="backend", action='store_true',
default=False, help="Run backend compilation")
parser.add_argument("--software", dest="software", action='store_true',
default=False, help="Run software compilation")
parser.add_argument("--vitis", dest="vitis", action='store_true',
default=False, help="[EXPERIMENTAL] Run xsct (Vitis) to generate dtbo")
parser.add_argument("--xsa", dest="xsa", type=str, default='',
help="location of xsa file, uses backend generated if ran with backend option")
parser.add_argument("--be", dest="be", type=str, default='vivado',
help="Backend to use. Default: vivado")
parser.add_argument("--sysgen", dest="sysgen", type=str, default='',
help="Specify a specific sysgen startup script.")
parser.add_argument("--jobs", dest="jobs", type=int, default=4,
help="Number of cores to run compiles with. Default=4")
parser.add_argument("--threads", dest="threads", type=str, default='multi',
help="Processor threads to use for compiling - either multi or single. Default: multi")
parser.add_argument("--nonprojectmode", dest="nonprojectmode",
action='store_false', default=True,
help="Project Mode is enabled by default/Non Project Mode "
"is disabled by Default (NB: Vivado Only)")
parser.add_argument("-m", "--model", dest="model", type=str,
default='/tools/mlib_devel/jasper_library/test_models/'
'test.slx',
help="model to compile")
parser.add_argument("-c", "--builddir", dest="builddir", type=str,
default='',
help="build directory. Default: Use directory with same "
"name as model")
vivado_synth_strats_list = ['Flow_AreaOptimized_high',
'Flow_AreaOptimized_medium',
'Flow_AreaMultThresholdDSP',
'Flow_AlternateRoutability',
'Flow_PerfOptimized_high',
'Flow_PerfThresholdCarry',
'Flow_RuntimeOptimized']
vivado_synth_strats_str = '\n ||'.join(vivado_synth_strats_list)
parser.add_argument("--synth_strat", dest="synth_strat",
type=str, default=None,
help="Specify the Synthesis Strategy for your compile. "
"Your options are: ||{}".format(vivado_synth_strats_str))
vivado_impl_strats_list = ['Performance_Explore',
'Performance_ExplorePostRoutePhysOpt',
'Performance_ExploreWithRemap',
'Performance_WLBlockPlacement',
'Performance_WLBlockPlacementFanoutOpt',
'Performance_EarlyBlockPlacement',
'Performance_NetDelay_high',
'Performance_NetDelay_low',
'Performance_Retiming',
'Performance_ExtraTimingOpt',
'Performance_RefinePlacement',
'Performance_SpreadSLLs',
'Performance_BalanceSLLs',
'Performance_BalanceSLRs',
'Performance_HighUtilSLRs',
'Congestion_SpreadLogic_high',
'Congestion_SpreadLogic_medium',
'Congestion_SpreadLogic_low',
'Congestion_SSI_SpreadLogic_high',
'Congestion_SSI_SpreadLogic_low',
'Area_Explore',
'Area_ExploreSequential',
'Area_ExploreWithRemap',
'Power_DefaultOpt',
'Power_ExploreArea',
'Flow_RunPhysOpt',
'Flow_RunPostRoutePhysOpt',
'Flow_RuntimeOptimized',
'Flow_Quick']
vivado_impl_strats_str = ' \n ||'.join(vivado_impl_strats_list)
parser.add_argument("--impl_strat", dest="impl_strat",
type=str, default=None,
help="Specify the Implementation Strategy for your compile. "
"Your options are: ||{}".format(vivado_impl_strats_str))
opts = parser.parse_args()
sys.argv = [sys.argv[0]] # Keep only the script name. Flush other options
# if we don't have the environment set up, source the default config file
if 'XILINX_PATH' not in list(sorted(os.environ.keys())):
this_file_path = os.path.realpath(__file__)
config_file_path = os.path.join(os.path.dirname(os.path.dirname(this_file_path)), 'vivado_config.local')
if os.path.exists(config_file_path):
shell_source(config_file_path)
# get build directory
# use user defined directory else use a directory with same name as model
builddir = opts.builddir or opts.model[:-4]
# logging stuff...
os.system('mkdir -p %s' % builddir)
logger = logging.getLogger('jasper')
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler('%s/jasper.log' % builddir, mode='w')
handler.setLevel(logging.DEBUG)
logformat = logging.Formatter('%(levelname)s - %(asctime)s - %(name)s - '
'%(message)s')
handler.setFormatter(logformat)
logger.addHandler(handler)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
logger.addHandler(ch)
logger.info('Starting compile')
logger.info('Adding .gitignore')
with open(os.path.join(builddir, '.gitignore'), 'w') as fh:
fh.write('*\n')
fh.write('*/\n')
fh.write('!outputs/\n')
if opts.be == 'vivado':
os.environ['SYSGEN_SCRIPT'] = os.environ['MLIB_DEVEL_PATH'] + '/startsg'
if opts.synth_strat is not None:
# Check if the Strategy specified exists/is known
# - Looking for a direct match, albeit as lower-case
result = [synth_strat for synth_strat in vivado_synth_strats_list
if opts.synth_strat.lower() == synth_strat.lower()]
if len(result) < 1:
# Problem
errmsg = 'Synthesis Strategy specified is not supported - {}. ' \
'\nChoose from the following options:\n ||{}'.format(opts.synth_strat, vivado_synth_strats_str)
logger.error(errmsg)
raise ValueError
opts.synth_strat = result[0]
logger.debug('Using the following Synthesis Strategy: {}'.format(opts.synth_strat))
if opts.impl_strat is not None:
# Check if the Strategy specified exists/is known
# - Looking for a direct match, albeit as lower-case
result = [impl_strat for impl_strat in vivado_impl_strats_list
if opts.impl_strat.lower() == impl_strat.lower()]
if len(result) < 1:
# Problem
errmsg = 'Implementation Strategy specified is not supported - {}. ' \
'\nChoose from the following options:\n ||{}'.format(opts.impl_strat, vivado_impl_strats_str)
logger.error(errmsg)
raise ValueError
opts.impl_strat = result[0]
logger.debug('Using the following Implementation Strategy: {}'.format(opts.impl_strat))
logger.debug('Vivado compile will be executed.')
if opts.be == 'ise':
os.environ['SYSGEN_SCRIPT'] = os.environ['MLIB_DEVEL_PATH'] + '/startsg_ise'
logger.debug('ISE compile will be executed.')
if opts.sysgen != '':
os.environ['SYSGEN_SCRIPT'] = opts.sysgen
if not os.path.isfile(os.environ['SYSGEN_SCRIPT']):
raise RuntimeError('Could not find sysgen startup script: '
'%s' % os.environ['SYSGEN_SCRIPT'])
# initialise the toolflow
tf = toolflow.Toolflow(frontend='simulink', compile_dir=builddir,
frontend_target=opts.model, jobs=opts.jobs)
if opts.perfile:
tf.frontend.gen_periph_file(tf.periph_file)
tf.frontend.write_git_info_file()
if opts.frontend:
tf.frontend.compile_user_ip(update=True)
if opts.middleware:
tf.gen_periph_objs()
tf.build_top()
tf.generate_hdl()
tf.check_templates()
tf.generate_consts()
tf.write_core_info()
tf.write_core_jam_info()
tf.constraints_rule_check()
tf.dump_castro(tf.compile_dir+'/castro.yml')
if opts.backend or opts.software:
try:
platform = tf.plat
except AttributeError:
platform = None
# if vivado is selected to compile
if opts.be == 'vivado':
platform.backend_target = 'vivado'
# Project Mode assignment (True = Project Mode,
# False = Non-Project Mode)
platform.project_mode = opts.nonprojectmode
backend = toolflow.VivadoBackend(plat=platform,
compile_dir=tf.compile_dir,
periph_objs=tf.periph_objs)
backend.import_from_castro(backend.compile_dir + '/castro.yml')
backend.initialize()
# launch vivado via the generated .tcl file
backend.compile(cores=opts.jobs, plat=platform,
synth_strat=opts.synth_strat, impl_strat=opts.impl_strat, threads=opts.threads)
# if ISE is selected to compile
elif opts.be == 'ise':
platform.backend_target = 'ise'
# Project Mode assignment (True = Project Mode,
# False = Non-Project Mode). Not used in ISE.
platform.project_mode = opts.nonprojectmode
backend = toolflow.ISEBackend(plat=platform, compile_dir=tf.compile_dir)
backend.import_from_castro(backend.compile_dir + '/castro.yml')
backend.initialize()
# launch ISE via the generated .tcl file
backend.compile()
# Default to vivado for compile
else:
platform.backend_target = 'vivado'
# Project Mode assignment (True = Project Mode,
# False = Non-Project Mode)
platform.project_mode = opts.nonprojectmode
backend = toolflow.VivadoBackend(plat=platform,
compile_dir=tf.compile_dir)
backend.import_from_castro(backend.compile_dir + '/castro.yml')
backend.initialize()
# launch vivado via the generated .tcl file
backend.compile(cores=opts.jobs, plat=platform,
synth_strat=opts.synth_strat, impl_strat=opts.impl_strat, threads=opts.threads)
if opts.software:
binary = backend.bin_loc
bit_file = backend.bit_loc
hex_file = backend.hex_loc
mcs_file = backend.mcs_loc
prm_file = backend.prm_loc
bitstream = backend.bitstream_loc
backend.output_fpg = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d.fpg' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
#Generate the hex timestamp for the golden and multiboot images, if selected
if platform.boot_image == 'golden':
backend.output_bin = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_golden.bin' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
backend.output_hex = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_golden.hex' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
backend.output_mcs = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_golden.mcs' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
backend.output_prm = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_golden.prm' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
elif platform.boot_image == 'multiboot':
backend.output_bin = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_multiboot.bin' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
backend.output_hex = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_multiboot.hex' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
backend.output_mcs = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_multiboot.mcs' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
backend.output_prm = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d_multiboot.prm' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
#Only generate the bof and fpg files if a toolflow image
if platform.boot_image == 'toolflow':
# generate bot bof and fpg files for all platforms
backend.output_bof = tf.frontend_target_base[:-4]
backend.output_bof += '_%d-%02d-%02d_%02d%02d.bof' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
os.system('cp %s %s/top.bin' % (binary, backend.compile_dir))
os.system('cp %s %s/top.bit' % (bit_file, backend.compile_dir))
if platform.name.startswith("au"):
backend.mkfpg(bitstream, backend.output_fpg)
else:
backend.mkfpg(binary, backend.output_fpg)
print('Created %s/%s' % (backend.output_dir, backend.output_fpg))
# Only generate the hex and mcs files if a golden image or multiboot image
if platform.boot_image == 'golden' or platform.boot_image == 'multiboot':
os.system('cp %s %s/%s' % (
binary, backend.output_dir, backend.output_bin))
os.system('cp %s %s/%s' % (
hex_file, backend.output_dir, backend.output_hex))
os.system('cp %s %s/%s' % (
mcs_file, backend.output_dir, backend.output_mcs))
os.system('cp %s %s/%s' % (
prm_file, backend.output_dir, backend.output_prm))
print('Created bin file: %s/%s' % (backend.output_dir, backend.output_bin))
print('Created hex file: %s/%s' % (backend.output_dir, backend.output_hex))
print('Created mcs file: %s/%s' % (backend.output_dir, backend.output_mcs))
print('Created prm file: %s/%s' % (backend.output_dir, backend.output_prm))
# end
if opts.vitis:
if opts.backend:
xsa_loc = backend.xsa_loc
else:
if opts.xsa != '':
xsa_loc = opts.xsa
else:
raise RuntimeError('--xsa option must be provided when not running with the backend option')
vitis = toolflow.VitisBackend(xsa=xsa_loc, plat=None, compile_dir=tf.compile_dir, periph_objs=tf.periph_objs)
vitis.compile()
# running this seperate of `--backend` will require work of the user. The rfdc in casperfpga is currently
# expecting the `.dtbo` and `.fpg` live in the same place with the same name (different ext) and so a
# separate compilation will require the user to change the name of the `.dtbo` to match the `.fpg`
vitis.output_dtbo = tf.frontend_target_base[:-4] + '_%d-%02d-%02d_%02d%02d.dtbo' % (
tf.start_time.tm_year, tf.start_time.tm_mon, tf.start_time.tm_mday,
tf.start_time.tm_hour, tf.start_time.tm_min)
vitis.mkdtbo(vitis.dtsi_loc, os.path.join(vitis.output_dir, vitis.output_dtbo))
print('Created %s/%s' % (vitis.output_dir, vitis.output_dtbo))