From cd2212abe3666af3464489d90c6e959afa31340d Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Mon, 19 Jun 2017 11:57:01 +1200 Subject: [PATCH 01/22] Initial click cli structure in place. Changed version string method to single source. --- hlsclt/__init__.py | 1 - hlsclt/_version.py | 1 + hlsclt/hlsclt.py | 23 ++++++++++++++++++++++- setup.py | 11 ++++++++--- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 hlsclt/_version.py diff --git a/hlsclt/__init__.py b/hlsclt/__init__.py index 9df94e5..143f486 100644 --- a/hlsclt/__init__.py +++ b/hlsclt/__init__.py @@ -1,2 +1 @@ # __init__.py -__version__ = '1.0.0.dev2' diff --git a/hlsclt/_version.py b/hlsclt/_version.py new file mode 100644 index 0000000..b06045d --- /dev/null +++ b/hlsclt/_version.py @@ -0,0 +1 @@ +__version__ = '1.0.0.dev3' diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index f0896cb..5dfc3bf 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -6,6 +6,8 @@ """ ### Imports ### +import click +from ._version import __version__ import os import sys import shutil @@ -14,6 +16,25 @@ import contextlib from distutils.util import strtobool +### New Click Stuff ### +@click.group() +@click.version_option(version=__version__) +def cli(): + """Helper tool for using Vivado HLS through the command line. If no arguments are specified then a default run is executed which includes C simulation, C synthesis, Cosimulation and export for both Vivado IP Catalog and System Generator. If any of the run options are specified then only those specified are performed.""" + pass +@cli.command('clean',short_help='Remove generated files.') +def clean(): + """Removes all Vivado HLS generated files and the generated Tcl build script.""" + click.echo("Clean Mode") +@cli.command('build',short_help='Run Vivado HLS build stages.') +def build(): + """Runs the Vivado HLS tool and executes the specified build stages.""" + click.echo("Build Mode") +@cli.command('report',short_help='Open reports.') +def report(): + """Opens the Vivado HLS report for the chosen build stages.""" + click.echo("Report Mode") + ### Class definitions ### class Error(Exception): """Base class for exceptions in this module.""" @@ -126,7 +147,7 @@ def main(): export_dsp_group = parser.add_mutually_exclusive_group() export_dsp_group.add_argument("-export_dsp", help="perform export for System Generator", action="store_true") export_dsp_group.add_argument("-evaluate_dsp", help="perform export for System Generator with build to place and route", action="store_true") - args = parser.parse_args() + #args = parser.parse_args() # Load project specifics from local config file and add to config dict config_loaded = get_vars_from_file('hls_config.py') diff --git a/setup.py b/setup.py index 5095365..30bc277 100644 --- a/setup.py +++ b/setup.py @@ -11,10 +11,15 @@ with open(path.join(here, 'README.md'), encoding='utf-8') as f: long_description = f.read() +# Get the version number +version = {} +with open("hlsclt/_version.py") as fp: + exec(fp.read(), version) + setup( name='hlsclt', - version='1.0.0.dev2', + version=version['__version__'], description='A Vivado HLS Command Line Helper Tool', long_description=long_description, @@ -41,10 +46,10 @@ packages=find_packages(), - install_requires=[], + install_requires=['Click'], entry_points = { - 'console_scripts': ['hlsclt=hlsclt.hlsclt:main'] + 'console_scripts': ['hlsclt=hlsclt.hlsclt:cli'] }, include_package_data=True, From 0089b5891729ce6cb74f79a768a60ad3256bf4fd Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Mon, 19 Jun 2017 15:00:46 +1200 Subject: [PATCH 02/22] Clean command finished. Config loading and parsing finished. --- hlsclt/hlsclt.py | 123 +++++++++++++++++++++++++++++++---------------- 1 file changed, 81 insertions(+), 42 deletions(-) diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index 5dfc3bf..9cfa764 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -16,25 +16,6 @@ import contextlib from distutils.util import strtobool -### New Click Stuff ### -@click.group() -@click.version_option(version=__version__) -def cli(): - """Helper tool for using Vivado HLS through the command line. If no arguments are specified then a default run is executed which includes C simulation, C synthesis, Cosimulation and export for both Vivado IP Catalog and System Generator. If any of the run options are specified then only those specified are performed.""" - pass -@cli.command('clean',short_help='Remove generated files.') -def clean(): - """Removes all Vivado HLS generated files and the generated Tcl build script.""" - click.echo("Clean Mode") -@cli.command('build',short_help='Run Vivado HLS build stages.') -def build(): - """Runs the Vivado HLS tool and executes the specified build stages.""" - click.echo("Build Mode") -@cli.command('report',short_help='Open reports.') -def report(): - """Opens the Vivado HLS report for the chosen build stages.""" - click.echo("Report Mode") - ### Class definitions ### class Error(Exception): """Base class for exceptions in this module.""" @@ -50,29 +31,34 @@ class ConfigError(Error): def __init__(self, message): self.message = message -### Support Functions ### -def try_delete(item): - try: - shutil.rmtree(item) - except OSError: - try: - os.remove(item) - except OSError: - return 1 - else: - return 0 - else: - return 0 +### New Click Stuff ### +def abort_if_false(ctx, param, value): + if not value: + ctx.abort() + +def generate_default_config(): + config = { + "project_name" : "proj_" + os.path.relpath(".",".."), + "top_level_function_name" : "", + "src_dir_name" : "src", + "tb_dir_name" : "tb", + "src_files" : "", + "tb_files" : "", + "part_name" : "", + "clock_period" : "", + "language" : "vhdl", + } + return config def get_vars_from_file(filename): import imp try: - with open(filename) as f: + with click.open_file(filename) as f: config = imp.load_source('config', '', f) return config except OSError: print("Error: No hls_config.py found, please create a config file for your project. For an example config file please see the 'examples' folder within the hlsclt install directory.") - sys.exit() + raise click.Abort() def parse_config_vars(config_loaded, config, errors): config_loaded_dict = dict((name, getattr(config_loaded, name)) for name in dir(config_loaded) if not name.startswith('__')) @@ -89,6 +75,66 @@ def parse_config_vars(config_loaded, config, errors): errors.append(err) continue +def try_delete(item): + try: + shutil.rmtree(item) + except OSError: + try: + os.remove(item) + except OSError: + return 1 + else: + return 0 + else: + return 0 + +def clean_up_generated_files(config): + if try_delete(config["project_name"]) + try_delete("run_hls.tcl") + try_delete("vivado_hls.log") == 3: + click.echo("Warning: Nothing to remove!") + else: + click.echo("Cleaned up generated files.") + +@click.group() +@click.version_option(version=__version__) +@click.pass_context +def cli(ctx): + """Helper tool for using Vivado HLS through the command line. If no arguments are specified then a default run is executed which includes C simulation, C synthesis, Cosimulation and export for both Vivado IP Catalog and System Generator. If any of the run options are specified then only those specified are performed.""" + config = generate_default_config(); + config_loaded = get_vars_from_file('hls_config.py') + errors = [] + parse_config_vars(config_loaded, config, errors) + if len(errors) != 0: + for err in errors: + print(err) + print("Config Errors, exiting...") + raise click.Abort() + ctx.obj = config + pass +@cli.command('clean',short_help='Remove generated files.') +@click.option('--yes', is_flag=True, callback=abort_if_false, + expose_value=False, + prompt='Are you sure you want to remove all generated files?') +@click.pass_obj +def clean(config): + """Removes all Vivado HLS generated files and the generated Tcl build script.""" + clean_up_generated_files(config) +@cli.command('build',short_help='Run Vivado HLS build stages.') +def build(): + """Runs the Vivado HLS tool and executes the specified build stages.""" + click.echo("Build Mode") +@cli.command('report',short_help='Open reports.') +def report(): + """Opens the Vivado HLS report for the chosen build stages.""" + click.echo("Report Mode") + + +### Support Functions ### + + + + + + def just_loop_on(input): if isinstance(input, str): yield input @@ -150,14 +196,7 @@ def main(): #args = parser.parse_args() # Load project specifics from local config file and add to config dict - config_loaded = get_vars_from_file('hls_config.py') - errors = [] - parse_config_vars(config_loaded, config, errors) - if len(errors) != 0: - for err in errors: - print(err) - print("Config Errors, exiting...") - sys.exit() + # Check for clean argument if args.clean: From ff9abdfe97cc093cf31d47bd5dca36abf226b80e Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Mon, 19 Jun 2017 16:51:24 +1200 Subject: [PATCH 03/22] Added click structure for nested build commands. --- hlsclt/hlsclt.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index 9cfa764..037b63d 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -118,10 +118,28 @@ def cli(ctx): def clean(config): """Removes all Vivado HLS generated files and the generated Tcl build script.""" clean_up_generated_files(config) -@cli.command('build',short_help='Run Vivado HLS build stages.') -def build(): +@cli.group(chain=True, invoke_without_command=True, short_help='Run HLS build stages.') +@click.option('--keep', is_flag=True) +@click.option('--report', is_flag=True) +def build(keep,report): """Runs the Vivado HLS tool and executes the specified build stages.""" click.echo("Build Mode") + pass +@build.command('csim') +@click.pass_obj +def csim(config): + """Runs the Vivado HLS C simulation stage.""" + click.echo("Csim Mode") +@build.command('syn') +@click.pass_obj +def syn(config): + """Runs the Vivado HLS C synthesis stage.""" + click.echo("Syn Mode") +@build.command('cosim') +@click.pass_obj +def cosim(config): + """Runs the Vivado HLS cosimulation stage.""" + click.echo("Cosim Mode") @cli.command('report',short_help='Open reports.') def report(): """Opens the Vivado HLS report for the chosen build stages.""" @@ -131,10 +149,6 @@ def report(): ### Support Functions ### - - - - def just_loop_on(input): if isinstance(input, str): yield input From c6dee31dfc7aa9cf107a97a0b59b2951eaabc39c Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Tue, 20 Jun 2017 12:27:10 +1200 Subject: [PATCH 04/22] Functionality from 1.0.0.dev2 release recreated using click framwork. --- hlsclt/hlsclt.py | 331 ++++++++++++++++++++++++----------------------- 1 file changed, 169 insertions(+), 162 deletions(-) diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index 037b63d..62e6b26 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -31,6 +31,13 @@ class ConfigError(Error): def __init__(self, message): self.message = message +class hlsclt_internal_object(object): + def __init__(self, config={}, solution_num=1, file=None, syn_command_present=False): + self.config = config + self.solution_num = solution_num + self.file=file + self.syn_command_present = syn_command_present + ### New Click Stuff ### def abort_if_false(ctx, param, value): if not value: @@ -88,12 +95,119 @@ def try_delete(item): else: return 0 -def clean_up_generated_files(config): +def clean_up_generated_files(obj): + config = obj.config if try_delete(config["project_name"]) + try_delete("run_hls.tcl") + try_delete("vivado_hls.log") == 3: click.echo("Warning: Nothing to remove!") else: click.echo("Cleaned up generated files.") +def find_solution_num(ctx): + config = ctx.obj.config + # Find solution_num + paths = glob(config["project_name"] + "/solution*/") + solution_num = len(paths) + if solution_num == 0: + solution_num = 1; + elif ctx.params["keep"]: + solution_num = solution_num + 1 + return solution_num + +def do_start_build_stuff(ctx): + config = ctx.obj.config + solution_num = ctx.obj.solution_num + try: + file = click.open_file("run_hls.tcl","w") + file.write("open_project " + config["project_name"] + "\n") + file.write("set_top " + config["top_level_function_name"] + "\n") + for src_file in config["src_files"]: + file.write("add_files " + config["src_dir_name"] + "/" + src_file + "\n") + for tb_file in config["tb_files"]: + file.write("add_files -tb " + config["tb_dir_name"] + "/" + tb_file + "\n") + if ctx.params['keep']: + file.write("open_solution -reset \"solution" + str(solution_num) + "\"" + "\n") + else: + file.write("open_solution \"solution" + str(solution_num) + "\"" + "\n") + file.write("set_part \{" + config["part_name"] + "\}" + "\n") + file.write("create_clock -period " + config["clock_period"] + " -name default" + "\n") + return file + except OSError: + click.echo("Woah! Couldn't create a Tcl run file in the current folder!") + raise click.Abort() + +def do_default_build(ctx): + config = ctx.obj.config + file = ctx.obj.file + file.write("csim_design -clean" + "\n") + file.write("csynth_design" + "\n") + file.write("cosim_design -O -rtl " + config["language"] + "\n") + file.write("export_design -format ip_catalog" + "\n") + file.write("export_design -format sysgen" + "\n") + +def do_csim_stuff(ctx): + file = ctx.obj.file + file.write("csim_design -clean" + "\n") + +def do_syn_stuff(ctx): + file = ctx.obj.file + file.write("csynth_design" + "\n") + +def check_for_syn_results(proj_name, solution_num, top_level_function_name): + return_val = False + try: + with click.open_file(proj_name + "/solution" + str(solution_num) + "/syn/report/" + top_level_function_name + "_csynth.rpt"): + return_val = True + except OSError: + pass + return return_val + +def syn_lookahead_check(ctx): + config = ctx.obj.config + solution_num = ctx.obj.solution_num + file = ctx.obj.file + if (not ctx.obj.syn_command_present) and (not check_for_syn_results(config["project_name"], solution_num, config["top_level_function_name"])): + if click.confirm("C Synthesis has not yet been run but is required for the process(es) you have selected.\nWould you like to add it to this run?", default=True): + click.echo("Adding csynth option.") + file.write("csynth_design" + "\n") + else: + click.echo("Ok, watch out for missing synthesis errors!") + +def do_cosim_stuff(ctx,debug): + config = ctx.obj.config + file = ctx.obj.file + if debug: + for language in just_loop_on(config["language"]): + file.write("cosim_design -rtl " + language + " -trace_level all" + "\n") + else: + for language in just_loop_on(config["language"]): + file.write("cosim_design -O -rtl " + language + "\n") + +def just_loop_on(input): + if isinstance(input, str): + yield input + else: + try: + for item in input: + yield item + except TypeError: + yield input + +def do_export_stuff(ctx,type,evaluate): + config = ctx.obj.config + file = ctx.obj.file + if evaluate: + if "ip" in type: + for language in just_loop_on(config["language"]): + file.write("export_design -format ip_catalog -evaluate " + language + "\n") + if "sysgen" in type: + for language in just_loop_on(config["language"]): + file.write("export_design -format sysgen -evaluate " + language + "\n") + else: + if "ip" in type: + file.write("export_design -format ip_catalog" + "\n") + if "sysgen" in type: + file.write("export_design -format sysgen" + "\n") + @click.group() @click.version_option(version=__version__) @click.pass_context @@ -108,183 +222,76 @@ def cli(ctx): print(err) print("Config Errors, exiting...") raise click.Abort() - ctx.obj = config + obj = hlsclt_internal_object(config) + ctx.obj = obj pass + @cli.command('clean',short_help='Remove generated files.') @click.option('--yes', is_flag=True, callback=abort_if_false, expose_value=False, - prompt='Are you sure you want to remove all generated files?') + prompt='Are you sure you want to remove all generated files?', + help='Force quiet removal.') @click.pass_obj -def clean(config): +def clean(obj): """Removes all Vivado HLS generated files and the generated Tcl build script.""" - clean_up_generated_files(config) + clean_up_generated_files(obj) + @cli.group(chain=True, invoke_without_command=True, short_help='Run HLS build stages.') -@click.option('--keep', is_flag=True) -@click.option('--report', is_flag=True) -def build(keep,report): +@click.option('-k','--keep', is_flag=True, help='Preserves existing solutions and creates a new one.') +@click.option('-r','--report', is_flag=True, help='Open build reports when finished.') +@click.pass_context +def build(ctx,keep,report): """Runs the Vivado HLS tool and executes the specified build stages.""" - click.echo("Build Mode") + ctx.obj.solution_num = find_solution_num(ctx) + ctx.obj.file = do_start_build_stuff(ctx) pass + +@build.resultcallback() +@click.pass_context +def build_end_callback(ctx,sub_command_returns,keep,report): + if not any(sub_command_returns): + if click.confirm("No build stages specified, would you like to run a default sequence using all the build stages?", abort=True): + do_default_build(ctx) + ctx.obj.file.write("exit" + "\n") + ctx.obj.file.close() + # Call the Vivado HLS process + os.system("vivado_hls -f run_hls.tcl") + @build.command('csim') -@click.pass_obj -def csim(config): +@click.pass_context +def csim(ctx): """Runs the Vivado HLS C simulation stage.""" - click.echo("Csim Mode") + do_csim_stuff(ctx) + return True + @build.command('syn') -@click.pass_obj -def syn(config): +@click.pass_context +def syn(ctx): """Runs the Vivado HLS C synthesis stage.""" - click.echo("Syn Mode") + do_syn_stuff(ctx) + ctx.obj.syn_command_present = True + return True + @build.command('cosim') -@click.pass_obj -def cosim(config): +@click.option('-d', '--debug', is_flag=True, help='Turns off compile optimisations and enables logging for cosim.') +@click.pass_context +def cosim(ctx,debug): """Runs the Vivado HLS cosimulation stage.""" - click.echo("Cosim Mode") + syn_lookahead_check(ctx) + do_cosim_stuff(ctx,debug) + return True + +@build.command('export') +@click.option('-t', '--type', required=True, multiple=True, type=click.Choice(['ip','sysgen']), help='Specify an export type, Vivado IP Catalog or System Generator. Accepts multiple occurances.') +@click.option('-e', '--evaluate', is_flag=True, help='Runs Vivado synthesis and place and route for the generated export.') +@click.pass_context +def export(ctx, type, evaluate): + """Runs the Vivado HLS export stage.""" + syn_lookahead_check(ctx) + do_export_stuff(ctx,type,evaluate) + return True + @cli.command('report',short_help='Open reports.') def report(): """Opens the Vivado HLS report for the chosen build stages.""" click.echo("Report Mode") - - -### Support Functions ### - - -def just_loop_on(input): - if isinstance(input, str): - yield input - else: - try: - for item in input: - yield item - except TypeError: - yield input - -def check_for_syn_results(proj_name, solution_num, top_level_function_name): - return_val = False - try: - with open(proj_name + "/solution" + str(solution_num) + "/syn/report/" + top_level_function_name + "_csynth.rpt"): - return_val = True - except OSError: - pass - return return_val - -def prompt(query): - sys.stdout.write('%s [y/n]: ' % query) - val = input() - try: - ret = strtobool(val) - except ValueError: - sys.stdout.write('Please answer with a y/n\n') - return prompt(query) - return ret - -def main(): - # Set up default config dictionary - config = { - "project_name" : "proj_" + os.path.relpath(".",".."), - "top_level_function_name" : "", - "src_dir_name" : "src", - "tb_dir_name" : "tb", - "src_files" : "", - "tb_files" : "", - "part_name" : "", - "clock_period" : "", - "language" : "vhdl", - } - - # Parse command line arguments - parser = argparse.ArgumentParser(description="Helper tool for using Vivado HLS through the command line. If no arguments are specified then a default run is executed which includes C simulation, C synthesis, Cosimulation and export for both Vivado IP Catalog and System Generator. If any of the run options are specified then only those specified are performed.") - parser.add_argument("-clean", help="remove all Vivado HLS generated files", action="store_true") - parser.add_argument("-keep", help="keep all previous solution and generate a new one", action="store_true") - parser.add_argument("-csim", help="perform C simulation stage", action="store_true") - parser.add_argument("-syn", help="perform C synthesis stage", action="store_true") - cosim_group = parser.add_mutually_exclusive_group() - cosim_group.add_argument("-cosim", help="perform cosimulation", action="store_true") - cosim_group.add_argument("-cosim_debug", help="perform cosimulation with debug logging", action="store_true") - export_ip_group = parser.add_mutually_exclusive_group() - export_ip_group.add_argument("-export_ip", help="perform export for Vivado IP Catalog", action="store_true") - export_ip_group.add_argument("-evaluate_ip", help="perform export for Vivado IP Catalog with build to place and route", action="store_true") - export_dsp_group = parser.add_mutually_exclusive_group() - export_dsp_group.add_argument("-export_dsp", help="perform export for System Generator", action="store_true") - export_dsp_group.add_argument("-evaluate_dsp", help="perform export for System Generator with build to place and route", action="store_true") - #args = parser.parse_args() - - # Load project specifics from local config file and add to config dict - - - # Check for clean argument - if args.clean: - if len(sys.argv) > 2: - print("Warning: The 'Clean' option is exclusive. All other arguments will be ignored.") - if try_delete(config["project_name"]) + try_delete("run_hls.tcl") + try_delete("vivado_hls.log") == 3: - print("Warning: Nothing to remove!") - else: - print("Cleaned up generated files.") - sys.exit() - - # Find solution_num - paths = glob(config["project_name"] + "/solution*/") - solution_num = len(paths) - if solution_num == 0: - solution_num = 1; - elif args.keep: - solution_num = solution_num + 1 - - # Write out TCL file - file = open("run_hls.tcl","w") - file.write("open_project " + config["project_name"] + "\n") - file.write("set_top " + config["top_level_function_name"] + "\n") - for src_file in config["src_files"]: - file.write("add_files " + config["src_dir_name"] + "/" + src_file + "\n") - for tb_file in config["tb_files"]: - file.write("add_files -tb " + config["tb_dir_name"] + "/" + tb_file + "\n") - if args.keep: - file.write("open_solution -reset \"solution" + str(solution_num) + "\"" + "\n") - else: - file.write("open_solution \"solution" + str(solution_num) + "\"" + "\n") - file.write("set_part \{" + config["part_name"] + "\}" + "\n") - file.write("create_clock -period " + config["clock_period"] + " -name default" + "\n") - - if not(args.csim or args.syn or args.cosim or args.cosim_debug or args.export_ip or args.export_dsp or args.evaluate_ip or args.evaluate_dsp): - file.write("csim_design -clean" + "\n") - file.write("csynth_design" + "\n") - file.write("cosim_design -O -rtl " + config["language"] + "\n") - file.write("export_design -format ip_catalog" + "\n") - file.write("export_design -format sysgen" + "\n") - file.write("exit" + "\n") - else: - if args.csim: - file.write("csim_design -clean" + "\n") - if args.syn: - file.write("csynth_design" + "\n") - # Check for arguments which will require csynth where syn has not been passed as an argument - if (not args.syn) and (args.cosim or args.cosim_debug or args.export_ip or args.export_dsp or args.evaluate_ip or args.evaluate_dsp): - if not check_for_syn_results(config["project_name"], solution_num, config["top_level_function_name"]): - if prompt("C Synthesis has not yet been run but is required for the process(es) you have selected.\nWould you like to add it to this run?"): - print("Adding csynth option.") - file.write("csynth_design" + "\n") - else: - print("Ok, watch out for missing synthesis errors!") - if args.cosim: - for language in just_loop_on(config["language"]): - file.write("cosim_design -O -rtl " + language + "\n") - if args.cosim_debug: - for language in just_loop_on(config["language"]): - file.write("cosim_design -rtl " + language + " -trace_level all" + "\n") - if args.export_dsp: - file.write("export_design -format ip_catalog" + "\n") - if args.export_ip: - file.write("export_design -format sysgen" + "\n") - if args.evaluate_dsp: - for language in just_loop_on(config["language"]): - file.write("export_design -format ip_catalog -evaluate " + language + "\n") - if args.evaluate_ip: - for language in just_loop_on(config["language"]): - file.write("export_design -format sysgen -evaluate " + language + "\n") - file.write("exit") - file.close() - - # Call the Vivado HLS process - os.system("vivado_hls -f run_hls.tcl") - -if __name__ == "__main__": main() From 439802817e7ec56d9eb8df5068b8f1b012c3c3c4 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Tue, 20 Jun 2017 16:29:14 +1200 Subject: [PATCH 05/22] Restructured package to increase readibility. --- hlsclt/classes.py | 29 ++++ hlsclt/clean/__init__.py | 0 hlsclt/clean/clean_commands.py | 50 ++++++ hlsclt/helper_funcs.py | 53 ++++++ hlsclt/hlsclt.py | 279 ++----------------------------- hlsclt/report/__init__.py | 0 hlsclt/report/report_commands.py | 15 ++ 7 files changed, 159 insertions(+), 267 deletions(-) create mode 100644 hlsclt/classes.py create mode 100644 hlsclt/clean/__init__.py create mode 100644 hlsclt/clean/clean_commands.py create mode 100644 hlsclt/helper_funcs.py create mode 100644 hlsclt/report/__init__.py create mode 100644 hlsclt/report/report_commands.py diff --git a/hlsclt/classes.py b/hlsclt/classes.py new file mode 100644 index 0000000..14f8a6f --- /dev/null +++ b/hlsclt/classes.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +""" Class definitions for the HLSCLT Command Line Tool. + +Copyright (c) 2017 Ben Marshall +""" + +# Generic error class +class Error(Exception): + """Base class for exceptions in this module.""" + pass + +# Specific error class for local config file errors +class ConfigError(Error): + """Exception raised for options not defined in config. + + Attributes: + message -- explanation of the error + """ + + def __init__(self, message): + self.message = message + +# Class to hold application specific info within the Click context. +class hlsclt_internal_object(object): + def __init__(self, config={}, solution_num=1, file=None, syn_command_present=False): + self.config = config + self.solution_num = solution_num + self.file=file + self.syn_command_present = syn_command_present diff --git a/hlsclt/clean/__init__.py b/hlsclt/clean/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hlsclt/clean/clean_commands.py b/hlsclt/clean/clean_commands.py new file mode 100644 index 0000000..e584687 --- /dev/null +++ b/hlsclt/clean/clean_commands.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +""" Clean up subcommands for HLSCLT. + +Copyright (c) 2017 Ben Marshall +""" + +### Imports ### +import click +import shutil +import os + +### Supporting Functions### +# Callback function used to exit the program on a negative user prompt response +def abort_if_false(ctx, param, value): + if not value: + ctx.abort() + +# Function to safely handle file deletions and return status +def try_delete(item): + try: + shutil.rmtree(item) + except OSError: + try: + os.remove(item) + except OSError: + return 1 + else: + return 0 + else: + return 0 + +# Funtion to remove generated files +def clean_up_generated_files(obj): + config = obj.config + if try_delete(config["project_name"]) + try_delete("run_hls.tcl") + try_delete("vivado_hls.log") == 3: + click.echo("Warning: Nothing to remove!") + else: + click.echo("Cleaned up generated files.") + +### Click Command Definitions ### +# Clean Command +@click.command('clean',short_help='Remove generated files.') +@click.option('--yes', is_flag=True, callback=abort_if_false, + expose_value=False, + prompt='Are you sure you want to remove all generated files?', + help='Force quiet removal.') +@click.pass_obj +def clean(obj): + """Removes all Vivado HLS generated files and the generated Tcl build script.""" + clean_up_generated_files(obj) diff --git a/hlsclt/helper_funcs.py b/hlsclt/helper_funcs.py new file mode 100644 index 0000000..7163bfd --- /dev/null +++ b/hlsclt/helper_funcs.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" Helper functions for the HLSCLT Command Line Tool. + +Copyright (c) 2017 Ben Marshall +""" + +### Imports ### +import click +import os +import imp + +### Function Definitions ### + +# Function to generate the default config dicttionary +def generate_default_config(): + config = { + "project_name" : "proj_" + os.path.relpath(".",".."), + "top_level_function_name" : "", + "src_dir_name" : "src", + "tb_dir_name" : "tb", + "src_files" : "", + "tb_files" : "", + "part_name" : "", + "clock_period" : "", + "language" : "vhdl", + } + return config + +# Function to read in the config from a local file and generate a config structure. +def get_vars_from_file(filename): + try: + with click.open_file(filename) as f: + config = imp.load_source('config', '', f) + return config + except OSError: + click.echo("Error: No hls_config.py found, please create a config file for your project. For an example config file please see the 'examples' folder within the hlsclt install directory.") + raise click.Abort() + +# Funtion to parse a loaded config structure and overwrite the config dictionary defaults. +def parse_config_vars(config_loaded, config, errors): + config_loaded_dict = dict((name, getattr(config_loaded, name)) for name in dir(config_loaded) if not name.startswith('__')) + config_loaded_set = set(config_loaded_dict) + config_set = set(config) + options_defined = config_loaded_set.intersection(config_set) + for name in config: + if str(name) in options_defined: + config[name] = config_loaded_dict[name] + try: + if not config[name]: + raise ConfigError("Error: " + name + " is not defined in config file. No default exists, please define a value in the config file.") + except ConfigError as err: + errors.append(err) + continue diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index 62e6b26..cb62f31 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -9,210 +9,19 @@ import click from ._version import __version__ import os -import sys -import shutil -import argparse -from glob import glob -import contextlib -from distutils.util import strtobool - -### Class definitions ### -class Error(Exception): - """Base class for exceptions in this module.""" - pass - -class ConfigError(Error): - """Exception raised for options not defined in config. - - Attributes: - message -- explanation of the error - """ - - def __init__(self, message): - self.message = message - -class hlsclt_internal_object(object): - def __init__(self, config={}, solution_num=1, file=None, syn_command_present=False): - self.config = config - self.solution_num = solution_num - self.file=file - self.syn_command_present = syn_command_present - -### New Click Stuff ### -def abort_if_false(ctx, param, value): - if not value: - ctx.abort() - -def generate_default_config(): - config = { - "project_name" : "proj_" + os.path.relpath(".",".."), - "top_level_function_name" : "", - "src_dir_name" : "src", - "tb_dir_name" : "tb", - "src_files" : "", - "tb_files" : "", - "part_name" : "", - "clock_period" : "", - "language" : "vhdl", - } - return config - -def get_vars_from_file(filename): - import imp - try: - with click.open_file(filename) as f: - config = imp.load_source('config', '', f) - return config - except OSError: - print("Error: No hls_config.py found, please create a config file for your project. For an example config file please see the 'examples' folder within the hlsclt install directory.") - raise click.Abort() - -def parse_config_vars(config_loaded, config, errors): - config_loaded_dict = dict((name, getattr(config_loaded, name)) for name in dir(config_loaded) if not name.startswith('__')) - config_loaded_set = set(config_loaded_dict) - config_set = set(config) - options_defined = config_loaded_set.intersection(config_set) - for name in config: - if str(name) in options_defined: - config[name] = config_loaded_dict[name] - try: - if not config[name]: - raise ConfigError("Error: " + name + " is not defined in config file. No default exists, please define a value in the config file.") - except ConfigError as err: - errors.append(err) - continue - -def try_delete(item): - try: - shutil.rmtree(item) - except OSError: - try: - os.remove(item) - except OSError: - return 1 - else: - return 0 - else: - return 0 - -def clean_up_generated_files(obj): - config = obj.config - if try_delete(config["project_name"]) + try_delete("run_hls.tcl") + try_delete("vivado_hls.log") == 3: - click.echo("Warning: Nothing to remove!") - else: - click.echo("Cleaned up generated files.") - -def find_solution_num(ctx): - config = ctx.obj.config - # Find solution_num - paths = glob(config["project_name"] + "/solution*/") - solution_num = len(paths) - if solution_num == 0: - solution_num = 1; - elif ctx.params["keep"]: - solution_num = solution_num + 1 - return solution_num - -def do_start_build_stuff(ctx): - config = ctx.obj.config - solution_num = ctx.obj.solution_num - try: - file = click.open_file("run_hls.tcl","w") - file.write("open_project " + config["project_name"] + "\n") - file.write("set_top " + config["top_level_function_name"] + "\n") - for src_file in config["src_files"]: - file.write("add_files " + config["src_dir_name"] + "/" + src_file + "\n") - for tb_file in config["tb_files"]: - file.write("add_files -tb " + config["tb_dir_name"] + "/" + tb_file + "\n") - if ctx.params['keep']: - file.write("open_solution -reset \"solution" + str(solution_num) + "\"" + "\n") - else: - file.write("open_solution \"solution" + str(solution_num) + "\"" + "\n") - file.write("set_part \{" + config["part_name"] + "\}" + "\n") - file.write("create_clock -period " + config["clock_period"] + " -name default" + "\n") - return file - except OSError: - click.echo("Woah! Couldn't create a Tcl run file in the current folder!") - raise click.Abort() - -def do_default_build(ctx): - config = ctx.obj.config - file = ctx.obj.file - file.write("csim_design -clean" + "\n") - file.write("csynth_design" + "\n") - file.write("cosim_design -O -rtl " + config["language"] + "\n") - file.write("export_design -format ip_catalog" + "\n") - file.write("export_design -format sysgen" + "\n") - -def do_csim_stuff(ctx): - file = ctx.obj.file - file.write("csim_design -clean" + "\n") - -def do_syn_stuff(ctx): - file = ctx.obj.file - file.write("csynth_design" + "\n") - -def check_for_syn_results(proj_name, solution_num, top_level_function_name): - return_val = False - try: - with click.open_file(proj_name + "/solution" + str(solution_num) + "/syn/report/" + top_level_function_name + "_csynth.rpt"): - return_val = True - except OSError: - pass - return return_val - -def syn_lookahead_check(ctx): - config = ctx.obj.config - solution_num = ctx.obj.solution_num - file = ctx.obj.file - if (not ctx.obj.syn_command_present) and (not check_for_syn_results(config["project_name"], solution_num, config["top_level_function_name"])): - if click.confirm("C Synthesis has not yet been run but is required for the process(es) you have selected.\nWould you like to add it to this run?", default=True): - click.echo("Adding csynth option.") - file.write("csynth_design" + "\n") - else: - click.echo("Ok, watch out for missing synthesis errors!") - -def do_cosim_stuff(ctx,debug): - config = ctx.obj.config - file = ctx.obj.file - if debug: - for language in just_loop_on(config["language"]): - file.write("cosim_design -rtl " + language + " -trace_level all" + "\n") - else: - for language in just_loop_on(config["language"]): - file.write("cosim_design -O -rtl " + language + "\n") - -def just_loop_on(input): - if isinstance(input, str): - yield input - else: - try: - for item in input: - yield item - except TypeError: - yield input - -def do_export_stuff(ctx,type,evaluate): - config = ctx.obj.config - file = ctx.obj.file - if evaluate: - if "ip" in type: - for language in just_loop_on(config["language"]): - file.write("export_design -format ip_catalog -evaluate " + language + "\n") - if "sysgen" in type: - for language in just_loop_on(config["language"]): - file.write("export_design -format sysgen -evaluate " + language + "\n") - else: - if "ip" in type: - file.write("export_design -format ip_catalog" + "\n") - if "sysgen" in type: - file.write("export_design -format sysgen" + "\n") +from .classes import * +from .helper_funcs import * +from .clean import clean_commands +from .build import build_commands +from .report import report_commands +### Main Click Entry Point ### @click.group() @click.version_option(version=__version__) @click.pass_context def cli(ctx): """Helper tool for using Vivado HLS through the command line. If no arguments are specified then a default run is executed which includes C simulation, C synthesis, Cosimulation and export for both Vivado IP Catalog and System Generator. If any of the run options are specified then only those specified are performed.""" + # Generate a default config dict and then load in the local config file. config = generate_default_config(); config_loaded = get_vars_from_file('hls_config.py') errors = [] @@ -222,76 +31,12 @@ def cli(ctx): print(err) print("Config Errors, exiting...") raise click.Abort() + # Store the loaded config in an object within the Click context so it is available to all commands. obj = hlsclt_internal_object(config) ctx.obj = obj pass -@cli.command('clean',short_help='Remove generated files.') -@click.option('--yes', is_flag=True, callback=abort_if_false, - expose_value=False, - prompt='Are you sure you want to remove all generated files?', - help='Force quiet removal.') -@click.pass_obj -def clean(obj): - """Removes all Vivado HLS generated files and the generated Tcl build script.""" - clean_up_generated_files(obj) - -@cli.group(chain=True, invoke_without_command=True, short_help='Run HLS build stages.') -@click.option('-k','--keep', is_flag=True, help='Preserves existing solutions and creates a new one.') -@click.option('-r','--report', is_flag=True, help='Open build reports when finished.') -@click.pass_context -def build(ctx,keep,report): - """Runs the Vivado HLS tool and executes the specified build stages.""" - ctx.obj.solution_num = find_solution_num(ctx) - ctx.obj.file = do_start_build_stuff(ctx) - pass - -@build.resultcallback() -@click.pass_context -def build_end_callback(ctx,sub_command_returns,keep,report): - if not any(sub_command_returns): - if click.confirm("No build stages specified, would you like to run a default sequence using all the build stages?", abort=True): - do_default_build(ctx) - ctx.obj.file.write("exit" + "\n") - ctx.obj.file.close() - # Call the Vivado HLS process - os.system("vivado_hls -f run_hls.tcl") - -@build.command('csim') -@click.pass_context -def csim(ctx): - """Runs the Vivado HLS C simulation stage.""" - do_csim_stuff(ctx) - return True - -@build.command('syn') -@click.pass_context -def syn(ctx): - """Runs the Vivado HLS C synthesis stage.""" - do_syn_stuff(ctx) - ctx.obj.syn_command_present = True - return True - -@build.command('cosim') -@click.option('-d', '--debug', is_flag=True, help='Turns off compile optimisations and enables logging for cosim.') -@click.pass_context -def cosim(ctx,debug): - """Runs the Vivado HLS cosimulation stage.""" - syn_lookahead_check(ctx) - do_cosim_stuff(ctx,debug) - return True - -@build.command('export') -@click.option('-t', '--type', required=True, multiple=True, type=click.Choice(['ip','sysgen']), help='Specify an export type, Vivado IP Catalog or System Generator. Accepts multiple occurances.') -@click.option('-e', '--evaluate', is_flag=True, help='Runs Vivado synthesis and place and route for the generated export.') -@click.pass_context -def export(ctx, type, evaluate): - """Runs the Vivado HLS export stage.""" - syn_lookahead_check(ctx) - do_export_stuff(ctx,type,evaluate) - return True - -@cli.command('report',short_help='Open reports.') -def report(): - """Opens the Vivado HLS report for the chosen build stages.""" - click.echo("Report Mode") +# Add Click Sub Commands +cli.add_command(clean_commands.clean) +cli.add_command(build_commands.build) +cli.add_command(report_commands.report) diff --git a/hlsclt/report/__init__.py b/hlsclt/report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hlsclt/report/report_commands.py b/hlsclt/report/report_commands.py new file mode 100644 index 0000000..70937e0 --- /dev/null +++ b/hlsclt/report/report_commands.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +""" Report subcommands for HLSCLT. + +Copyright (c) 2017 Ben Marshall +""" + +### Imports ### +import click + +### Click Command Definitions ### +# Report Command +@click.command('report',short_help='Open reports.') +def report(): + """Opens the Vivado HLS report for the chosen build stages.""" + click.echo("Report Mode") From 8f2df862b786a4fab57d8b126c9627c145f3e0fb Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Tue, 20 Jun 2017 16:33:16 +1200 Subject: [PATCH 06/22] Renamed subcommand directories to avoid conflict with .gitignore settings. --- hlsclt/{clean => build_commands}/__init__.py | 0 hlsclt/build_commands/build_commands.py | 192 ++++++++++++++++++ hlsclt/{report => clean_commands}/__init__.py | 0 .../clean_commands.py | 0 hlsclt/hlsclt.py | 6 +- hlsclt/report_commands/__init__.py | 0 .../report_commands.py | 0 7 files changed, 195 insertions(+), 3 deletions(-) rename hlsclt/{clean => build_commands}/__init__.py (100%) create mode 100644 hlsclt/build_commands/build_commands.py rename hlsclt/{report => clean_commands}/__init__.py (100%) rename hlsclt/{clean => clean_commands}/clean_commands.py (100%) create mode 100644 hlsclt/report_commands/__init__.py rename hlsclt/{report => report_commands}/report_commands.py (100%) diff --git a/hlsclt/clean/__init__.py b/hlsclt/build_commands/__init__.py similarity index 100% rename from hlsclt/clean/__init__.py rename to hlsclt/build_commands/__init__.py diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py new file mode 100644 index 0000000..adfe7f0 --- /dev/null +++ b/hlsclt/build_commands/build_commands.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +""" Build related subcommands for HLSCLT. + +Copyright (c) 2017 Ben Marshall +""" + +### Imports ### +import click +from glob import glob +import os + +### Supporting Functions ### +# Function to find the highest solution number within a HLS project. +def find_solution_num(ctx): + config = ctx.obj.config + # Seach for solution folders + paths = glob(config["project_name"] + "/solution*/") + solution_num = len(paths) + # First solution is always 1. + if solution_num == 0: + solution_num = 1; + # If keep argument is specified we are starting a new solution. + elif ctx.params["keep"]: + solution_num = solution_num + 1 + return solution_num + +# Function to generate the 'pre-amble' within the HLS Tcl build script. +def do_start_build_stuff(ctx): + config = ctx.obj.config + solution_num = ctx.obj.solution_num + try: + file = click.open_file("run_hls.tcl","w") + file.write("open_project " + config["project_name"] + "\n") + file.write("set_top " + config["top_level_function_name"] + "\n") + for src_file in config["src_files"]: + file.write("add_files " + config["src_dir_name"] + "/" + src_file + "\n") + for tb_file in config["tb_files"]: + file.write("add_files -tb " + config["tb_dir_name"] + "/" + tb_file + "\n") + if ctx.params['keep']: + file.write("open_solution -reset \"solution" + str(solution_num) + "\"" + "\n") + else: + file.write("open_solution \"solution" + str(solution_num) + "\"" + "\n") + file.write("set_part \{" + config["part_name"] + "\}" + "\n") + file.write("create_clock -period " + config["clock_period"] + " -name default" + "\n") + return file + except OSError: + click.echo("Woah! Couldn't create a Tcl run file in the current folder!") + raise click.Abort() + +# Function to write a default build into the HLS Tcl build script. +def do_default_build(ctx): + config = ctx.obj.config + file = ctx.obj.file + file.write("csim_design -clean" + "\n") + file.write("csynth_design" + "\n") + file.write("cosim_design -O -rtl " + config["language"] + "\n") + file.write("export_design -format ip_catalog" + "\n") + file.write("export_design -format sysgen" + "\n") + +# Function which defines the main actions of the 'csim' command. +def do_csim_stuff(ctx): + file = ctx.obj.file + file.write("csim_design -clean" + "\n") + +# Function which defines the main actions of the 'syn' command. +def do_syn_stuff(ctx): + file = ctx.obj.file + file.write("csynth_design" + "\n") + +# Function to perform a search for existing c synthesis results in a specified hls project and solution. +def check_for_syn_results(proj_name, solution_num, top_level_function_name): + return_val = False + try: + with click.open_file(proj_name + "/solution" + str(solution_num) + "/syn/report/" + top_level_function_name + "_csynth.rpt"): + return_val = True + except OSError: + pass + return return_val + +# Function to check is C synthesis is going to be required but may have been forgorgotten by the user. +def syn_lookahead_check(ctx): + config = ctx.obj.config + solution_num = ctx.obj.solution_num + file = ctx.obj.file + if (not ctx.obj.syn_command_present) and (not check_for_syn_results(config["project_name"], solution_num, config["top_level_function_name"])): + if click.confirm("C Synthesis has not yet been run but is required for the process(es) you have selected.\nWould you like to add it to this run?", default=True): + click.echo("Adding csynth option.") + file.write("csynth_design" + "\n") + else: + click.echo("Ok, watch out for missing synthesis errors!") + +# Function which defines the main actions of the 'cosim' command. +def do_cosim_stuff(ctx,debug): + config = ctx.obj.config + file = ctx.obj.file + if debug: + for language in just_loop_on(config["language"]): + file.write("cosim_design -rtl " + language + " -trace_level all" + "\n") + else: + for language in just_loop_on(config["language"]): + file.write("cosim_design -O -rtl " + language + "\n") + +# Function which loops over any data structure (lists, dicts etc) but not strings +def just_loop_on(input): + if isinstance(input, str): + yield input + else: + try: + for item in input: + yield item + except TypeError: + yield input + +# Function which defines the main actions of the 'export' command. +def do_export_stuff(ctx,type,evaluate): + config = ctx.obj.config + file = ctx.obj.file + if evaluate: + if "ip" in type: + for language in just_loop_on(config["language"]): + file.write("export_design -format ip_catalog -evaluate " + language + "\n") + if "sysgen" in type: + for language in just_loop_on(config["language"]): + file.write("export_design -format sysgen -evaluate " + language + "\n") + else: + if "ip" in type: + file.write("export_design -format ip_catalog" + "\n") + if "sysgen" in type: + file.write("export_design -format sysgen" + "\n") + +### Click Command Definitions ### +# Build group entry point +@click.group(chain=True, invoke_without_command=True, short_help='Run HLS build stages.') +@click.option('-k','--keep', is_flag=True, help='Preserves existing solutions and creates a new one.') +@click.option('-r','--report', is_flag=True, help='Open build reports when finished.') +@click.pass_context +def build(ctx,keep,report): + """Runs the Vivado HLS tool and executes the specified build stages.""" + ctx.obj.solution_num = find_solution_num(ctx) + ctx.obj.file = do_start_build_stuff(ctx) + pass + +# Callback which executes when all specified build subcommands have been finished. +@build.resultcallback() +@click.pass_context +def build_end_callback(ctx,sub_command_returns,keep,report): + # Catch the case where no subcommands have been issued and offer a default build + if not any(sub_command_returns): + if click.confirm("No build stages specified, would you like to run a default sequence using all the build stages?", abort=True): + do_default_build(ctx) + ctx.obj.file.write("exit" + "\n") + ctx.obj.file.close() + # Call the Vivado HLS process + os.system("vivado_hls -f run_hls.tcl") + +# csim subcommand +@build.command('csim') +@click.pass_context +def csim(ctx): + """Runs the Vivado HLS C simulation stage.""" + do_csim_stuff(ctx) + return True + +# syn subcommand +@build.command('syn') +@click.pass_context +def syn(ctx): + """Runs the Vivado HLS C synthesis stage.""" + do_syn_stuff(ctx) + ctx.obj.syn_command_present = True + return True + +# cosim subcommand +@build.command('cosim') +@click.option('-d', '--debug', is_flag=True, help='Turns off compile optimisations and enables logging for cosim.') +@click.pass_context +def cosim(ctx,debug): + """Runs the Vivado HLS cosimulation stage.""" + syn_lookahead_check(ctx) + do_cosim_stuff(ctx,debug) + return True + +# export subcommand +@build.command('export') +@click.option('-t', '--type', required=True, multiple=True, type=click.Choice(['ip','sysgen']), help='Specify an export type, Vivado IP Catalog or System Generator. Accepts multiple occurances.') +@click.option('-e', '--evaluate', is_flag=True, help='Runs Vivado synthesis and place and route for the generated export.') +@click.pass_context +def export(ctx, type, evaluate): + """Runs the Vivado HLS export stage.""" + syn_lookahead_check(ctx) + do_export_stuff(ctx,type,evaluate) + return True diff --git a/hlsclt/report/__init__.py b/hlsclt/clean_commands/__init__.py similarity index 100% rename from hlsclt/report/__init__.py rename to hlsclt/clean_commands/__init__.py diff --git a/hlsclt/clean/clean_commands.py b/hlsclt/clean_commands/clean_commands.py similarity index 100% rename from hlsclt/clean/clean_commands.py rename to hlsclt/clean_commands/clean_commands.py diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index cb62f31..027c6b5 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -11,9 +11,9 @@ import os from .classes import * from .helper_funcs import * -from .clean import clean_commands -from .build import build_commands -from .report import report_commands +from .clean_commands import clean_commands +from .build_commands import build_commands +from .report_commands import report_commands ### Main Click Entry Point ### @click.group() diff --git a/hlsclt/report_commands/__init__.py b/hlsclt/report_commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hlsclt/report/report_commands.py b/hlsclt/report_commands/report_commands.py similarity index 100% rename from hlsclt/report/report_commands.py rename to hlsclt/report_commands/report_commands.py From 0ae9bb15796e14e0662b8a46e0b01f8aa70c3e06 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Tue, 20 Jun 2017 17:54:54 +1200 Subject: [PATCH 07/22] Added option to report command. --- hlsclt/report_commands/report_commands.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 70937e0..15d9623 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -9,7 +9,10 @@ ### Click Command Definitions ### # Report Command -@click.command('report',short_help='Open reports.') -def report(): +@click.command('report', short_help='Open reports.') +@click.option('-s', '--stage', required=True, multiple=True, + type=click.Choice(['csim','syn','cosim','export']), + help='Which build stage to open the report for. Multiple occurances accepted') +def report(stage): """Opens the Vivado HLS report for the chosen build stages.""" click.echo("Report Mode") From d9b02125604b93cad2bb03be721499bded0a6be7 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Tue, 20 Jun 2017 20:16:11 +1200 Subject: [PATCH 08/22] Initial implementation of report opening command. --- hlsclt/build_commands/build_commands.py | 12 +-------- hlsclt/examples/simple_adder/src/dut.cpp | 1 + hlsclt/helper_funcs.py | 11 ++++++++ hlsclt/report_commands/report_commands.py | 31 +++++++++++++++++++++-- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py index adfe7f0..2f40442 100644 --- a/hlsclt/build_commands/build_commands.py +++ b/hlsclt/build_commands/build_commands.py @@ -8,6 +8,7 @@ import click from glob import glob import os +from hlsclt.helper_funcs import just_loop_on ### Supporting Functions ### # Function to find the highest solution number within a HLS project. @@ -100,17 +101,6 @@ def do_cosim_stuff(ctx,debug): for language in just_loop_on(config["language"]): file.write("cosim_design -O -rtl " + language + "\n") -# Function which loops over any data structure (lists, dicts etc) but not strings -def just_loop_on(input): - if isinstance(input, str): - yield input - else: - try: - for item in input: - yield item - except TypeError: - yield input - # Function which defines the main actions of the 'export' command. def do_export_stuff(ctx,type,evaluate): config = ctx.obj.config diff --git a/hlsclt/examples/simple_adder/src/dut.cpp b/hlsclt/examples/simple_adder/src/dut.cpp index 9db18ae..676a387 100755 --- a/hlsclt/examples/simple_adder/src/dut.cpp +++ b/hlsclt/examples/simple_adder/src/dut.cpp @@ -1,4 +1,5 @@ int simple_adder(int a, int b) { + #pragma HLS PIPELINE int c; c = a + b; return c; diff --git a/hlsclt/helper_funcs.py b/hlsclt/helper_funcs.py index 7163bfd..d82a7d7 100644 --- a/hlsclt/helper_funcs.py +++ b/hlsclt/helper_funcs.py @@ -51,3 +51,14 @@ def parse_config_vars(config_loaded, config, errors): except ConfigError as err: errors.append(err) continue + +# Function which loops over any data structure (lists, dicts etc) but not strings +def just_loop_on(input): + if isinstance(input, str): + yield input + else: + try: + for item in input: + yield item + except TypeError: + yield input diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 15d9623..8e35525 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -6,6 +6,31 @@ ### Imports ### import click +import os +from hlsclt.helper_funcs import just_loop_on + +### Supporting Functions ### +# Function for opening reports. +def open_report(ctx,report): + config = ctx.obj.config + solution_num = ctx.obj.solution_num + report_files = [] + if report == 'csim': + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/csim/report/" + config["top_level_function_name"] + "_csim.log") + elif report == 'syn': + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/syn/report/" + config["top_level_function_name"] + "_csynth.rpt") + elif report == 'cosim': + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt") + for language in just_loop_on(config["language"]): + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["language"] + "/" + config["top_level_function_name"] + ".log") + elif report == 'export': + for language in just_loop_on(config["language"]): + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["language"] + "/" + config["top_level_function_name"] + "_export.rpt") + for file in report_files: + return_val = os.system('xdg-open ' + file + ' >/dev/null 2>&1') + if return_val != 0: + click.echo("Error: Looks like the " + report + " report doesn't exist for project: " + config["project_name"] + ", solution number: " + str(solution_num) + ". Make sure you have run that build stage.") + ### Click Command Definitions ### # Report Command @@ -13,6 +38,8 @@ @click.option('-s', '--stage', required=True, multiple=True, type=click.Choice(['csim','syn','cosim','export']), help='Which build stage to open the report for. Multiple occurances accepted') -def report(stage): +@click.pass_context +def report(ctx,stage): """Opens the Vivado HLS report for the chosen build stages.""" - click.echo("Report Mode") + for report in stage: + open_report(ctx,report) From ffa59d42ad96fc0abe8a40578d19ab33eadbfeb7 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Wed, 21 Jun 2017 13:13:06 +1200 Subject: [PATCH 09/22] Finished implmentation of report opening command. --- hlsclt/build_commands/build_commands.py | 17 +---------------- hlsclt/helper_funcs.py | 19 ++++++++++++++++++- hlsclt/report_commands/report_commands.py | 5 +++-- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py index 2f40442..124cc39 100644 --- a/hlsclt/build_commands/build_commands.py +++ b/hlsclt/build_commands/build_commands.py @@ -6,25 +6,10 @@ ### Imports ### import click -from glob import glob import os -from hlsclt.helper_funcs import just_loop_on +from hlsclt.helper_funcs import just_loop_on, find_solution_num ### Supporting Functions ### -# Function to find the highest solution number within a HLS project. -def find_solution_num(ctx): - config = ctx.obj.config - # Seach for solution folders - paths = glob(config["project_name"] + "/solution*/") - solution_num = len(paths) - # First solution is always 1. - if solution_num == 0: - solution_num = 1; - # If keep argument is specified we are starting a new solution. - elif ctx.params["keep"]: - solution_num = solution_num + 1 - return solution_num - # Function to generate the 'pre-amble' within the HLS Tcl build script. def do_start_build_stuff(ctx): config = ctx.obj.config diff --git a/hlsclt/helper_funcs.py b/hlsclt/helper_funcs.py index d82a7d7..b9bfcd8 100644 --- a/hlsclt/helper_funcs.py +++ b/hlsclt/helper_funcs.py @@ -8,9 +8,9 @@ import click import os import imp +from glob import glob ### Function Definitions ### - # Function to generate the default config dicttionary def generate_default_config(): config = { @@ -62,3 +62,20 @@ def just_loop_on(input): yield item except TypeError: yield input + +# Function to find the highest solution number within a HLS project. +def find_solution_num(ctx): + config = ctx.obj.config + # Seach for solution folders + paths = glob(config["project_name"] + "/solution*/") + solution_num = len(paths) + # First solution is always 1. + if solution_num == 0: + solution_num = 1; + # If keep argument is specified we are starting a new solution. + try: + if ctx.params["keep"]: + solution_num = solution_num + 1 + except KeyError: + pass + return solution_num diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 8e35525..c8fc261 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -7,7 +7,7 @@ ### Imports ### import click import os -from hlsclt.helper_funcs import just_loop_on +from hlsclt.helper_funcs import just_loop_on, find_solution_num ### Supporting Functions ### # Function for opening reports. @@ -25,7 +25,7 @@ def open_report(ctx,report): report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["language"] + "/" + config["top_level_function_name"] + ".log") elif report == 'export': for language in just_loop_on(config["language"]): - report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["language"] + "/" + config["top_level_function_name"] + "_export.rpt") + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + config["language"] + "/" + config["top_level_function_name"] + "_export.rpt") for file in report_files: return_val = os.system('xdg-open ' + file + ' >/dev/null 2>&1') if return_val != 0: @@ -41,5 +41,6 @@ def open_report(ctx,report): @click.pass_context def report(ctx,stage): """Opens the Vivado HLS report for the chosen build stages.""" + ctx.obj.solution_num = find_solution_num(ctx) for report in stage: open_report(ctx,report) From e5eaa0658229b10edc7d0ba4955685d2cc6e0b96 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Wed, 21 Jun 2017 14:16:14 +1200 Subject: [PATCH 10/22] Added functionality to open hls reports after build. --- hlsclt/build_commands/build_commands.py | 32 ++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py index 124cc39..33ce6c6 100644 --- a/hlsclt/build_commands/build_commands.py +++ b/hlsclt/build_commands/build_commands.py @@ -7,7 +7,9 @@ ### Imports ### import click import os +import subprocess from hlsclt.helper_funcs import just_loop_on, find_solution_num +from hlsclt.report_commands.report_commands import open_report ### Supporting Functions ### # Function to generate the 'pre-amble' within the HLS Tcl build script. @@ -103,6 +105,16 @@ def do_export_stuff(ctx,type,evaluate): if "sysgen" in type: file.write("export_design -format sysgen" + "\n") +# Function which defines the actions that occur after a HLS build. +def do_end_build_stuff(ctx,sub_command_returns,report): + # Check for reporting flag + if report: + if not sub_command_returns: + # Must be on the default run, add all stages manually + sub_command_returns = ['csim','syn','cosim','export'] + for report in sub_command_returns: + open_report(ctx,report) + ### Click Command Definitions ### # Build group entry point @click.group(chain=True, invoke_without_command=True, short_help='Run HLS build stages.') @@ -120,13 +132,21 @@ def build(ctx,keep,report): @click.pass_context def build_end_callback(ctx,sub_command_returns,keep,report): # Catch the case where no subcommands have been issued and offer a default build - if not any(sub_command_returns): + if not sub_command_returns: if click.confirm("No build stages specified, would you like to run a default sequence using all the build stages?", abort=True): do_default_build(ctx) ctx.obj.file.write("exit" + "\n") ctx.obj.file.close() # Call the Vivado HLS process - os.system("vivado_hls -f run_hls.tcl") + hls_processs = subprocess.run(["vivado_hls", "-f", "run_hls.tcl"]) + # Check return status of the HLS process. + if hls_processs.returncode < 0: + raise click.Abort() + elif hls_processs.returncode > 0: + click.echo("Warning: HLS Process returned an error, skipping report opening!") + raise click.Abort() + else: + do_end_build_stuff(ctx,sub_command_returns,report) # csim subcommand @build.command('csim') @@ -134,7 +154,7 @@ def build_end_callback(ctx,sub_command_returns,keep,report): def csim(ctx): """Runs the Vivado HLS C simulation stage.""" do_csim_stuff(ctx) - return True + return 'csim' # syn subcommand @build.command('syn') @@ -143,7 +163,7 @@ def syn(ctx): """Runs the Vivado HLS C synthesis stage.""" do_syn_stuff(ctx) ctx.obj.syn_command_present = True - return True + return 'syn' # cosim subcommand @build.command('cosim') @@ -153,7 +173,7 @@ def cosim(ctx,debug): """Runs the Vivado HLS cosimulation stage.""" syn_lookahead_check(ctx) do_cosim_stuff(ctx,debug) - return True + return 'cosim' # export subcommand @build.command('export') @@ -164,4 +184,4 @@ def export(ctx, type, evaluate): """Runs the Vivado HLS export stage.""" syn_lookahead_check(ctx) do_export_stuff(ctx,type,evaluate) - return True + return 'export' From f71f9f606bdffa859cf0c1188bae37579396ee54 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Wed, 21 Jun 2017 14:48:27 +1200 Subject: [PATCH 11/22] Added open gui command. --- hlsclt/hlsclt.py | 1 + hlsclt/report_commands/report_commands.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index 027c6b5..0f44be0 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -40,3 +40,4 @@ def cli(ctx): cli.add_command(clean_commands.clean) cli.add_command(build_commands.build) cli.add_command(report_commands.report) +cli.add_command(report_commands.open_gui) diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index c8fc261..12830ba 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -7,9 +7,18 @@ ### Imports ### import click import os +import subprocess +from glob import glob from hlsclt.helper_funcs import just_loop_on, find_solution_num ### Supporting Functions ### +# Function to check if project exists +def check_for_project(ctx): + config = ctx.obj.config + if not glob(config["project_name"]): + click.echo("Error: Can't find a project folder have you run a build process yet?") + raise click.Abort() + # Function for opening reports. def open_report(ctx,report): config = ctx.obj.config @@ -31,6 +40,10 @@ def open_report(ctx,report): if return_val != 0: click.echo("Error: Looks like the " + report + " report doesn't exist for project: " + config["project_name"] + ", solution number: " + str(solution_num) + ". Make sure you have run that build stage.") +# Function for opening the HLS GUI +def open_project_in_gui(ctx): + config = ctx.obj.config + hls_process = subprocess.Popen(["vivado_hls", "-p", config["project_name"]]) ### Click Command Definitions ### # Report Command @@ -41,6 +54,14 @@ def open_report(ctx,report): @click.pass_context def report(ctx,stage): """Opens the Vivado HLS report for the chosen build stages.""" + check_for_project(ctx) ctx.obj.solution_num = find_solution_num(ctx) for report in stage: open_report(ctx,report) + +@click.command('open_gui', short_help='Open the Vivado HLS GUI and load the project.') +@click.pass_context +def open_gui(ctx): + """Opens the Vivado HLS GUI and loads the project.""" + check_for_project(ctx) + open_project_in_gui(ctx) From 6fe3f3245cafc025a32f0170d18538a6e2355de7 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Wed, 21 Jun 2017 15:27:04 +1200 Subject: [PATCH 12/22] Started project status command implementation. --- hlsclt/hlsclt.py | 1 + hlsclt/report_commands/report_commands.py | 55 ++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index 0f44be0..f80392c 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -41,3 +41,4 @@ def cli(ctx): cli.add_command(build_commands.build) cli.add_command(report_commands.report) cli.add_command(report_commands.open_gui) +cli.add_command(report_commands.status) diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 12830ba..a0e412f 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -31,10 +31,10 @@ def open_report(ctx,report): elif report == 'cosim': report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt") for language in just_loop_on(config["language"]): - report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["language"] + "/" + config["top_level_function_name"] + ".log") + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + language + "/" + config["top_level_function_name"] + ".log") elif report == 'export': for language in just_loop_on(config["language"]): - report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + config["language"] + "/" + config["top_level_function_name"] + "_export.rpt") + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + language + "/" + config["top_level_function_name"] + "_export.rpt") for file in report_files: return_val = os.system('xdg-open ' + file + ' >/dev/null 2>&1') if return_val != 0: @@ -45,6 +45,49 @@ def open_project_in_gui(ctx): config = ctx.obj.config hls_process = subprocess.Popen(["vivado_hls", "-p", config["project_name"]]) +# Function for finding the project status +def gather_project_status(ctx): + config = ctx.obj.config + solution_num = ctx.obj.solution_num + project_status = [] + if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/csim/report/" + config["top_level_function_name"] + "_csim.log"): + project_status.append("csim_done") + if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/syn/report/" + config["top_level_function_name"] + "_csynth.rpt"): + project_status.append('syn_done') + if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt"): + project_status.append('cosim_done') + cache = [] + for line in click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt"): + cache.append(line) + if "vhdl" in cache[line].lower(): + if "pass" in cache[line].lower(): + project_status.append('cosim_vhdl_pass') + elif "fail" in cache[line].lower(): + project_status.append('cosim_vhdl_fail') + if "verilog" in cache[line].lower(): + if "pass" in cache[line].lower(): + project_status.append('cosim_verilog_pass') + elif "fail" in cache[line].lower(): + project_status.append('cosim_verilog_fail') + if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/ip"): + project_status.append('export_ip_done') + if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/sysgen"): + project_status.append('export_sysgen_done') + for language in just_loop_on(config["language"]): + if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + language + "/" + config["top_level_function_name"] + "_export.rpt"): + if language == "vhdl": + project_status.append('evaluate_vhdl_done') + elif language == "verilog": + project_status.append('evaluate_verilog_done') + return project_status + + +# Function for printing out the project status +def print_project_status(ctx): + project_status = gather_project_status(ctx) + click.echo(project_status) + + ### Click Command Definitions ### # Report Command @click.command('report', short_help='Open reports.') @@ -65,3 +108,11 @@ def open_gui(ctx): """Opens the Vivado HLS GUI and loads the project.""" check_for_project(ctx) open_project_in_gui(ctx) + +@click.command('status', short_help='Print out the current project status.') +@click.pass_context +def status(ctx): + """Prints out a message detailing the current project status.""" + check_for_project(ctx) + ctx.obj.solution_num = find_solution_num(ctx) + print_project_status(ctx) From 19f10bce6d178af15272d50ae883587851bbb397 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Wed, 21 Jun 2017 15:34:38 +1200 Subject: [PATCH 13/22] Fixed cosim status parsing code. --- hlsclt/report_commands/report_commands.py | 25 +++++++++++------------ 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index a0e412f..43ae581 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -56,19 +56,18 @@ def gather_project_status(ctx): project_status.append('syn_done') if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt"): project_status.append('cosim_done') - cache = [] - for line in click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt"): - cache.append(line) - if "vhdl" in cache[line].lower(): - if "pass" in cache[line].lower(): - project_status.append('cosim_vhdl_pass') - elif "fail" in cache[line].lower(): - project_status.append('cosim_vhdl_fail') - if "verilog" in cache[line].lower(): - if "pass" in cache[line].lower(): - project_status.append('cosim_verilog_pass') - elif "fail" in cache[line].lower(): - project_status.append('cosim_verilog_fail') + with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt") as f: + for line in f: + if "vhdl" in line.lower(): + if "pass" in line.lower(): + project_status.append('cosim_vhdl_pass') + elif "fail" in line.lower(): + project_status.append('cosim_vhdl_fail') + if "verilog" in line.lower(): + if "pass" in line.lower(): + project_status.append('cosim_verilog_pass') + elif "fail" in line.lower(): + project_status.append('cosim_verilog_fail') if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/ip"): project_status.append('export_ip_done') if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/sysgen"): From af8fb7ae744a8eaccea7db67d09954c5cdb040fb Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Wed, 21 Jun 2017 19:23:32 +1200 Subject: [PATCH 14/22] Added status command. --- hlsclt/report_commands/report_commands.py | 37 ++++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 43ae581..7044714 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -50,12 +50,20 @@ def gather_project_status(ctx): config = ctx.obj.config solution_num = ctx.obj.solution_num project_status = [] - if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/csim/report/" + config["top_level_function_name"] + "_csim.log"): - project_status.append("csim_done") + try: + with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/csim/report/" + config["top_level_function_name"] + "_csim.log") as f: + status_line = f.readlines()[-2] + if "0 errors" in status_line.lower(): + project_status.append("csim_pass") + elif "fail" in status_line.lower(): + project_status.append("csim_fail") + else: + project_status.append("csim_done") + except OSError: + pass if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/syn/report/" + config["top_level_function_name"] + "_csynth.rpt"): project_status.append('syn_done') - if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt"): - project_status.append('cosim_done') + try: with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt") as f: for line in f: if "vhdl" in line.lower(): @@ -68,6 +76,9 @@ def gather_project_status(ctx): project_status.append('cosim_verilog_pass') elif "fail" in line.lower(): project_status.append('cosim_verilog_fail') + project_status.append('cosim_done') + except OSError: + pass if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/ip"): project_status.append('export_ip_done') if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/sysgen"): @@ -83,8 +94,24 @@ def gather_project_status(ctx): # Function for printing out the project status def print_project_status(ctx): + config = ctx.obj.config + solution_num = ctx.obj.solution_num project_status = gather_project_status(ctx) - click.echo(project_status) + click.secho("Project Details", bold=True) + click.echo(" Project Name: " + config["project_name"]) + click.echo(" Number of solutions generated: " + str(solution_num)) + click.echo(" Latest Solution Folder: '" + config["project_name"] + "/solution" + str(solution_num) + "'") + click.secho("Build Status", bold=True) + click.echo(" C Simulation: " + (click.style("Pass", fg='green') if "csim_pass" in project_status else (click.style("Fail", fg='red') if "csim_fail" in project_status else (click.style("Run (Can't get status)", fg='yellow') if "csim_done" in project_status else click.style("Not Run", fg='yellow'))))) + click.echo(" C Synthesis: " + (click.style("Run", fg='green') if "syn_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" Cosimulation: " + (click.style("Run", fg='green') if "cosim_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" VHDL: " + (click.style("Pass", fg='green') if "cosim_vhdl_pass" in project_status else (click.style("Fail", fg='red') if "cosim_vhdl_fail" in project_status else click.style("Not Run", fg='yellow')))) + click.echo(" Verilog: " + (click.style("Pass", fg='green') if "cosim_verilog_pass" in project_status else (click.style("Fail", fg='red') if "cosim_verilog_fail" in project_status else click.style("Not Run", fg='yellow')))) + click.echo(" Export:" ) + click.echo(" IP Catalog: " + (click.style("Run", fg='green') if "export_ip_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" System Generator: " + (click.style("Run", fg='green') if "export_sysgen_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" VHDL Evaluate: " + (click.style("Run", fg='green') if "evaluate_vhdl_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" Verilog Evaluate: " + (click.style("Run", fg='green') if "evaluate_verilog_done" in project_status else click.style("Not Run", fg='yellow'))) ### Click Command Definitions ### From c65b98afd3fed9d6057aecb80bf2f694c4cb7920 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Wed, 21 Jun 2017 19:47:07 +1200 Subject: [PATCH 15/22] Tidied up status command stuff. --- hlsclt/examples/simple_adder/hls_config.py | 2 +- hlsclt/report_commands/report_commands.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/hlsclt/examples/simple_adder/hls_config.py b/hlsclt/examples/simple_adder/hls_config.py index 864e4dc..e1bf8a5 100644 --- a/hlsclt/examples/simple_adder/hls_config.py +++ b/hlsclt/examples/simple_adder/hls_config.py @@ -8,4 +8,4 @@ tb_files = ["testbench.cpp"] part_name = "xc7z020clg484-1" clock_period = "10" -#language = ["vhdl", "verilog"] +#language = ["vhdl","verilog"] diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 7044714..7327271 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -45,13 +45,15 @@ def open_project_in_gui(ctx): config = ctx.obj.config hls_process = subprocess.Popen(["vivado_hls", "-p", config["project_name"]]) -# Function for finding the project status +# Function for gathering the project status def gather_project_status(ctx): config = ctx.obj.config solution_num = ctx.obj.solution_num project_status = [] + # Pull details from csim report try: with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/csim/report/" + config["top_level_function_name"] + "_csim.log") as f: + # Pass/Fail info is always in the second last line of the csim report status_line = f.readlines()[-2] if "0 errors" in status_line.lower(): project_status.append("csim_pass") @@ -61,10 +63,13 @@ def gather_project_status(ctx): project_status.append("csim_done") except OSError: pass + # Pull setails from csynth report if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/syn/report/" + config["top_level_function_name"] + "_csynth.rpt"): project_status.append('syn_done') + # Pull details from cosim report try: with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt") as f: + # search through cosim report to find out pass/fail status for each language for line in f: if "vhdl" in line.lower(): if "pass" in line.lower(): @@ -79,10 +84,12 @@ def gather_project_status(ctx): project_status.append('cosim_done') except OSError: pass + # Pull details from implementation directory, first the presence of an export... if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/ip"): project_status.append('export_ip_done') if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/sysgen"): project_status.append('export_sysgen_done') + # ... then the presence of a Vivado evaluate run for language in just_loop_on(config["language"]): if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + language + "/" + config["top_level_function_name"] + "_export.rpt"): if language == "vhdl": @@ -91,16 +98,18 @@ def gather_project_status(ctx): project_status.append('evaluate_verilog_done') return project_status - # Function for printing out the project status def print_project_status(ctx): config = ctx.obj.config solution_num = ctx.obj.solution_num project_status = gather_project_status(ctx) + # Print out a 'pretty' message showing project status, first up some project details click.secho("Project Details", bold=True) click.echo(" Project Name: " + config["project_name"]) click.echo(" Number of solutions generated: " + str(solution_num)) click.echo(" Latest Solution Folder: '" + config["project_name"] + "/solution" + str(solution_num) + "'") + # And now details about what builds have been run/are passing. + # This section uses lots (too many!) 'conditional expressions' to embed formatting into the output. click.secho("Build Status", bold=True) click.echo(" C Simulation: " + (click.style("Pass", fg='green') if "csim_pass" in project_status else (click.style("Fail", fg='red') if "csim_fail" in project_status else (click.style("Run (Can't get status)", fg='yellow') if "csim_done" in project_status else click.style("Not Run", fg='yellow'))))) click.echo(" C Synthesis: " + (click.style("Run", fg='green') if "syn_done" in project_status else click.style("Not Run", fg='yellow'))) @@ -113,7 +122,6 @@ def print_project_status(ctx): click.echo(" VHDL Evaluate: " + (click.style("Run", fg='green') if "evaluate_vhdl_done" in project_status else click.style("Not Run", fg='yellow'))) click.echo(" Verilog Evaluate: " + (click.style("Run", fg='green') if "evaluate_verilog_done" in project_status else click.style("Not Run", fg='yellow'))) - ### Click Command Definitions ### # Report Command @click.command('report', short_help='Open reports.') From 2eceb83e1c531fe59aa331343dd652602f5649db Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Thu, 22 Jun 2017 08:48:14 +1200 Subject: [PATCH 16/22] Removed dual language loops. --- hlsclt/build_commands/build_commands.py | 12 ++---- hlsclt/examples/simple_adder/hls_config.py | 2 +- hlsclt/report_commands/report_commands.py | 43 ++++++++-------------- 3 files changed, 21 insertions(+), 36 deletions(-) diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py index 33ce6c6..c52b397 100644 --- a/hlsclt/build_commands/build_commands.py +++ b/hlsclt/build_commands/build_commands.py @@ -82,11 +82,9 @@ def do_cosim_stuff(ctx,debug): config = ctx.obj.config file = ctx.obj.file if debug: - for language in just_loop_on(config["language"]): - file.write("cosim_design -rtl " + language + " -trace_level all" + "\n") + file.write("cosim_design -rtl " + config["language"] + " -trace_level all" + "\n") else: - for language in just_loop_on(config["language"]): - file.write("cosim_design -O -rtl " + language + "\n") + file.write("cosim_design -O -rtl " + config["language"] + "\n") # Function which defines the main actions of the 'export' command. def do_export_stuff(ctx,type,evaluate): @@ -94,11 +92,9 @@ def do_export_stuff(ctx,type,evaluate): file = ctx.obj.file if evaluate: if "ip" in type: - for language in just_loop_on(config["language"]): - file.write("export_design -format ip_catalog -evaluate " + language + "\n") + file.write("export_design -format ip_catalog -evaluate " + config["language"] + "\n") if "sysgen" in type: - for language in just_loop_on(config["language"]): - file.write("export_design -format sysgen -evaluate " + language + "\n") + file.write("export_design -format sysgen -evaluate " + config["language"] + "\n") else: if "ip" in type: file.write("export_design -format ip_catalog" + "\n") diff --git a/hlsclt/examples/simple_adder/hls_config.py b/hlsclt/examples/simple_adder/hls_config.py index e1bf8a5..25bb97b 100644 --- a/hlsclt/examples/simple_adder/hls_config.py +++ b/hlsclt/examples/simple_adder/hls_config.py @@ -8,4 +8,4 @@ tb_files = ["testbench.cpp"] part_name = "xc7z020clg484-1" clock_period = "10" -#language = ["vhdl","verilog"] +language = "vhdl" diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 7327271..cb7de1f 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -30,11 +30,9 @@ def open_report(ctx,report): report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/syn/report/" + config["top_level_function_name"] + "_csynth.rpt") elif report == 'cosim': report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt") - for language in just_loop_on(config["language"]): - report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + language + "/" + config["top_level_function_name"] + ".log") + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["language"] + "/" + config["top_level_function_name"] + ".log") elif report == 'export': - for language in just_loop_on(config["language"]): - report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + language + "/" + config["top_level_function_name"] + "_export.rpt") + report_files.append(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + config["language"] + "/" + config["top_level_function_name"] + "_export.rpt") for file in report_files: return_val = os.system('xdg-open ' + file + ' >/dev/null 2>&1') if return_val != 0: @@ -52,7 +50,7 @@ def gather_project_status(ctx): project_status = [] # Pull details from csim report try: - with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/csim/report/" + config["top_level_function_name"] + "_csim.log") as f: + with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/csim/report/" + config["top_level_function_name"] + "_csim.log","r") as f: # Pass/Fail info is always in the second last line of the csim report status_line = f.readlines()[-2] if "0 errors" in status_line.lower(): @@ -61,6 +59,7 @@ def gather_project_status(ctx): project_status.append("csim_fail") else: project_status.append("csim_done") + f.close() except OSError: pass # Pull setails from csynth report @@ -68,20 +67,16 @@ def gather_project_status(ctx): project_status.append('syn_done') # Pull details from cosim report try: - with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt") as f: + with click.open_file(config["project_name"] + "/solution" + str(solution_num) + "/sim/report/" + config["top_level_function_name"] + "_cosim.rpt","r") as f: # search through cosim report to find out pass/fail status for each language for line in f: - if "vhdl" in line.lower(): + if config["language"] in line.lower(): if "pass" in line.lower(): - project_status.append('cosim_vhdl_pass') + project_status.append('cosim_pass') elif "fail" in line.lower(): - project_status.append('cosim_vhdl_fail') - if "verilog" in line.lower(): - if "pass" in line.lower(): - project_status.append('cosim_verilog_pass') - elif "fail" in line.lower(): - project_status.append('cosim_verilog_fail') + project_status.append('cosim_fail') project_status.append('cosim_done') + f.close() except OSError: pass # Pull details from implementation directory, first the presence of an export... @@ -90,12 +85,8 @@ def gather_project_status(ctx): if os.path.isdir(config["project_name"] + "/solution" + str(solution_num) + "/impl/sysgen"): project_status.append('export_sysgen_done') # ... then the presence of a Vivado evaluate run - for language in just_loop_on(config["language"]): - if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + language + "/" + config["top_level_function_name"] + "_export.rpt"): - if language == "vhdl": - project_status.append('evaluate_vhdl_done') - elif language == "verilog": - project_status.append('evaluate_verilog_done') + if os.path.isfile(config["project_name"] + "/solution" + str(solution_num) + "/impl/report/" + config["language"] + "/" + config["top_level_function_name"] + "_export.rpt"): + project_status.append('evaluate_done') return project_status # Function for printing out the project status @@ -108,19 +99,17 @@ def print_project_status(ctx): click.echo(" Project Name: " + config["project_name"]) click.echo(" Number of solutions generated: " + str(solution_num)) click.echo(" Latest Solution Folder: '" + config["project_name"] + "/solution" + str(solution_num) + "'") + click.echo(" Language Choice: " + config["language"]) # And now details about what builds have been run/are passing. # This section uses lots (too many!) 'conditional expressions' to embed formatting into the output. click.secho("Build Status", bold=True) click.echo(" C Simulation: " + (click.style("Pass", fg='green') if "csim_pass" in project_status else (click.style("Fail", fg='red') if "csim_fail" in project_status else (click.style("Run (Can't get status)", fg='yellow') if "csim_done" in project_status else click.style("Not Run", fg='yellow'))))) click.echo(" C Synthesis: " + (click.style("Run", fg='green') if "syn_done" in project_status else click.style("Not Run", fg='yellow'))) - click.echo(" Cosimulation: " + (click.style("Run", fg='green') if "cosim_done" in project_status else click.style("Not Run", fg='yellow'))) - click.echo(" VHDL: " + (click.style("Pass", fg='green') if "cosim_vhdl_pass" in project_status else (click.style("Fail", fg='red') if "cosim_vhdl_fail" in project_status else click.style("Not Run", fg='yellow')))) - click.echo(" Verilog: " + (click.style("Pass", fg='green') if "cosim_verilog_pass" in project_status else (click.style("Fail", fg='red') if "cosim_verilog_fail" in project_status else click.style("Not Run", fg='yellow')))) + click.echo(" Cosimulation: " + (click.style("Pass", fg='green') if "cosim_pass" in project_status else (click.style("Fail", fg='red') if "cosim_fail" in project_status else (click.style("Run (Can't get status)", fg='yellow') if "cosim_done" in project_status else click.style("Not Run", fg='yellow'))))) click.echo(" Export:" ) - click.echo(" IP Catalog: " + (click.style("Run", fg='green') if "export_ip_done" in project_status else click.style("Not Run", fg='yellow'))) - click.echo(" System Generator: " + (click.style("Run", fg='green') if "export_sysgen_done" in project_status else click.style("Not Run", fg='yellow'))) - click.echo(" VHDL Evaluate: " + (click.style("Run", fg='green') if "evaluate_vhdl_done" in project_status else click.style("Not Run", fg='yellow'))) - click.echo(" Verilog Evaluate: " + (click.style("Run", fg='green') if "evaluate_verilog_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" IP Catalog: " + (click.style("Run", fg='green') if "export_ip_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" System Generator: " + (click.style("Run", fg='green') if "export_sysgen_done" in project_status else click.style("Not Run", fg='yellow'))) + click.echo(" Export Evaluation: " + (click.style("Run", fg='green') if "evaluate_done" in project_status else click.style("Not Run", fg='yellow'))) ### Click Command Definitions ### # Report Command From e77288311fdf9dbf256325da60a807c9139e15ea Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Thu, 22 Jun 2017 08:50:11 +1200 Subject: [PATCH 17/22] Removed just_loop_on func as not required any more. --- hlsclt/build_commands/build_commands.py | 2 +- hlsclt/helper_funcs.py | 11 ----------- hlsclt/report_commands/report_commands.py | 2 +- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py index c52b397..1e80136 100644 --- a/hlsclt/build_commands/build_commands.py +++ b/hlsclt/build_commands/build_commands.py @@ -8,7 +8,7 @@ import click import os import subprocess -from hlsclt.helper_funcs import just_loop_on, find_solution_num +from hlsclt.helper_funcs import find_solution_num from hlsclt.report_commands.report_commands import open_report ### Supporting Functions ### diff --git a/hlsclt/helper_funcs.py b/hlsclt/helper_funcs.py index b9bfcd8..2a98e4d 100644 --- a/hlsclt/helper_funcs.py +++ b/hlsclt/helper_funcs.py @@ -52,17 +52,6 @@ def parse_config_vars(config_loaded, config, errors): errors.append(err) continue -# Function which loops over any data structure (lists, dicts etc) but not strings -def just_loop_on(input): - if isinstance(input, str): - yield input - else: - try: - for item in input: - yield item - except TypeError: - yield input - # Function to find the highest solution number within a HLS project. def find_solution_num(ctx): config = ctx.obj.config diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index cb7de1f..3dbefe6 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -9,7 +9,7 @@ import os import subprocess from glob import glob -from hlsclt.helper_funcs import just_loop_on, find_solution_num +from hlsclt.helper_funcs import find_solution_num ### Supporting Functions ### # Function to check if project exists From 86edf70b76ae3052c8877b5873f282d54643f451 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Thu, 22 Jun 2017 09:26:02 +1200 Subject: [PATCH 18/22] Removed python 3 shebang. Project seems compatible with python2.7. --- hlsclt/hlsclt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hlsclt/hlsclt.py b/hlsclt/hlsclt.py index f80392c..e619290 100755 --- a/hlsclt/hlsclt.py +++ b/hlsclt/hlsclt.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ A Vivado HLS Command Line Helper tool From 7e6465b9216cb8911a6017d61377505f3639f7e3 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Thu, 22 Jun 2017 09:32:17 +1200 Subject: [PATCH 19/22] Added universal flag and extra classifiers to setup config to specificy python 2 compatibility. --- setup.cfg | 2 ++ setup.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..3c6e79c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/setup.py b/setup.py index 30bc277..0b0e64d 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,8 @@ 'Intended Audience :: Developers', 'Topic :: Software Development :: Build Tools', 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3.6' + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 2.7' ], keywords='xilinx vivado development', From fbfad84d81b0ccca179bde99a9288e17d2fddcd4 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Thu, 22 Jun 2017 10:57:10 +1200 Subject: [PATCH 20/22] Updated README to cover new tool usage. Fixed a couple of typos. --- README.md | 148 +++++++++++++++++----- hlsclt/build_commands/build_commands.py | 2 +- hlsclt/report_commands/report_commands.py | 2 +- 3 files changed, 121 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index d848dde..fb3d67f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,21 @@ # Vivado HLS Command Line Tool (hlsclt) + A Vivado HLS Command Line Helper Tool. -Current functionality includes flexibly executing the main Vivado HLS build stages and cleaning up generated files. Supports a command line driven development process. +Supports a command line driven development process, which increases the performance of the HLS tool and massively increases compatibility with source control tools to provide an increase in productivity. + +## Features +- Flexibly execute any of the Vivado HLS build stages +- Open build reports +- Clean up generated files +- View complete project status +- Open Vivado HLS GUI with project loaded ## Requirements -Python 3 - tested with python 3.6.1 +- Python 2 or 3 + - Tested with 3.6.1 and 2.7.5 +- Vivado HLS + - Tested with Vivado HLS 2017.1 ## Install Easy installation: @@ -16,11 +27,12 @@ Manual installation: ```Shell git clone https://github.com/benjmarshall/hlsclt.git sudo cp ./hlsclt/hlsclt/hlsclt.py /usr/local/bin/hlsclt -sudo chmod +x /usr/local.bin/hlsclt +sudo chmod +x /usr/local/bin/hlsclt ``` ## Usage -This tool is intended to aid command line driven development process for Vivado HLS. Whilst the tool is designed to be flexible, certain guidelines should be followed. A top level project folder should contain your HLS source files (or folders) and a 'hls_config.py' file which specifies some of the required configuration for a HLS project (device, clock speed etc). +### Quickstart +This tool is intended to aid a command line driven development process for Vivado HLS. Whilst the tool is designed to be flexible, certain guidelines should be followed. A top level project folder should contain your HLS source files (or folders) and a 'hls_config.py' file which specifies some of the required configuration for a HLS project (device, clock speed etc). A recommended directory structure is as follows: @@ -32,42 +44,120 @@ A recommended directory structure is as follows: - testbench.cpp - hls_config.py -An example project structure and hls_config.py can be found in the [examples](hlsclt/examples) directory. +An example project structure and hls_config.py can be found in the [examples](hlsclt/examples) directory. A full guide for setting a config.py can be seen in the [Project Config](#project-config) section. -The tool should be invoked from within the project folder, i.e.: -```Shell +The tool should be invoked from within the project folder, i.e. : +``` cd my_project_name -hlsclt -csim +hlsclt build csim ``` The tool will read in the configuration from your 'hls_config.py' file and invoke Vivado HLS to perform the chosen build stages. -All of the tool options can be seen my using the '--help' argument: +All of the tool options can be seen by using the '--help' argument: ``` [ben@localhost]$ hlsclt --help -usage: hlsclt [-h] [-clean] [-keep] [-csim] [-syn] [-cosim | -cosim_debug] [-export_ip | -evaluate_ip] - [-export_dsp | -evaluate_dsp] - -Helper tool for using Vivado HLS through the command line. If no arguments are specified then a default -run is executed which includes C simulation, C synthesis, Cosimulation and export for both Vivado IP -Catalog and System Generator. If any of the run options are specified then only those specified are -performed. - -optional arguments: - -h, --help show this help message and exit - -clean remove all Vivado HLS generated files - -keep keep all previous solution and generate a new one - -csim perform C simulation stage - -syn perform C synthesis stage - -cosim perform cosimulation - -cosim_debug perform cosimulation with debug logging - -export_ip perform export for Vivado IP Catalog - -evaluate_ip perform export for Vivado IP Catalog with build to place and route - -export_dsp perform export for System Generator - -evaluate_dsp perform export for System Generator with build to place and route +Usage: hlsclt [OPTIONS] COMMAND [ARGS]... + + Helper tool for using Vivado HLS through the command line. If no arguments + + are specified then a default run is executed which includes C simulation, + C synthesis, Cosimulation and export for both Vivado IP Catalog and System + Generator. If any of the run options are specified then only those + specified are performed. + +Options: + --version Show the version and exit. + --help Show this message and exit. + +Commands: + build Run HLS build stages. + clean Remove generated files. + open_gui Open the Vivado HLS GUI and load the project. + report Open reports. + status Print out the current project status. +``` + +### Nested Commands +The tool is built using 'nested' commands (like git for example), where the main command 'hlsclt' has a group of subcommands, some of which in turn have subcommands. The 'status' command is a simple example of single level of nesting: + +``` +[ben@localhost]$ hlsclt status +Project Details + + Project Name: proj_simple_adder + Number of solutions generated: 1 + Latest Solution Folder: 'proj_simple_adder/solution1' + Language Choice: vhdl +Build Status + C Simulation: Pass + C Synthesis: Not Run + Cosimulation: Not Run + Export: + IP Catalog: Not Run + System Generator: Not Run + Export Evaluation: Not Run +``` + +The build subcommand is slightly more complex than the other top-level commands. Nested subcommands under the build command can be chained in order to perform multiple HLS build stages, each with their own options: + +``` +[ben@localhost]$ hlsclt build csim syn cosim -d +``` + +In this example the tool will launch the HLS process to run a C simulation, followed by C Synthesis, and finally a cosimulation with debugging enabled so that we can view the waveforms of the cosimulation at a later point. + +Each command or subcommand has it's own help option which gives specific information about the command and how to use it. For example the export subcomand: +``` +[ben@localhost]$ hlsclt build export --help +Usage: hlsclt build export [OPTIONS] + + Runs the Vivado HLS export stage. + +Options: + -t, --type [ip|sysgen] Specify an export type, Vivado IP Catalog or System + Generator. Accepts multiple occurrences. [required] + -e, --evaluate Runs Vivado synthesis and place and route for the + generated export. + --help Show this message and exit. +``` + +### Project Config +Each Vivado HLS project requires a 'config.py' file in order to be used hlsclt. This file contains all of the information required by Vivado HLS and hlsclt to perform build operations for your project. The file uses basic python syntax to specify the configuration in a parsable format. The full list of available configuration options is shown below: + +|Configuration Item | Variable Name | Valid Options | Required | +|-------------------|-----------------------|--------------------------------|----------| +|Project Name |project_name |Any valid directory name |No (Default is name of the containing project folder prepended with 'proj_') | +|Function Name |top_level_function_name|String which match function name|Yes | +|Source Files Dir |src_dir_name |Name of directory where source files are located, relative to the project folder|No (Default is 'src')| +|Testbench Files Dir|tb_dir_name |Name of directory where testbench files are located, relative to the project folder|No (Default is 'tb')| +|Source Files |src_files |A list of source files required, located within the Source Files directory|Yes| +|Testbench Files |tb_files |A list of testbench files required, located within the Testbench Files directory|Yes| +|Device String |part_name |A device string as used by Vivado HLS (see examples)|Yes| +|Clock Period |clock_period |A value in nanoseconds input as a string, e.g. "10"|Yes| +|HDL Language |language |Either "vhdl" or "verilog" |No (Default is "vhdl")| + + +Here is an example file taken from the [simple_adder](hlsclt/examples/simple_adder) example shipped with the tool (note that some of the optional items have been commented out in order to use the defaults): + +```python +# Config file for Simple Adder Vivado HLS project + +#project_name = "optional_project_name_here" +top_level_function_name = "simple_adder" +#src_dir_name = "src" +#tb_dir_name = "tb" +src_files = ["dut.h","dut.cpp"] +tb_files = ["testbench.cpp"] +part_name = "xc7z020clg484-1" +clock_period = "10" +language = "vhdl" ``` ## License See [LICENSE](LICENSE) + +## Bugs/Issues +If you have any issues or find a bug please first search the [open issues](https://github.com/benjmarshall/hlsclt/issues) on github and then submit a new issue ticket. diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py index 1e80136..a14150f 100644 --- a/hlsclt/build_commands/build_commands.py +++ b/hlsclt/build_commands/build_commands.py @@ -173,7 +173,7 @@ def cosim(ctx,debug): # export subcommand @build.command('export') -@click.option('-t', '--type', required=True, multiple=True, type=click.Choice(['ip','sysgen']), help='Specify an export type, Vivado IP Catalog or System Generator. Accepts multiple occurances.') +@click.option('-t', '--type', required=True, multiple=True, type=click.Choice(['ip','sysgen']), help='Specify an export type, Vivado IP Catalog or System Generator. Accepts multiple occurences.') @click.option('-e', '--evaluate', is_flag=True, help='Runs Vivado synthesis and place and route for the generated export.') @click.pass_context def export(ctx, type, evaluate): diff --git a/hlsclt/report_commands/report_commands.py b/hlsclt/report_commands/report_commands.py index 3dbefe6..54bc652 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -116,7 +116,7 @@ def print_project_status(ctx): @click.command('report', short_help='Open reports.') @click.option('-s', '--stage', required=True, multiple=True, type=click.Choice(['csim','syn','cosim','export']), - help='Which build stage to open the report for. Multiple occurances accepted') + help='Which build stage to open the report for. Multiple occurences accepted') @click.pass_context def report(ctx,stage): """Opens the Vivado HLS report for the chosen build stages.""" From c8e468350504c3ada19e5d17a89fa2fc4bb9828c Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Thu, 22 Jun 2017 11:14:49 +1200 Subject: [PATCH 21/22] Wording changes in README. --- README.md | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index fb3d67f..bd76bc2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A Vivado HLS Command Line Helper Tool. -Supports a command line driven development process, which increases the performance of the HLS tool and massively increases compatibility with source control tools to provide an increase in productivity. +Supports a command line driven development process, which increases the performance of the HLS tool and aids compatibility with source control tools, in order achieve an increase in productivity. ## Features - Flexibly execute any of the Vivado HLS build stages @@ -13,22 +13,15 @@ Supports a command line driven development process, which increases the performa ## Requirements - Python 2 or 3 - - Tested with 3.6.1 and 2.7.5 + - Tested with and 2.7.5 and 3.6.1 - Vivado HLS - Tested with Vivado HLS 2017.1 ## Install -Easy installation: ```Shell pip install hlsclt ``` - -Manual installation: -```Shell -git clone https://github.com/benjmarshall/hlsclt.git -sudo cp ./hlsclt/hlsclt/hlsclt.py /usr/local/bin/hlsclt -sudo chmod +x /usr/local/bin/hlsclt -``` +Depends on [Click](https://pypi.python.org/pypi/click) which will be installed automatically by pip. ## Usage ### Quickstart @@ -44,24 +37,23 @@ A recommended directory structure is as follows: - testbench.cpp - hls_config.py -An example project structure and hls_config.py can be found in the [examples](hlsclt/examples) directory. A full guide for setting a config.py can be seen in the [Project Config](#project-config) section. +An example project structure and hls_config.py can be found in the [examples](hlsclt/examples) directory. A full guide for setting a config.py can be seen in the [Project Config](#project-configuration) section. The tool should be invoked from within the project folder, i.e. : -``` +```Shell cd my_project_name hlsclt build csim ``` The tool will read in the configuration from your 'hls_config.py' file and invoke Vivado HLS to perform the chosen build stages. -All of the tool options can be seen by using the '--help' argument: +All of the tools commands and options can be seen by using the '--help' argument: ``` [ben@localhost]$ hlsclt --help Usage: hlsclt [OPTIONS] COMMAND [ARGS]... Helper tool for using Vivado HLS through the command line. If no arguments - are specified then a default run is executed which includes C simulation, C synthesis, Cosimulation and export for both Vivado IP Catalog and System Generator. If any of the run options are specified then only those @@ -80,12 +72,11 @@ Commands: ``` ### Nested Commands -The tool is built using 'nested' commands (like git for example), where the main command 'hlsclt' has a group of subcommands, some of which in turn have subcommands. The 'status' command is a simple example of single level of nesting: +The tool is built using the concept of 'nested' commands (like git for example), where the main command 'hlsclt' has a group of subcommands, some of which in turn have subcommands. The 'status' command is a simple example of single level of nesting: ``` [ben@localhost]$ hlsclt status Project Details - Project Name: proj_simple_adder Number of solutions generated: 1 Latest Solution Folder: 'proj_simple_adder/solution1' @@ -123,8 +114,8 @@ Options: --help Show this message and exit. ``` -### Project Config -Each Vivado HLS project requires a 'config.py' file in order to be used hlsclt. This file contains all of the information required by Vivado HLS and hlsclt to perform build operations for your project. The file uses basic python syntax to specify the configuration in a parsable format. The full list of available configuration options is shown below: +### Project Configuration +Each Vivado HLS project requires a 'config.py' file in order to use hlsclt. This file contains all of the information required by Vivado HLS and hlsclt to perform build operations for your project. The file uses basic python syntax to specify the configuration in a parsable format. The full list of available configuration options is shown below: |Configuration Item | Variable Name | Valid Options | Required | |-------------------|-----------------------|--------------------------------|----------| From f6abf357fd2b5ea7da514cd4174dbecb9e36bc38 Mon Sep 17 00:00:00 2001 From: Ben Marshall Date: Thu, 22 Jun 2017 11:27:28 +1200 Subject: [PATCH 22/22] Updated version string to alpha 1. --- hlsclt/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlsclt/_version.py b/hlsclt/_version.py index b06045d..56d49e0 100644 --- a/hlsclt/_version.py +++ b/hlsclt/_version.py @@ -1 +1 @@ -__version__ = '1.0.0.dev3' +__version__ = '1.0.0.a1'