Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: convert OR detailed router drc file to klayout xml #607

Draft
wants to merge 6 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openlane/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
ScopedFile,
)
from .toolbox import Toolbox
from .drc import DRC, Violation
from .drc import DRC, Violation, BoundingBox
from . import cli
from .tpe import get_tpe, set_tpe
from .ring_buffer import RingBuffer
89 changes: 84 additions & 5 deletions openlane/common/drc.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,18 @@
from dataclasses import dataclass, field, asdict
from typing import List, Optional, Tuple, Dict

BoundingBox = Tuple[Decimal, Decimal, Decimal, Decimal] # microns

@dataclass
class BoundingBox:
donn marked this conversation as resolved.
Show resolved Hide resolved
llx: Decimal
lly: Decimal
urx: Decimal
ury: Decimal


@dataclass
class BoundingBoxWithDescription(BoundingBox):
description: Optional[str] = None


@dataclass
Expand Down Expand Up @@ -54,6 +65,67 @@ class DRC:
module: str
violations: Dict[str, Violation]

@classmethod
def from_openroad(
Self,
report: io.TextIOWrapper,
module: str,
) -> Tuple["DRC", int]:
class State(IntEnum):
vio_type = 0
src = 1
bbox = 10

re_violation = re.compile(r"violation type: (?P<type>.*)$")
re_src = re.compile(r"srcs: (?P<src1>\S+)( (?P<src2>\S+))?")
re_bbox = re.compile(
r"bbox = \( (?P<llx>\S+), (?P<lly>\S+) \) - \( (?P<urx>\S+), (?P<ury>\S+) \) on Layer (?P<layer>\S+)"
)
bbox_count = 0
violations: Dict[str, Violation] = {}
state = State.vio_type
vio_type = src1 = src2 = lly = llx = urx = ury = ""
for line in report:
line = line.strip()
if state == State.vio_type:
vio_match = re_violation.match(line)
assert vio_match is not None, "Error while parsing drc report file"
vio_type = vio_match.group("type")
state = State.src
elif state == State.src:
src_match = re_src.match(line)
assert src_match is not None, "Error while parsing drc report file"
src1 = src_match.group("src1")
src2 = src_match.group("src2")
state = State.bbox
elif state == State.bbox:
bbox_match = re_bbox.match(line)
assert bbox_match is not None, "Error while parsing drc report file"
llx = bbox_match.group("llx")
lly = bbox_match.group("lly")
urx = bbox_match.group("urx")
ury = bbox_match.group("ury")
layer = bbox_match.group("layer")
bbox_count += 1
bounding_box = BoundingBoxWithDescription(
Decimal(llx),
Decimal(lly),
Decimal(urx),
Decimal(ury),
f"{src1} to {src2}",
)
violation = (layer, vio_type)
description = vio_type
if violations.get(vio_type) is not None:
violations[vio_type].bounding_boxes.append(bounding_box)
else:
violations[vio_type] = Violation(
[violation], description, [bounding_box]
)
state = State.vio_type

return (Self(module, violations), bbox_count)

@classmethod
def from_magic(
Self,
Expand Down Expand Up @@ -125,7 +197,7 @@ class State(IntEnum):
f"invalid bounding box at line {i}: bounding box has {len(coord_list)}/4 elements"
)

