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(precheck): add support for .gds.br compressed layouts #87

Merged
merged 2 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
45 changes: 33 additions & 12 deletions precheck/precheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import logging
import os
import subprocess
import tempfile
import time
import traceback
import xml.etree.ElementTree as ET

import brotli
import gdstk
import klayout.db as pya
import klayout.rdb as rdb
Expand Down Expand Up @@ -90,14 +92,13 @@ def klayout_zero_area(gds: str):
return klayout_drc(gds, "zero_area", "zeroarea.rb.drc")


def klayout_checks(gds: str):
def klayout_checks(gds: str, expected_name: str):
layout = pya.Layout()
layout.read(gds)
layers = parse_lyp_layers(LYP_FILE)

logging.info("Running top macro name check...")
top_cell = layout.top_cell()
expected_name = os.path.splitext(os.path.basename(gds))[0]
if top_cell.name != expected_name:
raise PrecheckFailure(
f"Top macro name mismatch: expected {expected_name}, got {top_cell.name}"
Expand Down Expand Up @@ -137,15 +138,31 @@ def main():
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logging.info(f"PDK_ROOT: {PDK_ROOT}")

if args.gds.endswith(".gds"):
gds_stem = args.gds.removesuffix(".gds")
gds_temp = None
gds_file = args.gds
elif args.gds.endswith(".gds.br"):
gds_stem = args.gds.removesuffix(".gds.br")
gds_temp = tempfile.NamedTemporaryFile(suffix=".gds", delete=False)
gds_file = gds_temp.name
logging.info(f"decompressing {args.gds} to {gds_file}")
with open(args.gds, "rb") as f:
data = f.read()
with open(gds_file, "wb") as f:
f.write(brotli.decompress(data))
else:
raise PrecheckFailure("Layout file extension is neither .gds nor .gds.br")

if args.top_module:
top_module = args.top_module
else:
top_module = os.path.splitext(os.path.basename(args.gds))[0]
top_module = os.path.basename(gds_stem)

if args.lef:
lef = args.lef
else:
lef = os.path.splitext(args.gds)[0] + ".lef"
lef = gds_stem + ".lef"

if args.template_def:
template_def = args.template_def
Expand Down Expand Up @@ -175,23 +192,23 @@ def main():
logging.info(f"using def template {template_def}")

checks = [
["Magic DRC", lambda: magic_drc(args.gds, top_module)],
["KLayout FEOL", lambda: klayout_drc(args.gds, "feol")],
["KLayout BEOL", lambda: klayout_drc(args.gds, "beol")],
["KLayout offgrid", lambda: klayout_drc(args.gds, "offgrid")],
["Magic DRC", lambda: magic_drc(gds_file, top_module)],
["KLayout FEOL", lambda: klayout_drc(gds_file, "feol")],
["KLayout BEOL", lambda: klayout_drc(gds_file, "beol")],
["KLayout offgrid", lambda: klayout_drc(gds_file, "offgrid")],
[
"KLayout pin label overlapping drawing",
lambda: klayout_drc(
args.gds,
gds_file,
"pin_label_purposes_overlapping_drawing",
"pin_label_purposes_overlapping_drawing.rb.drc",
),
],
["KLayout zero area", lambda: klayout_zero_area(args.gds)],
["KLayout Checks", lambda: klayout_checks(args.gds)],
["KLayout zero area", lambda: klayout_zero_area(gds_file)],
["KLayout Checks", lambda: klayout_checks(gds_file, top_module)],
[
"Pin check",
lambda: pin_check(args.gds, lef, template_def, top_module, uses_3v3),
lambda: pin_check(gds_file, lef, template_def, top_module, uses_3v3),
],
]

Expand Down Expand Up @@ -226,6 +243,10 @@ def main():
with open(f"{REPORTS_PATH}/results.md", "w") as f:
f.write(markdown_table)

if gds_temp is not None:
gds_temp.close()
os.unlink(gds_temp.name)

if error_count > 0:
logging.error(f"Precheck failed for {args.gds}! 😭")
logging.error(f"See {REPORTS_PATH} for more details")
Expand Down
3 changes: 2 additions & 1 deletion precheck/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ name = "tt-precheck"
requires-python = ">=3.11"
dynamic = ["version"]
dependencies = [
"klayout>=0.28.17.post1,<0.29.0",
"brotli",
"gdstk",
"klayout>=0.28.17.post1,<0.29.0",
"numpy<2",
"pytest",
"PyYAML",
Expand Down
4 changes: 3 additions & 1 deletion precheck/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# This file is autogenerated by pip-compile with Python 3.13
# by the following command:
#
# pip-compile
#
brotli==1.1.0
# via tt-precheck (pyproject.toml)
gdstk==0.9.52
# via tt-precheck (pyproject.toml)
iniconfig==2.0.0
Expand Down
8 changes: 4 additions & 4 deletions precheck/test_precheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,30 +341,30 @@ def test_klayout_beol_fail(gds_fail_met1_poly: str):


def test_klayout_checks_pass(gds_valid: str):
precheck.klayout_checks(gds_valid)
precheck.klayout_checks(gds_valid, "TEST_valid")


def test_klayout_checks_fail_metal5(gds_fail_metal5_poly: str):
with pytest.raises(
precheck.PrecheckFailure, match=r"Forbidden layer met5\.drawing found in .+"
):
precheck.klayout_checks(gds_fail_metal5_poly)
precheck.klayout_checks(gds_fail_metal5_poly, "TEST_met5_error")


def test_klayout_checks_fail_pr_boundary(gds_no_pr_boundary: str):
with pytest.raises(
precheck.PrecheckFailure,
match=r"prBoundary.boundary \(235/4\) layer not found in .+",
):
precheck.klayout_checks(gds_no_pr_boundary)
precheck.klayout_checks(gds_no_pr_boundary, "TEST_no_prboundary")


def test_klayout_top_module_name(gds_invalid_macro_name: str):
with pytest.raises(
precheck.PrecheckFailure,
match="Top macro name mismatch: expected TEST_invalid_macro_name, got wrong_name",
):
precheck.klayout_checks(gds_invalid_macro_name)
precheck.klayout_checks(gds_invalid_macro_name, "TEST_invalid_macro_name")


def test_klayout_zero_area_drc_pass(gds_valid: str):
Expand Down
Loading