blob: 06d999f87a677bfd571313ff57e44a457152dbc9 [file] [log] [blame]
# Copyright 2020-2021 Efabless Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import yaml
import click
@click.command()
@click.option(
"--benchmark",
"-b",
"benchmark_file",
required=True,
help="The csv file from which to extract the benchmark results",
)
@click.option(
"--design",
"-d",
required=True,
help="The design to compare for between the two scripts. Same as -design in flow.tcl",
)
@click.option(
"--run-path",
"-p",
required=True,
help="The run path, will be used to search for any missing files.",
)
@click.option(
"--output-report",
"-o",
"output_report_file",
required=True,
help="The file to print the final report in",
)
@click.argument("regression_results_file")
def cli(benchmark_file, design, run_path, output_report_file, regression_results_file):
tolerance = {
"general_tolerance": 1,
"tritonRoute_violations": 2,
"Magic_violations": 10,
"antenna_violations": 10,
"lvs_total_errors": 0,
}
critical_statistics = [
"tritonRoute_violations",
"Magic_violations",
"antenna_violations",
"lvs_total_errors",
]
magic_file_extensions = ["gds", "mag", "lef", "spice"]
def compare_vals(benchmark_value, regression_value, param):
if str(benchmark_value) == "-1":
return True
if str(regression_value) == "-1":
return False
tol = 0 - tolerance["general_tolerance"]
if param in tolerance.keys():
tol = 0 - tolerance[param]
if float(benchmark_value) - float(regression_value) >= tol:
return True
else:
return False
def parse_csv(csv_file):
def get_csv_index(header, label):
for i in range(len(header)):
if label == header[i]:
return i
else:
return -1
design_out = dict()
csv_opener = open(csv_file, "r")
csv_data = csv_opener.read().split("\n")
header_info = csv_data[0].split(",")
designPathIdx = get_csv_index(header_info, "design")
if designPathIdx == -1:
print("invalid report. No design paths.")
for i in range(1, len(csv_data)):
if not len(csv_data[i]):
continue
entry = csv_data[i].split(",")
designPath = entry[designPathIdx]
if designPath == design:
for idx in range(len(header_info)):
if idx != designPathIdx:
design_out[header_info[idx]] = entry[idx]
break
return design_out
def critical_mismatch(benchmark, regression_result):
if len(benchmark):
return False, "The design is not benchmarked"
for stat in critical_statistics:
if compare_vals(benchmark[stat], regression_result[stat], stat):
continue
else:
if str(regression_result[stat]) == "-1":
return (
True,
"The test didn't pass the stage responsible for " + stat,
)
else:
return (
True,
"The results of " + stat + " mismatched with the benchmark",
)
return False, "The test passed"
def compare_status(benchmark, regression_result):
if len(benchmark) == 0:
return False, "The design is not benchmarked"
elif "fail" in str(regression_result["flow_status"]):
if "fail" in str(benchmark["flow_status"]):
return (
False,
"The OpenLane flow failed, but the benchmark never saw it succeed",
)
else:
return True, "The OpenLane flow failed outright, check the logs"
else:
return False, "The test passed"
def missing_resulting_files(design):
search_prefix = os.path.join(
run_path, "results", "signoff", str(design["design_name"])
)
for ext in magic_file_extensions:
required_result = f"{search_prefix}.{ext}"
if not os.path.isfile(required_result):
return (
True,
f"File {required_result} is missing from the results directory",
)
return False, "The test passed"
benchmark = parse_csv(benchmark_file)
regression_result = parse_csv(regression_results_file)
did_pass = True
notes = None
failure, reason = compare_status(benchmark, regression_result)
if failure:
did_pass = False
notes = notes or reason
failure, reason = critical_mismatch(benchmark, regression_result)
if failure:
did_pass = False
notes = notes or reason
failure, reason = missing_resulting_files(regression_result)
if failure:
did_pass = False
notes = notes or reason
design_report = {"pass": did_pass, "notes": notes}
current_yaml_str = "{}"
try:
current_yaml_str = open(output_report_file).read()
except FileNotFoundError:
pass
current_yaml = yaml.safe_load(current_yaml_str)
current_yaml[design] = design_report
current_yaml_str = yaml.safe_dump(current_yaml, sort_keys=False)
import sys
print(output_report_file, file=sys.stderr)
with open(output_report_file, "w") as f:
f.write(current_yaml_str)
if __name__ == "__main__":
cli()