bounding_box: BoundingBox = (
bounding_box = BoundingBox(
coord_list[0],
coord_list[1],
coord_list[2],
Expand Down Expand Up @@ -155,7 +227,7 @@ def from_magic_feedback(
"Invalid syntax: 'box' command has less than 4 arguments"
)
lx, ly, ux, uy = components[0:4]
last_bounding_box = (
last_bounding_box = BoundingBox(
Decimal(lx) * cif_scale,
Decimal(ly) * cif_scale,
Decimal(ux) * cif_scale,
Expand Down Expand Up @@ -239,7 +311,14 @@ def to_klayout_xml(self, out: io.BufferedIOBase):
multiplicity.text = str(len(violation.bounding_boxes))
xf.write(cell, category, visited, multiplicity)
with xf.element("values"):
llx, lly, urx, ury = bounding_box
value = ET.Element("value")
value.text = f"polygon: ({llx},{lly};{urx},{lly};{urx},{ury};{llx},{ury})"
value.text = f"polygon: ({bounding_box.llx},{bounding_box.lly};{bounding_box.urx},{bounding_box.lly};{bounding_box.urx},{bounding_box.ury};{bounding_box.llx},{bounding_box.ury})"
xf.write(value)
if isinstance(
bounding_box, BoundingBoxWithDescription
):
value = ET.Element("value")
value.text = (
f"text: '{bounding_box.description}'"
)
xf.write(value)
4 changes: 2 additions & 2 deletions openlane/scripts/openroad/drt.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ if { [info exists ::env(DRT_MAX_LAYER)] } {
detailed_route\
-bottom_routing_layer $min_layer\
-top_routing_layer $max_layer\
-output_drc $::env(STEP_DIR)/$::env(DESIGN_NAME).drc\
-output_drc $::env(_DRC_REPORT_PATH)\
-droute_end_iter $::env(DRT_OPT_ITERS)\
-or_seed 42\
-verbose 1

write_views
write_views
18 changes: 17 additions & 1 deletion openlane/steps/openroad.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
from ..common import (
Path,
TclUtils,
DRC as DRCObject,
get_script_dir,
mkdirp,
aggregate_metrics,
Expand Down Expand Up @@ -1615,9 +1616,24 @@ def get_script_path(self):

def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
kwargs, env = self.extract_env(kwargs)
report_path = os.path.join(self.step_dir, "reports", "drc.rpt")
klayout_db_path = os.path.join(self.step_dir, "reports", "drc.xml")
mkdirp(os.path.join(self.step_dir, "reports"))
env["DRT_THREADS"] = env.get("DRT_THREADS", str(_get_process_limit()))
env["_DRC_REPORT_PATH"] = report_path
info(f"Running TritonRoute with {env['DRT_THREADS']} threads…")
return super().run(state_in, env=env, **kwargs)
views_updates, metrics_updates = super().run(state_in, env=env, **kwargs)
drc, violation_count = DRCObject.from_openroad(
open(report_path, encoding="utf8"), self.config["DESIGN_NAME"]
)

drc.to_klayout_xml(open(klayout_db_path, "wb"))
if violation_count > 0:
self.warn(
f"DRC errors found after routing. View the report file at {report_path}.\nView KLayout xml file at {klayout_db_path}"
)

return views_updates, metrics_updates


@Step.factory.register()
Expand Down
54 changes: 27 additions & 27 deletions test/common/test_misc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,27 +92,27 @@ def test_slugify():


def test_magic_drc():
from openlane.common import DRC, Violation
from openlane.common import DRC, Violation, BoundingBox

drc_object, count = DRC.from_magic(io.StringIO(MAGIC_EXAMPLE))
violations = {
"LU.3": Violation(
rules=[("LU", "3")],
description="P-diff distance to N-tap must be < 15.0um (LU.3)",
bounding_boxes=[
(
BoundingBox(
Decimal("17.990"),
Decimal("21.995"),
Decimal("18.265"),
Decimal("22.995"),
),
(
BoundingBox(
Decimal("20.905"),
Decimal("22.935"),
Decimal("21.575"),
Decimal("22.995"),
),
(
BoundingBox(
Decimal("18.535"),
Decimal("21.995"),
Decimal("18.795"),
Expand Down Expand Up @@ -171,62 +171,62 @@ def test_magic_drc_exceptions():


def test_magic_feedback():
from openlane.common import DRC, Violation
from openlane.common import DRC, Violation, BoundingBox

expected_violations = {
"obsm4-metal4.ILLEGAL_OVERLAP": Violation(
rules=[("obsm4-metal4", "ILLEGAL_OVERLAP")],
description="Illegal overlap between obsm4 and metal4 (types do not connect)",
bounding_boxes=[
(
BoundingBox(
Decimal("11137.80"),
Decimal("4449.70"),
Decimal("11153.80"),
Decimal("4458.80"),
),
(
BoundingBox(
Decimal("11137.80"),
Decimal("4437.90"),
Decimal("11139.90"),
Decimal("4449.70"),
),
(
BoundingBox(
Decimal("11151.70"),
Decimal("4437.90"),
Decimal("11153.80"),
Decimal("4449.70"),
),
(
BoundingBox(
Decimal("11137.80"),
Decimal("4433.70"),
Decimal("11153.80"),
Decimal("4437.90"),
),
(
BoundingBox(
Decimal("11137.80"),
Decimal("4421.90"),
Decimal("11139.90"),
Decimal("4433.70"),
),
(
BoundingBox(
Decimal("11151.70"),
Decimal("4421.90"),
Decimal("11153.80"),
Decimal("4433.70"),
),
(
BoundingBox(
Decimal("11137.80"),
Decimal("4417.70"),
Decimal("11153.80"),
Decimal("4421.90"),
),
(
BoundingBox(
Decimal("11137.80"),
Decimal("4412.80"),
Decimal("11139.90"),
Decimal("4417.70"),
),
(
BoundingBox(
Decimal("11151.70"),
Decimal("4412.80"),
Decimal("11153.80"),
Expand All @@ -238,19 +238,19 @@ def test_magic_feedback():
rules=[("obsm4-via4", "ILLEGAL_OVERLAP")],
description="Illegal overlap between obsm4 and via4 (types do not connect)",
bounding_boxes=[
(
BoundingBox(
Decimal("11139.90"),
Decimal("4437.90"),
Decimal("11151.70"),
Decimal("4449.70"),
),
(
BoundingBox(
Decimal("11139.90"),
Decimal("4421.90"),
Decimal("11151.70"),
Decimal("4433.70"),
),
(
BoundingBox(
Decimal("11139.90"),
Decimal("4412.80"),
Decimal("11151.70"),
Expand All @@ -262,31 +262,31 @@ def test_magic_feedback():
rules=[("UNKNOWN", "UNKNOWN2")],
description="device missing 1 terminal;\n connecting remainder to node VGND",
bounding_boxes=[
(
BoundingBox(
Decimal("8232.15"),
Decimal("8080.15"),
Decimal("8238.05"),
Decimal("8085.65"),
),
(
BoundingBox(
Decimal("8204.55"),
Decimal("8080.15"),
Decimal("8224.25"),
Decimal("8085.65"),
),
(
BoundingBox(
Decimal("8186.15"),
Decimal("8035.95"),
Decimal("8215.05"),
Decimal("8041.45"),
),
(
BoundingBox(
Decimal("8149.35"),
Decimal("8080.15"),
Decimal("8196.65"),
Decimal("8085.65"),
),
(
BoundingBox(
Decimal("393.75"),
Decimal("8025.75"),
Decimal("404.25"),
Expand All @@ -298,31 +298,31 @@ def test_magic_feedback():
rules=[("UNKNOWN", "UNKNOWN3")],
description="device missing 1 terminal;\n connecting remainder to node VPWR",
bounding_boxes=[
(
BoundingBox(
Decimal("8232.15"),
Decimal("8063.15"),
Decimal("8238.05"),
Decimal("8071.85"),
),
(
BoundingBox(
Decimal("8204.55"),
Decimal("8063.15"),
Decimal("8224.25"),
Decimal("8071.85"),
),
(
BoundingBox(
Decimal("8186.15"),
Decimal("8049.75"),
Decimal("8215.05"),
Decimal("8058.45"),
),
(
BoundingBox(
Decimal("8149.35"),
Decimal("8063.15"),
Decimal("8196.65"),
Decimal("8071.85"),
),
(
BoundingBox(
Decimal("393.75"),
Decimal("8008.75"),
Decimal("404.25"),
Expand Down
Loading