Skip to content

Commit

Permalink
feat: convert OR detailed router drc file to klayout xml
Browse files Browse the repository at this point in the history
Signed-off-by: Kareem Farid <kareefardi@users.noreply.github.com>
  • Loading branch information
kareefardi committed Nov 28, 2024
1 parent 58bafed commit af4a258
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 8 deletions.
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:
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

0 comments on commit af4a258

Please sign in to comment.