diff --git a/openlane/flows/classic.py b/openlane/flows/classic.py index 9e80da3b9..e0939312f 100644 --- a/openlane/flows/classic.py +++ b/openlane/flows/classic.py @@ -26,6 +26,7 @@ Netgen, Checker, Verilator, + Misc, ) _common_config_vars = [ @@ -372,6 +373,7 @@ class Classic(SequentialFlow): Yosys.EQY, Checker.SetupViolations, Checker.HoldViolations, + Misc.ReportManufacturability, ] config_vars = _common_config_vars + [ diff --git a/openlane/steps/misc.py b/openlane/steps/misc.py index cdae05da6..8196f7161 100644 --- a/openlane/steps/misc.py +++ b/openlane/steps/misc.py @@ -12,11 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +import textwrap from typing import Tuple from .step import ViewsUpdate, MetricsUpdate, Step from ..common import Path from ..state import State, DesignFormat +from ..steps import Netgen, Magic, KLayout, OpenROAD +from ..logging import warn @Step.factory.register() @@ -47,3 +50,133 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: out.write(line) return {DesignFormat.SDC: Path(target)}, {} + + +@Step.factory.register() +class ReportManufacturability(Step): + id = "Misc.ReportManufacturability" + name = "Report Manufacturability" + long_name = "Report Manufacturability (DRC, LVS, Antenna)" + inputs = [] + outputs = [] + + def __get_lvs_report(self, state_in): + lvs_step = Netgen.LVS.id + report = [] + report.append( + textwrap.dedent( + """ + ██╗ ██╗ ██╗███████╗ + ██║ ██║ ██║██╔════╝ + ██║ ██║ ██║███████╗ + ██║ ╚██╗ ██╔╝╚════██║ + ███████╗╚████╔╝ ███████║ + ╚══════╝ ╚═══╝ ╚══════╝ + """ + ) + ) + try: + total = state_in.metrics["design__lvs_errors__count"] + unmatched_pins = state_in.metrics["design__lvs_unmatched_pins__count"] + unmatched_nets = state_in.metrics["design__lvs_unmatched_nets__count"] + if total == 0: + report.append("Passed ✅") + else: + report.append("Failed 𐄂") + report.append(f"Total Errors: {total}") + report.append(f"Unmatched Pins: {unmatched_pins}") + report.append(f"Unmatched Nets: {unmatched_nets}") + report.append(f"Check {lvs_step} report directory") + except KeyError as key: + warn(f"{key} not reported. Perhaps step: {lvs_step} didn't run.") + report.append("N/A") + + return "\n".join(report) + + def __get_drc_report(self, state_in): + klayout_step = KLayout.DRC.id + magic_step = Magic.DRC.id + klayout_failed = False + magic_failed = False + report = [] + + report.append( + textwrap.dedent( + """ + ██████╗ ██████╗ ██████╗ + ██╔══██╗██╔══██╗██╔════╝ + ██║ ██║██████╔╝██║ + ██║ ██║██╔══██╗██║ + ██████╔╝██║ ██║╚██████╗ + ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ + """ + ) + ) + + klayout = state_in.metrics.get("klayout__drc_error__count", "N/A") + if klayout == "N/A": + warn( + f"klayout__drc_error__count not reported. Perhaps step: {klayout_step} didn't run." + ) + elif klayout > 0: + klayout_failed = True + + magic = state_in.metrics.get("magic__drc_error__count", "N/A") + if magic == "N/A": + warn( + f"magic__drc_error__count not reported. Perhaps step: {magic_step} didn't run." + ) + elif magic > 0: + magic_failed = True + + if magic == "N/A" and klayout == "N/A": + report.append("N/A") + elif magic_failed or klayout_failed: + report.append("Failed 𐄂") + report.append(f"KLayout DRC errors: {klayout}") + report.append(f"Magic DRC errors: {magic}") + else: + report.append("Passed ✅") + + return "\n".join(report) + + def __get_antenna_report(self, state_in): + antenna_step = OpenROAD.CheckAntennas.id + report = [] + report.append( + textwrap.dedent( + """ + █████╗ ███╗ ██╗████████╗███████╗███╗ ██╗███╗ ██╗ █████╗ + ██╔══██╗████╗ ██║╚══██╔══╝██╔════╝████╗ ██║████╗ ██║██╔══██╗ + ███████║██╔██╗ ██║ ██║ █████╗ ██╔██╗ ██║██╔██╗ ██║███████║ + ██╔══██║██║╚██╗██║ ██║ ██╔══╝ ██║╚██╗██║██║╚██╗██║██╔══██║ + ██║ ██║██║ ╚████║ ██║ ███████╗██║ ╚████║██║ ╚████║██║ ██║ + ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═══╝╚═╝ ╚═╝ + """ + ) + ) + + try: + nets = state_in.metrics["antenna__violating__nets"] + pins = state_in.metrics["antenna__violating__pins"] + if pins + nets == 0: + report.append("Passed ✅") + else: + report.append("Failed 𐄂") + report.append(f"Pin violations: {pins}") + report.append(f"Net violations: {nets}") + report.append(f"Check {antenna_step} report directory") + except KeyError as key: + warn(f"{key} not reported. Perhaps step: {antenna_step} didn't run.") + report.append("N/A") + + return "\n".join(report) + + def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: + lvs_report = self.__get_lvs_report(state_in) + drc_report = self.__get_drc_report(state_in) + antenna_report = self.__get_antenna_report(state_in) + print(antenna_report) + print(lvs_report) + print(drc_report) + return {}, {}