Skip to content

Commit

Permalink
revert: back off PR TinyTapeout#56
Browse files Browse the repository at this point in the history
  • Loading branch information
htfab committed May 29, 2024
1 parent efa70ce commit e223ea7
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 163 deletions.
118 changes: 6 additions & 112 deletions precheck/pin_check.py
Original file line number Diff line number Diff line change
@@ -1,95 +1,11 @@
import logging
import re
from collections.abc import Iterable
from itertools import combinations
from numbers import Real

import gdstk
from precheck_failure import PrecheckFailure


def canonicalize_rectangles(
rects: Iterable[Iterable[Real]],
) -> Iterable[Iterable[Real]]:
# lists all maximal rectangles covered by the union of input rectangles

sweep_events = {}
for lx, by, rx, ty in rects:
sweep_events[by] = sweep_events.get(by, {})
sweep_events[by][lx] = sweep_events[by].get(lx, 0) + 1
sweep_events[by][rx] = sweep_events[by].get(rx, 0) - 1
sweep_events[ty] = sweep_events.get(ty, {})
sweep_events[ty][lx] = sweep_events[ty].get(lx, 0) - 1
sweep_events[ty][rx] = sweep_events[ty].get(rx, 0) + 1

closed_rects = []
open_rects = {}
cross_section_events = {}
for y, sweep_line_events in sorted(sweep_events.items()):
old_multiplicity = 0
new_multiplicity = 0
is_covered, covered_intervals, covered_start = False, [], None
is_removed, removed_intervals, removed_start = False, [], None
for x in sorted(set(cross_section_events).union(sweep_line_events)):
old_delta = cross_section_events.get(x, 0)
new_delta = sweep_line_events.get(x, 0) + old_delta
old_multiplicity += old_delta
new_multiplicity += new_delta
assert old_multiplicity >= 0
assert new_multiplicity >= 0
was_covered, was_removed = is_covered, is_removed
is_covered = new_multiplicity > 0
is_removed = old_multiplicity > 0 and new_multiplicity == 0
if was_covered and not is_covered:
assert covered_start is not None
covered_intervals.append((covered_start, x))
covered_start = None
if was_removed and not is_removed:
assert removed_start is not None
removed_intervals.append((removed_start, x))
removed_start = None
if is_covered and not was_covered:
assert covered_start is None
covered_start = x
if is_removed and not was_removed:
assert removed_start is None
removed_start = x

for x, m in sorted(sweep_line_events.items()):
cross_section_events[x] = cross_section_events.get(x, 0) + m
if cross_section_events[x] == 0:
del cross_section_events[x]

for (lx, rx), by in open_rects.items():
closed = False
for ix, jx in removed_intervals:
if ix < rx and lx < jx:
closed = True
if closed:
closed_rects.append((lx, by, rx, y))

kept_intervals = [
(b, c)
for ((a, b), (c, d)) in zip(
[(None, None)] + removed_intervals, removed_intervals + [(None, None)]
)
]
open_rects_new = {}
for ix, jx in covered_intervals:
open_rects_new[(ix, jx)] = y
for (lx, rx), by in open_rects.items():
for ix, jx in kept_intervals:
if (ix is None or ix < rx) and (jx is None or lx < jx):
clx = lx if ix is None else max(lx, ix)
crx = rx if jx is None else min(rx, jx)
open_rects_new[(clx, crx)] = min(
open_rects_new.get((clx, crx), by), by
)
open_rects = open_rects_new

return sorted(set(closed_rects))


def parsefp3(value: str):
# parse fixed-point numbers with 3 digits after the decimal point
# e.g. '20.470' to 20470
Expand Down Expand Up @@ -245,20 +161,6 @@ def pin_check(gds: str, lef: str, template_def: str, toplevel: str):
lef_errors += 1
current_pin = None

lef_ports_orig = lef_ports
lef_ports = {}
for current_pin, ports_orig in lef_ports_orig.items():
ports_by_layers = {}
for layer, lx, by, rx, ty in ports_orig:
if layer not in ports_by_layers:
ports_by_layers[layer] = []
ports_by_layers[layer].append((lx, by, rx, ty))
ports = []
for layer, rects in sorted(ports_by_layers.items()):
for lx, by, rx, ty in canonicalize_rectangles(rects):
ports.append((layer, lx, by, rx, ty))
lef_ports[current_pin] = ports

for current_pin in def_pins:
if current_pin not in lef_ports:
logging.error(f"Pin {current_pin} not found in {lef}")
Expand Down Expand Up @@ -365,34 +267,26 @@ def pin_check(gds: str, lef: str, template_def: str, toplevel: str):
"met4.pin": (71, 16),
}

gds_layer_lookup = {j: i for i, j in gds_layers.items()}
polygon_list = {layer: [] for layer in gds_layers}
for poly in top.polygons:
poly_layer = (poly.layer, poly.datatype)
layer_name = gds_layer_lookup.get(poly_layer, None)
if layer_name is not None:
polygon_list[layer_name].append(poly)
merged_layers = {}
for layer in gds_layers:
merged_layers[layer] = gdstk.boolean(polygon_list[layer], [], "or")

