| # 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. |
| |
| # The only purpose of this file is to create a wrapper around report.py and config.py and make them usable by flow.tcl |
| |
| import os |
| import click |
| |
| import utils.utils as utils |
| |
| from report.report import Report |
| from report.get_file_name import get_name |
| |
| from config.config import ConfigHandler |
| |
| |
| @click.command() |
| @click.option("--design", "-d", required=True, help="Design Path") |
| @click.option("--design_name", "-n", required=True, help="Design Name") |
| @click.option("--tag", "-t", required=True, help="Run Tag") |
| @click.option( |
| "--run_path", |
| "-r", |
| default=None, |
| help="Run Path [Optional, otherwise derived from tag]", |
| ) |
| @click.option("--output_file", "-o", required=True, help="Output Final Summary Report") |
| @click.option( |
| "--man_report", "-m", default="/dev/null", help="Output Manufacturability Reports" |
| ) |
| def cli(design, design_name, tag, run_path, output_file, man_report): |
| """ |
| Creates manufacturability and metric summary reports for a given design and OpenLane run. |
| """ |
| # Extracting Configurations |
| params = ConfigHandler.get_config(design, tag, run_path) |
| |
| # Extracting Report |
| report = Report(design, tag, design_name, params, run_path).get_report() |
| |
| # Write into file |
| with open(output_file, "w") as f: |
| f.write(Report.get_header() + "," + ConfigHandler.get_header()) |
| f.write("\n") |
| f.write(report) |
| |
| # Adding Extra Attributes computed from configs and reported statistics |
| utils.add_computed_statistics(output_file) |
| |
| # Tracking Magic DRC, LVS, Antenna Logs: |
| run_path = run_path or utils.get_run_path(design, tag) |
| |
| _, magic_antenna_report = get_name( |
| os.path.join(run_path, "reports", "routing"), "antenna_violators.rpt" |
| ) |
| _, arc_antenna_report = get_name( |
| os.path.join(run_path, "reports", "signoff"), "antenna.rpt" |
| ) |
| _, magic_drc_report = get_name( |
| os.path.join(run_path, "reports", "signoff"), "drc.rpt" |
| ) |
| _, lvs_report = get_name( |
| os.path.join(run_path, "logs", "signoff"), f"{design_name}.lvs.lef.log" |
| ) |
| |
| printArr = [] |
| |
| printArr.append("Design Name: " + design_name) |
| printArr.append("Run Directory: " + str(run_path)) |
| |
| splitLine = "----------------------------------------" |
| |
| # Summarizing Magic DRC |
| drcVioDict = dict() |
| cnt = 0 |
| if os.path.exists(magic_drc_report): |
| drcFileOpener = open(magic_drc_report) |
| if drcFileOpener.mode == "r": |
| drcContent = drcFileOpener.read() |
| drcFileOpener.close() |
| |
| # design name |
| # violation message |
| # list of violations |
| # Total Count: |
| printArr.append(splitLine) |
| printArr.append("\nMagic DRC Summary:") |
| printArr.append("Source: " + str(magic_drc_report)) |
| if drcContent is not None: |
| drcSections = drcContent.split(splitLine) |
| if len(drcSections) > 2: |
| for i in range(1, len(drcSections) - 1, 2): |
| drcVioDict[drcSections[i]] = len(drcSections[i + 1].split("\n")) - 2 |
| for key in drcVioDict: |
| val = drcVioDict[key] |
| cnt += val |
| printArr.append( |
| 'Violation Message "' |
| + str(key.strip()) |
| + ' "found ' |
| + str(val) |
| + " Times." |
| ) |
| printArr.append("Total Magic DRC violations is " + str(cnt)) |
| else: |
| printArr.append("Source not found.") |
| # Summarizing LVS |
| printArr.append(splitLine) |
| printArr.append("\nLVS Summary:") |
| printArr.append("Source: " + str(lvs_report)) |
| if os.path.exists(lvs_report): |
| lvsFileOpener = open(lvs_report) |
| if lvsFileOpener.mode == "r": |
| lvsContent = lvsFileOpener.read() |
| lvsFileOpener.close() |
| flag = False |
| for line in lvsContent.split("\n"): |
| if line.find("Total errors =") != -1: |
| flag = True |
| printArr.append(line) |
| elif line.find("net") != -1: |
| printArr.append(line) |
| |
| if not flag: |
| printArr.append("Design is LVS clean.") |
| else: |
| printArr.append("Source not found.") |
| |
| # Summarizing Antennas |
| printArr.append(splitLine) |
| printArr.append("\nAntenna Summary:") |
| |
| if os.path.exists(arc_antenna_report): |
| printArr.append("Source: " + str(arc_antenna_report)) |
| antFileOpener = open(arc_antenna_report) |
| if antFileOpener.mode == "r": |
| antContent = antFileOpener.read().split("\n")[-5:] |
| antFileOpener.close() |
| for line in antContent: |
| if line.find("violated:") != -1: |
| printArr.append(line) |
| elif os.path.exists(magic_antenna_report): |
| printArr.append("Source: " + str(magic_antenna_report)) |
| antFileOpener = open(magic_antenna_report) |
| if antFileOpener.mode == "r": |
| antContent = antFileOpener.read().split("\n") |
| antFileOpener.close() |
| tot_cnt = 0 |
| for ant in antContent: |
| if len(str(ant).strip()): |
| tot_cnt += 1 |
| printArr.append("Number of pins violated: " + str(tot_cnt)) |
| else: |
| printArr.append("No antenna report found.") |
| |
| # write into file |
| with open(man_report, "w") as f: |
| f.write("\n".join(printArr)) |
| |
| |
| if __name__ == "__main__": |
| cli() |