|  | #!/usr/bin/env python3 | 
|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  | """ | 
|  | This script provides common DV simulation specific utilities. | 
|  | """ | 
|  |  | 
|  | import re | 
|  | from collections import OrderedDict | 
|  |  | 
|  |  | 
|  | # Capture the summary results as a list of lists. | 
|  | # The text coverage report is passed as input to the function, in addition to | 
|  | # the tool used. The tool returns a 2D list if the coverage report file was read | 
|  | # and the coverage was extracted successfully. It returns a tuple of: | 
|  | #   List of metrics and values | 
|  | #   Final coverage total | 
|  | # | 
|  | # Raises the appropriate exception if the coverage summary extraction fails. | 
|  | def get_cov_summary_table(cov_report_txt, tool): | 
|  | with open(cov_report_txt, 'r') as f: | 
|  | if tool == 'xcelium': | 
|  | return xcelium_cov_summary_table(f) | 
|  | if tool == 'vcs': | 
|  | return vcs_cov_summary_table(f) | 
|  | raise NotImplementedError(f"{tool} is unsupported for cov extraction.") | 
|  |  | 
|  |  | 
|  | # Same desc as above, but specific to Xcelium and takes an opened input stream. | 
|  | def xcelium_cov_summary_table(buf): | 
|  | for line in buf: | 
|  | if "name" in line: | 
|  | # Strip the line and remove the unwanted "* Covered" string. | 
|  | metrics = line.strip().replace("* Covered", "").split() | 
|  | # Change first item to 'Score'. | 
|  | metrics[0] = 'Score' | 
|  |  | 
|  | # Gather the list of metrics. | 
|  | items = OrderedDict() | 
|  | for metric in metrics: | 
|  | items[metric] = {} | 
|  | items[metric]['covered'] = 0 | 
|  | items[metric]['total'] = 0 | 
|  |  | 
|  | # Next line is a separator. | 
|  | line = buf.readline() | 
|  |  | 
|  | # Subsequent lines are coverage items to be aggregated. | 
|  | for line in buf: | 
|  | line = re.sub(r"%\s+\(", "%(", line) | 
|  | values = line.strip().split() | 
|  | for i, value in enumerate(values): | 
|  | value = value.strip() | 
|  | m = re.search(r"\((\d+)/(\d+).*\)", value) | 
|  | if m: | 
|  | items[metrics[i]]['covered'] += int(m.group(1)) | 
|  | items[metrics[i]]['total'] += int(m.group(2)) | 
|  | items['Score']['covered'] += int(m.group(1)) | 
|  | items['Score']['total'] += int(m.group(2)) | 
|  | # Capture the percentages and the aggregate. | 
|  | values = [] | 
|  | cov_total = None | 
|  | for metric in items.keys(): | 
|  | if items[metric]['total'] == 0: | 
|  | values.append("-- %") | 
|  | else: | 
|  | value = items[metric]['covered'] / items[metric][ | 
|  | 'total'] * 100 | 
|  | value = "{0:.2f} %".format(round(value, 2)) | 
|  | values.append(value) | 
|  | if metric == 'Score': | 
|  | cov_total = value | 
|  | return [items.keys(), values], cov_total | 
|  |  | 
|  | # If we reached here, then we were unable to extract the coverage. | 
|  | raise SyntaxError(f"Coverage data not found in {buf.name}!") | 
|  |  | 
|  |  | 
|  | # Same desc as above, but specific to VCS and takes an opened input stream. | 
|  | def vcs_cov_summary_table(buf): | 
|  | for line in buf: | 
|  | match = re.match("total coverage summary", line, re.IGNORECASE) | 
|  | if match: | 
|  | # Metrics on the next line. | 
|  | line = buf.readline().strip() | 
|  | metrics = line.split() | 
|  | # Values on the next. | 
|  | line = buf.readline().strip() | 
|  | # Pretty up the values - add % sign for ease of post | 
|  | # processing. | 
|  | values = [] | 
|  | for val in line.split(): | 
|  | val += " %" | 
|  | values.append(val) | 
|  | # first row is coverage total | 
|  | cov_total = values[0] | 
|  | return [metrics, values], cov_total | 
|  |  | 
|  | # If we reached here, then we were unable to extract the coverage. | 
|  | raise SyntaxError(f"Coverage data not found in {buf.name}!") |