mirror of
https://github.com/TinyTapeout/tt-support-tools.git
synced 2026-11-03 04:36:09 +01:00
feat(precheck): add support for .gds.br compressed layouts (#87)
* feat(precheck): add support for .gds.br compressed layouts * fix(precheck): adapt the test suite to changed klayout_checks() signature --------- Co-authored-by: htfab <mpw@htamas.net>
This commit is contained in:
+33
-12
@@ -3,10 +3,12 @@ import argparse
|
||||
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
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
@@ -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),
|
||||
],
|
||||
]
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -341,14 +341,14 @@ 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):
|
||||
@@ -356,7 +356,7 @@ def test_klayout_checks_fail_pr_boundary(gds_no_pr_boundary: str):
|
||||
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):
|
||||
@@ -364,7 +364,7 @@ def test_klayout_top_module_name(gds_invalid_macro_name: str):
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user