diff --git a/hlsclt/_version.py b/hlsclt/_version.py index ff529cd..b623d95 100644 --- a/hlsclt/_version.py +++ b/hlsclt/_version.py @@ -1 +1 @@ -__version__ = '1.0.0.a5' +__version__ = '1.0.0.a6' diff --git a/hlsclt/build_commands/build_commands.py b/hlsclt/build_commands/build_commands.py index 77a71ea..6899e3e 100644 --- a/hlsclt/build_commands/build_commands.py +++ b/hlsclt/build_commands/build_commands.py @@ -10,6 +10,7 @@ import subprocess from hlsclt.helper_funcs import find_solution_num from hlsclt.report_commands.report_commands import open_report +import shutil ### Supporting Functions ### # Function to generate the 'pre-amble' within the HLS Tcl build script. @@ -35,7 +36,7 @@ def do_start_build_stuff(ctx): file.write("set_part " + config["part_name"] + "\n") file.write("create_clock -period " + config["clock_period"] + " -name default" + "\n") return file - except OSError: + except (OSError, IOError): click.echo("Woah! Couldn't create a Tcl run file in the current folder!") raise click.Abort() @@ -66,7 +67,7 @@ def check_for_syn_results(proj_name, solution_num, top_level_function_name): try: with click.open_file(proj_name + "/solution" + str(solution_num) + "/syn/report/" + top_level_function_name + "_csynth.rpt"): return_val = True - except OSError: + except (OSError, IOError): pass return return_val @@ -108,6 +109,19 @@ def do_export_stuff(ctx,type,evaluate): # Function which defines the actions that occur after a HLS build. def do_end_build_stuff(ctx,sub_command_returns,report): + # Copy the src/ files as well as the config file to keep track of the changes over solutions + config = ctx.obj.config + solution_num = ctx.obj.solution_num + click.echo("Copying the source and config files to solution"+str(solution_num)) + destiny = config["project_name"] + "/solution" + str(solution_num) + destiny_src = destiny + "/src" + destiny_config = destiny + "/hls_config.py" + # If we are overwriting an existing solution delete the source directory first. + if ctx.params['keep'] == 0: + shutil.rmtree(destiny_src, ignore_errors=True) + shutil.copytree("src", destiny_src) + shutil.copyfile("hls_config.py", destiny_config) + # Check for reporting flag if report: if not sub_command_returns: diff --git a/hlsclt/clean_commands/clean_commands.py b/hlsclt/clean_commands/clean_commands.py index e584687..6ea5fbd 100644 --- a/hlsclt/clean_commands/clean_commands.py +++ b/hlsclt/clean_commands/clean_commands.py @@ -19,10 +19,10 @@ def abort_if_false(ctx, param, value): def try_delete(item): try: shutil.rmtree(item) - except OSError: + except (OSError, IOError): try: os.remove(item) - except OSError: + except (OSError, IOError): return 1 else: return 0 diff --git a/hlsclt/helper_funcs.py b/hlsclt/helper_funcs.py index c77e5f2..b6a42c3 100644 --- a/hlsclt/helper_funcs.py +++ b/hlsclt/helper_funcs.py @@ -35,7 +35,7 @@ def get_vars_from_file(filename): with click.open_file(filename) as f: config = imp.load_source('config', '', f) return config - except OSError: + except (OSError, IOError): 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() @@ -73,10 +73,12 @@ def find_solution_num(ctx): # 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 + else: + # Only if this isn't the first solution + # 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 54bc652..4cd4887 100644 --- a/hlsclt/report_commands/report_commands.py +++ b/hlsclt/report_commands/report_commands.py @@ -60,7 +60,7 @@ def gather_project_status(ctx): else: project_status.append("csim_done") f.close() - except OSError: + except (OSError, IOError): 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"): @@ -77,7 +77,9 @@ def gather_project_status(ctx): project_status.append('cosim_fail') project_status.append('cosim_done') f.close() - except OSError: + except (OSError, IOError): + pass + except: 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"): @@ -90,7 +92,7 @@ def gather_project_status(ctx): return project_status # Function for printing out the project status -def print_project_status(ctx): +def print_project_status(ctx, stats): config = ctx.obj.config solution_num = ctx.obj.solution_num project_status = gather_project_status(ctx) @@ -111,6 +113,81 @@ def print_project_status(ctx): 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'))) + # Provide a stats summary of obtained accross solutions + if stats: + if solution_num > 0: + click.secho("Solutions", bold=True) + for i in range(solution_num): + # solutions start in "1" + j = i + 1 + # Fetch the information directly from the report, if possible + try: + with click.open_file(config["project_name"] + "/solution" + str(j) + "/syn/report/" + config["top_level_function_name"] + "_csynth.rpt","r") as f: + click.echo(click.style(" Solution ", fg="magenta")+ str(j) + ":") + # Information is typically assembled as follows in this report: + # + # 14 ... + # 15 ================================================================ + # 16 == Performance Estimates + # 17 ================================================================ + # 18 + Timing (ns): + # 19 * Summary: + # 20 +--------+-------+----------+------------+ + # 21 | Clock | Target| Estimated| Uncertainty| + # 22 +--------+-------+----------+------------+ + # 23 |ap_clk | 5.00| 3.492| 0.62| + # 24 +--------+-------+----------+------------+ + # 25 + # 26 + Latency (clock cycles): + # 27 * Summary: + # 28 +-----+-----+-----+-----+---------+ + # 29 | Latency | Interval | Pipeline| + # 30 | min | max | min | max | Type | + # 31 +-----+-----+-----+-----+---------+ + # 32 | 686| 686| 686| 686| none | + # 33 +-----+-----+-----+-----+---------+ + # 34 ... + + # Fetch line 23: + # |ap_clk | 5.00| 3.492| 0.62| + report_content = f.readlines() + ap_clk_line = report_content[22] + ap_clk_line_elements = [x.strip() for x in ap_clk_line.split('|')] + clk_target = ap_clk_line_elements[2] + clk_estimated = ap_clk_line_elements[3] + clk_uncertainty = ap_clk_line_elements[4] + click.echo(" clock:") + click.echo(" - Target: "+ clk_target + " ns") + click.echo(" - Estimated: "+ + (click.style(clk_estimated, fg='green') if float(clk_estimated) < float(clk_target) else click.style(clk_estimated, fg='red')) + " ns") + click.echo(" - Uncertainty: "+ click.style(clk_uncertainty, fg='yellow') + " ns") + + + # Fetch line 32, latency in cycles + # | 686| 686| 686| 686| none | + summary_line = report_content[31] + summary_line_elements = [x.strip() for x in summary_line.split('|')] + latency_min = summary_line_elements[1] + latency_max = summary_line_elements[2] + interval_min = float(summary_line_elements[3]) + 1 + # Get the max interval (and sum 1 since a 0 interval/cycle means at least requires 1) + interval_max = float(summary_line_elements[4]) + 1 + click.echo(" period (time to execute:)):") + click.echo(" - min: "+ str(float(clk_estimated)*interval_min) + " ns") + click.echo(" - min (cycles): "+ str(int(interval_min)) + " cycles") + click.echo(" - max: "+ click.style(str((float(clk_estimated) + float(clk_uncertainty))*interval_max), fg="cyan") + " ns") + click.echo(" - max (cycles): "+ str(int(interval_max)) + " cycles") + + # 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") + f.close() + except IOError: + pass + ### Click Command Definitions ### # Report Command @click.command('report', short_help='Open reports.') @@ -133,9 +210,10 @@ def open_gui(ctx): open_project_in_gui(ctx) @click.command('status', short_help='Print out the current project status.') +@click.option('-s', '--stats', is_flag=True, help='Include a summary of stats for each solution.') @click.pass_context -def status(ctx): +def status(ctx, stats): """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) + print_project_status(ctx, stats)