gds_errors = 0
for current_pin, lef_rects in sorted(lef_ports_orig.items()):
for current_pin, lef_rects in sorted(lef_ports.items()):
for layer, lx, by, rx, ty in lef_rects:
if layer + ".pin" not in gds_layers:
raise PrecheckFailure(
f"Unexpected port layer in LEF: {current_pin} is on layer {layer}"
)
pin_layer = gds_layers[layer + ".pin"]

pin_ok = False
for poly in merged_layers[layer + ".pin"]:
for poly in top.polygons:
poly_layer = (poly.layer, poly.datatype)
if poly.contain_all(
((lx + 1) / 1000, (by + 1) / 1000),
((rx - 1) / 1000, (by + 1) / 1000),
((lx + 1) / 1000, (ty - 1) / 1000),
((rx - 1) / 1000, (ty - 1) / 1000),
):
pin_ok = True
if poly_layer == pin_layer:
pin_ok = True

if not pin_ok:
logging.error(
Expand Down
68 changes: 17 additions & 51 deletions precheck/test_precheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ def generate_analog_example(
lef_file: str,
toplevel: str,
vpwr_layer: str,
vpwr_boxes: str,
vpwr_box: str,
vgnd_layer: str,
vgnd_boxes: str,
vgnd_box: str,
):
with open(tcl_file, "w") as f:
f.write(
Expand All @@ -125,30 +125,26 @@ def read ../def/analog/tt_block_1x2_pg_ana.def
cellname rename tt_um_template {toplevel}
# VPWR
foreach vpwr_box {{ {vpwr_boxes} }} {{
box {{*}}$vpwr_box
paint {vpwr_layer}
label VPWR FreeSans {vpwr_layer}
}}
port VPWR makeall n
box {vpwr_box}
paint {vpwr_layer}
label VPWR FreeSans {vpwr_layer}
port VPWR make n
port VPWR use power
port VPWR class bidirectional
port conn n s e w
# VGND
foreach vgnd_box {{ {vgnd_boxes} }} {{
box {{*}}$vgnd_box
paint {vgnd_layer}
label VGND FreeSans {vgnd_layer}
}}
port VGND makeall n
box {vgnd_box}
paint {vgnd_layer}
label VGND FreeSans {vgnd_layer}
port VGND make n
port VGND use ground
port VGND class bidirectional
port conn n s e w
# Export
gds write {gds_file}
lef write {lef_file}
lef write {lef_file} -pinonly
"""
)
)
Expand Down Expand Up @@ -180,9 +176,9 @@ def gds_lef_analog_example(tmp_path_factory: pytest.TempPathFactory):
str(lef_file),
"TEST_analog_example",
"met4",
"{100 500 250 22076}",
"100 500 250 22076",
"met4",
"{4900 500 5050 22076}",
"4900 500 5050 22076",
)
return str(gds_file), str(lef_file)

Expand All @@ -200,9 +196,9 @@ def gds_lef_analog_wrong_vgnd(tmp_path_factory: pytest.TempPathFactory):
str(lef_file),
"TEST_analog_wrong_vgnd",
"met4",
"{100 500 250 22076}",
"100 500 250 22076",
"met3",
"{4900 500 5250 12076}",
"4900 500 5250 12076",
)
return str(gds_file), str(lef_file)

Expand All @@ -220,29 +216,9 @@ def gds_lef_analog_overlapping_vgnd(tmp_path_factory: pytest.TempPathFactory):
str(lef_file),
"TEST_analog_overlapping_vgnd",
"met4",
"{100 500 250 22076}",
"100 500 250 22076",
"met4",
"{349 20 549 22504}",
)
return str(gds_file), str(lef_file)


@pytest.fixture(scope="session")
def gds_lef_analog_compound_vgnd(tmp_path_factory: pytest.TempPathFactory):
"""Creates a GDS and LEF using the 1x2 analog template, with VGND consisting of two rectangles."""
tcl_file = tmp_path_factory.mktemp("tcl") / "TEST_analog_example.tcl"
gds_file = tmp_path_factory.mktemp("gds") / "TEST_analog_example.gds"
lef_file = tmp_path_factory.mktemp("lef") / "TEST_analog_example.lef"

generate_analog_example(
str(tcl_file),
str(gds_file),
str(lef_file),
"TEST_analog_compound_vgnd",
"met4",
"{100 500 250 22076}",
"met4",
"{4900 500 5050 12076} {4900 12000 5050 22076}",
"349 20 549 22504",
)
return str(gds_file), str(lef_file)

Expand Down Expand Up @@ -346,13 +322,3 @@ def test_pin_analog_overlapping_vgnd(gds_lef_analog_overlapping_vgnd: tuple[str,
"../def/analog/tt_block_1x2_pg_ana.def",
"TEST_analog_overlapping_vgnd",
)


def test_pin_analog_compound_vgnd(gds_lef_analog_compound_vgnd: tuple[str, str]):
gds_file, lef_file = gds_lef_analog_compound_vgnd
precheck.pin_check(
gds_file,
lef_file,
"../def/analog/tt_block_1x2_pg_ana.def",
"TEST_analog_compound_vgnd",
)

0 comments on commit e223ea7

Please sign in to comment.