feat(precheck): initial IHP support

still missing the IHP Klayout DRC deck
This commit is contained in:
Uri Shaked
2025-06-27 20:08:02 +03:00
parent c5199c8f27
commit fcd0fd9cfd
5 changed files with 532 additions and 58 deletions
+2 -2
View File
@@ -13,7 +13,7 @@ class LayerInfo:
return f"LayerInfo(name={self.name}, source={self.source}, layer={self.layer}, data_type={self.data_type})"
def parse_lyp_layers(lyp_file: str):
def parse_lyp_layers(lyp_file: str, only_valid: bool = True):
with open(lyp_file) as f:
xml_content = f.read()
root = ET.fromstring(xml_content)
@@ -25,7 +25,7 @@ def parse_lyp_layers(lyp_file: str):
source = properties.find("source")
valid = properties.find("valid")
if valid is not None and valid.text == "false":
if only_valid and valid is not None and valid.text == "false":
continue
if (
+6 -9
View File
@@ -6,6 +6,7 @@ from numbers import Real
import gdstk
from precheck_failure import PrecheckFailure
from tech_data import power_pins_layer, valid_lef_port_layers
def canonicalize_rectangles(
@@ -98,7 +99,9 @@ def parsefp3(value: str):
return int(mul)
def pin_check(gds: str, lef: str, template_def: str, toplevel: str, uses_3v3: bool):
def pin_check(
gds: str, lef: str, template_def: str, toplevel: str, uses_3v3: bool, tech: str
):
logging.info("Running pin check...")
logging.info(f"* gds: {gds}")
logging.info(f"* lef: {lef}")
@@ -307,7 +310,7 @@ def pin_check(gds: str, lef: str, template_def: str, toplevel: str, uses_3v3: bo
else:
for layer, lx, by, rx, ty in lef_ports[current_pin]:
width = rx - lx
if layer != "met4":
if layer != power_pins_layer[tech]:
logging.error(
f"Port {current_pin} has wrong layer in {lef}: {layer} != met4"
)
@@ -358,13 +361,7 @@ def pin_check(gds: str, lef: str, template_def: str, toplevel: str, uses_3v3: bo
raise PrecheckFailure("Wrong cell at GDS top-level")
top = top[0]
gds_layers = {
"met1.pin": (68, 16),
"met2.pin": (69, 16),
"met3.pin": (70, 16),
"met4.pin": (71, 16),
}
gds_layers = valid_lef_port_layers[tech]
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:
+108 -41
View File
@@ -17,11 +17,18 @@ import yaml
from klayout_tools import parse_lyp_layers
from pin_check import pin_check
from precheck_failure import PrecheckFailure
from tech_data import analog_pin_pos, layer_map, valid_layers
from tech_data import (
analog_pin_pos,
forbidden_layers,
layer_map,
lyp_filename,
tech_names,
valid_layers,
)
PDK_ROOT = os.getenv("PDK_ROOT")
PDK_NAME = os.getenv("PDK_NAME") or "sky130A"
LYP_FILE = f"{PDK_ROOT}/{PDK_NAME}/libs.tech/klayout/tech/{PDK_NAME}.lyp"
LYP_DIR = f"{PDK_ROOT}/{PDK_NAME}/libs.tech/klayout/tech"
REPORTS_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "reports")
if not PDK_ROOT:
@@ -36,6 +43,12 @@ def has_sky130_devices(gds: str):
return False
def load_layers(tech: str, only_valid: bool = True):
lyp_file = f"{LYP_DIR}/{lyp_filename[tech]}"
layers = parse_lyp_layers(lyp_file, only_valid)
return layers
def magic_drc(gds: str, toplevel: str):
logging.info(f"Running magic DRC on {gds} (module={toplevel})")
@@ -89,10 +102,10 @@ def klayout_zero_area(gds: str):
return klayout_drc(gds, "zero_area", "zeroarea.rb.drc")
def klayout_checks(gds: str, expected_name: str):
def klayout_checks(gds: str, expected_name: str, tech: str):
layout = pya.Layout()
layout.read(gds)
layers = parse_lyp_layers(LYP_FILE)
layers = load_layers(tech)
logging.info("Running top macro name check...")
top_cell = layout.top_cell()
@@ -102,13 +115,7 @@ def klayout_checks(gds: str, expected_name: str):
)
logging.info("Running forbidden layer check...")
forbidden_layers = [
"met5.drawing",
"met5.pin",
"met5.label",
]
for layer in forbidden_layers:
for layer in forbidden_layers[tech]:
layer_info = layers[layer]
logging.info(f"* Checking {layer_info.name}")
layer_index = layout.find_layer(layer_info.layer, layer_info.data_type)
@@ -125,7 +132,7 @@ def klayout_checks(gds: str, expected_name: str):
)
def boundary_check(gds: str):
def boundary_check(gds: str, tech: str):
"""Ensure that there are no shapes outside the project area."""
lib = gdstk.read_gds(gds)
tops = lib.top_level()
@@ -133,7 +140,10 @@ def boundary_check(gds: str):
raise PrecheckFailure("GDS top level not unique")
top = tops[0]
boundary = top.copy("test_boundary")
boundary.filter([(235, 4)], False)
layers = load_layers(tech)
layer_info = layers["prBoundary.boundary"]
boundary.filter([(layer_info.layer, layer_info.data_type)], False)
if top.bounding_box() != boundary.bounding_box():
raise PrecheckFailure("Shapes outside project area")
@@ -158,9 +168,23 @@ def power_pin_check(verilog: str, lef: str, uses_3v3: bool):
def layer_check(gds: str, tech: str):
"""Check that there are no invalid layers in the GDS file."""
layer_definition = load_layers(tech, only_valid=False)
lib = gdstk.read_gds(gds)
layers = lib.layers_and_datatypes().union(lib.layers_and_texttypes())
excess = layers - valid_layers[tech]
valid_layer_list = set(
map(
lambda layer_name: (
(
layer_definition[layer_name].layer,
layer_definition[layer_name].data_type,
)
if type(layer_name) is str
else layer_name
),
valid_layers[tech],
)
)
gds_layers = lib.layers_and_datatypes().union(lib.layers_and_texttypes())
excess = gds_layers - valid_layer_list
if excess:
raise PrecheckFailure(f"Invalid layers in GDS: {excess}")
@@ -234,11 +258,19 @@ def analog_pin_check(
def main():
default_tech = PDK_NAME
if default_tech not in tech_names:
default_tech = tech_names[0]
parser = argparse.ArgumentParser()
parser.add_argument("--gds", required=True)
parser.add_argument(
"--tech", required=False, default=default_tech, choices=tech_names
)
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logging.info(f"PDK_ROOT: {PDK_ROOT}")
logging.info(f"Tech: {args.tech}")
if args.gds.endswith(".gds"):
gds_stem = args.gds.removesuffix(".gds")
@@ -256,6 +288,10 @@ def main():
else:
raise PrecheckFailure("Layout file extension is neither .gds nor .gds.br")
if args.tech not in tech_names:
raise PrecheckFailure(f"Invalid tech: {args.tech}")
tech = args.tech
yaml_dir = os.path.dirname(args.gds)
while not os.path.exists(f"{yaml_dir}/info.yaml"):
yaml_dir = os.path.dirname(yaml_dir)
@@ -281,55 +317,86 @@ def main():
template_def = f"../def/analog/tt_analog_{tiles}_3v3.def"
else:
template_def = f"../def/analog/tt_analog_{tiles}.def"
elif tech == "ihp-sg13g2":
template_def = f"../ihp/def/tt_block_{tiles}_pgvdd.def"
else:
template_def = f"../def/tt_block_{tiles}_pg.def"
logging.info(f"using def template {template_def}")
lef_file = gds_stem + ".lef"
verilog_file = gds_stem + ".v"
tech = "sky130"
checks = [
["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(
{
"name": "Magic DRC",
"check": lambda: magic_drc(gds_file, top_module),
"tech": "sky130",
},
{
"name": "KLayout FEOL",
"check": lambda: klayout_drc(gds_file, "feol"),
"tech": "sky130",
},
{
"name": "KLayout BEOL",
"check": lambda: klayout_drc(gds_file, "beol"),
"tech": "sky130",
},
{
"name": "KLayout offgrid",
"check": lambda: klayout_drc(gds_file, "offgrid"),
"tech": "sky130",
},
{
"name": "KLayout pin label overlapping drawing",
"check": lambda: klayout_drc(
gds_file,
"pin_label_purposes_overlapping_drawing",
"pin_label_purposes_overlapping_drawing.rb.drc",
),
],
["KLayout zero area", lambda: klayout_zero_area(gds_file)],
["KLayout Checks", lambda: klayout_checks(gds_file, top_module)],
[
"Pin check",
lambda: pin_check(gds_file, lef_file, template_def, top_module, uses_3v3),
],
["Boundary check", lambda: boundary_check(gds_file)],
["Power pin check", lambda: power_pin_check(verilog_file, lef_file, uses_3v3)],
["Layer check", lambda: layer_check(gds_file, tech)],
["Cell name check", lambda: cell_name_check(gds_file)],
["urpm/nwell check", lambda: urpm_nwell_check(gds_file, top_module)],
[
"Analog pin check",
lambda: analog_pin_check(
},
{"name": "KLayout zero area", "check": lambda: klayout_zero_area(gds_file)},
{
"name": "KLayout Checks",
"check": lambda: klayout_checks(gds_file, top_module, tech),
},
{
"name": "Pin check",
"check": lambda: pin_check(
gds_file, lef_file, template_def, top_module, uses_3v3, tech
),
},
{"name": "Boundary check", "check": lambda: boundary_check(gds_file, tech)},
{
"name": "Power pin check",
"check": lambda: power_pin_check(verilog_file, lef_file, uses_3v3),
},
{"name": "Layer check", "check": lambda: layer_check(gds_file, tech)},
{"name": "Cell name check", "check": lambda: cell_name_check(gds_file)},
{
"name": "urpm/nwell check",
"check": lambda: urpm_nwell_check(gds_file, top_module),
},
{
"name": "Analog pin check",
"check": lambda: analog_pin_check(
gds_file, tech, is_analog, uses_3v3, analog_pins, pinout
),
],
},
]
testsuite = ET.Element("testsuite", name="Tiny Tapeout Prechecks")
error_count = 0
markdown_table = "# Tiny Tapeout Precheck Results\n\n"
markdown_table += "| Check | Result |\n|-----------|--------|\n"
for [name, check] in checks:
for check in checks:
name = check["name"]
if "tech" in check and check["tech"] != tech:
continue
start_time = time.time()
test_case = ET.SubElement(testsuite, "testcase", name=name)
try:
check()
check["check"]()
elapsed_time = time.time() - start_time
markdown_table += f"| {name} | ✅ |\n"
test_case.set("time", str(round(elapsed_time, 2)))
+401 -1
View File
@@ -59,6 +59,371 @@ valid_layers_sky130 = {
}
# fmt: on
valid_layers_ihp_sg13g2 = [
"Substrate.drawing",
"Substrate.text",
"Activ.drawing",
"Activ.label",
"Activ.pin",
"Activ.net",
"Activ.boundary",
"Activ.lvs",
"Activ.mask",
"Activ.nofill",
"Activ.OPC",
"Activ.iOPC",
"Activ.noqrc",
"GatPoly.drawing",
"GatPoly.label",
"GatPoly.pin",
"GatPoly.net",
"GatPoly.boundary",
"GatPoly.nofill",
"GatPoly.OPC",
"GatPoly.iOPC",
"GatPoly.noqrc",
"PolyRes.drawing",
"PolyRes.label",
"PolyRes.pin",
"PolyRes.net",
"PolyRes.boundary",
"NWell.drawing",
"NWell.label",
"NWell.pin",
"NWell.net",
"NWell.boundary",
"nBuLay.drawing",
"nBuLay.label",
"nBuLay.pin",
"nBuLay.net",
"nBuLay.boundary",
"nBuLay.block",
"PWell.block",
"pSD.drawing",
"nSD.drawing",
"nSD.block",
"SalBlock.drawing",
"ThickGateOx.drawing",
"Cont.drawing",
"Cont.net",
"Cont.boundary",
"Cont.OPC",
"Metal1.drawing",
"Metal1.label",
"Metal1.pin",
"Metal1.net",
"Metal1.boundary",
"Metal1.mask",
"Metal1.nofill",
"Metal1.slit",
"Metal1.text",
"Metal1.OPC",
"Metal1.noqrc",
"Metal1.res",
"Metal1.iprobe",
"Metal1.diffprb",
"Via1.drawing",
"Via1.net",
"Via1.boundary",
"Metal2.drawing",
"Metal2.label",
"Metal2.pin",
"Metal2.net",
"Metal2.boundary",
"Metal2.mask",
"Metal2.nofill",
"Metal2.slit",
"Metal2.text",
"Metal2.OPC",
"Metal2.noqrc",
"Metal2.res",
"Metal2.iprobe",
"Metal2.diffprb",
"Via2.drawing",
"Via2.net",
"Via2.boundary",
"Metal3.drawing",
"Metal3.label",
"Metal3.pin",
"Metal3.net",
"Metal3.boundary",
"Metal3.mask",
"Metal3.nofill",
"Metal3.slit",
"Metal3.text",
"Metal3.OPC",
"Metal3.noqrc",
"Metal3.res",
"Metal3.iprobe",
"Metal3.diffprb",
"MIM.drawing",
"MIM.net",
"MIM.boundary",
"Vmim.drawing",
"Via3.drawing",
"Via3.net",
"Via3.boundary",
"Metal4.drawing",
"Metal4.label",
"Metal4.pin",
"Metal4.net",
"Metal4.boundary",
"Metal4.mask",
"Metal4.nofill",
"Metal4.slit",
"Metal4.text",
"Metal4.OPC",
"Metal4.noqrc",
"Metal4.res",
"Metal4.iprobe",
"Metal4.diffprb",
"Via4.drawing",
"Via4.net",
"Via4.boundary",
"Metal5.drawing",
"Metal5.label",
"Metal5.pin",
"Metal5.net",
"Metal5.boundary",
"Metal5.mask",
"Metal5.nofill",
"Metal5.slit",
"Metal5.text",
"Metal5.OPC",
"Metal5.noqrc",
"Metal5.res",
"Metal5.iprobe",
"Metal5.diffprb",
"TopVia1.drawing",
"TopVia1.net",
"TopVia1.boundary",
"TopMetal1.drawing",
"TopMetal1.label",
"TopMetal1.pin",
"TopMetal1.net",
"TopMetal1.boundary",
"TopMetal1.mask",
"TopMetal1.nofill",
"TopMetal1.slit",
"TopMetal1.text",
"TopMetal1.noqrc",
"TopMetal1.res",
"TopMetal1.iprobe",
"TopMetal1.diffprb",
"TopVia2.drawing",
"TopVia2.net",
"TopVia2.boundary",
"TopMetal2.drawing",
"TopMetal2.label",
"TopMetal2.pin",
"TopMetal2.net",
"TopMetal2.boundary",
"TopMetal2.mask",
"TopMetal2.nofill",
"TopMetal2.slit",
"TopMetal2.text",
"TopMetal2.noqrc",
"TopMetal2.res",
"TopMetal2.iprobe",
"TopMetal2.diffprb",
"Passiv.drawing",
"Passiv.label",
"Passiv.pin",
"Passiv.net",
"Passiv.boundary",
"Passiv.pillar",
"Passiv.sbump",
"Passiv.pdl",
"NoMetFiller.drawing",
"ThinFilmRes.drawing",
"IND.drawing",
"IND.pin",
"IND.boundary",
"IND.text",
"EdgeSeal.drawing",
"EdgeSeal.boundary",
"dfpad.drawing",
"dfpad.pillar",
"dfpad.sbump",
"TEXT.drawing",
"Recog.drawing",
"Recog.pin",
"Recog.esd",
"Recog.diode",
"Recog.tsv",
"Recog.iprobe",
"Recog.diffprb",
"Recog.pillar",
"Recog.sbump",
"Recog.otp",
"Recog.pdiode",
"Recog.mom",
"Recog.pcm",
"DigiBnd.drawing",
"DigiBnd.drawing0",
"RES.drawing",
"RES.label",
"SRAM.drawing",
"SRAM.label",
"SRAM.boundary",
"TRANS.drawing",
"DigiSub.drawing",
"HeatTrans.drawing",
"HeatRes.drawing",
"NoRCX.drawing",
"NoRCX.m2m3",
"NoRCX.m2m4",
"NoRCX.m2m5",
"NoRCX.m2tm1",
"NoRCX.m2tm2",
"NoRCX.m3m4",
"NoRCX.m3m5",
"NoRCX.m3tm1",
"NoRCX.m3tm2",
"NoRCX.m4m5",
"NoRCX.m4tm1",
"NoRCX.m4tm2",
"NoRCX.m5tm1",
"NoRCX.m5tm2",
"NoRCX.tm1tm2",
"NoRCX.m1sub",
"NoRCX.m2sub",
"NoRCX.m3sub",
"NoRCX.m4sub",
"NoRCX.m5sub",
"NoRCX.tm1sub",
"NoRCX.tm2sub",
"BiWind.drawing",
"BiWind.OPC",
"BasPoly.drawing",
"BasPoly.label",
"BasPoly.pin",
"BasPoly.net",
"BasPoly.boundary",
"EmWind.drawing",
"EmWind.OPC",
"DeepCo.drawing",
"EmPoly.drawing",
"Varicap.drawing",
"EmWind3.drawing",
"EmWiHV3.drawing",
"ColOpen.drawing",
"ColWind.drawing",
"EmWiHV.drawing",
"DeepVia.drawing",
"LBE.drawing",
"BackMetal1.drawing",
"BackMetal1.label",
"BackMetal1.pin",
"BackMetal1.net",
"BackMetal1.boundary",
"BackMetal1.mask",
"BackMetal1.nofill",
"BackMetal1.slit",
"BackMetal1.text",
"BackMetal1.OPC",
"BackMetal1.noqrc",
"BackMetal1.res",
"BackMetal1.iprobe",
"BackMetal1.diffprb",
"BackPassiv.drawing",
"IntBondVia.drawing",
"IntBondMet.drawing",
"DevBondVia.drawing",
"DevBondMet.drawing",
"DevTrench.drawing",
"Redist.drawing",
"FBE.drawing",
"AntVia1.drawing",
"AntMetal2.drawing",
"AntMetal1.drawing",
"FLM.drawing",
"SNSRing.drawing",
"Sensor.drawing",
"SNSArms.drawing",
"SNSCMOSVia.drawing",
"SNSBotVia.drawing",
"SNSTopVia.drawing",
"NLDB.drawing",
"PLDB.drawing",
"RedBuLay.drawing",
"NLDD.drawing",
"PLDD.drawing",
"NExt.drawing",
"PExt.drawing",
"INLDPWL.drawing",
"nBuLayCut.drawing",
"GraphBot.drawing",
"GraphTop.drawing",
"GraphCont.drawing",
"SiWG.drawing",
"SiWG.nofill",
"SiGrating.drawing",
"SiNGrating.drawing",
"GraphPas.drawing",
"GraphPad.drawing",
"GraphMetal1.drawing",
"GraphMetal1.nofill",
"GraphMetal1.slit",
"GraphMetal1.OPC",
"GraphMet1L.drawing",
"GraphMet1L.nofill",
"GraphMet1L.slit",
"GraphMet1L.OPC",
"GraphGate.drawing",
"SiNWG.drawing",
"SiNWG.nofill",
"PWell.drawing",
"PWell.label",
"PWell.pin",
"PWell.net",
"PWell.boundary",
"IC.drawing",
"NoDRC.drawing",
"RadHard.drawing",
"MemCap.drawing",
"SMOS.drawing",
"Polimide.drawing",
"Polimide.label",
"Polimide.pin",
"Polimide.net",
"EXTBlock.drawing",
"NExtHV.drawing",
"PExtHV.drawing",
"MEMPAD.drawing",
"HafniumOx.drawing",
"MEMVia.drawing",
"RFMEM.drawing",
"FGEtch.drawing",
"CtrGat.drawing",
"FGImp.drawing",
"AlCuStop.drawing",
"prBoundary.drawing",
"prBoundary.label",
"prBoundary.boundary",
"Exchange0.drawing",
"Exchange0.label",
"Exchange0.pin",
"Exchange0.text",
"Exchange1.drawing",
"Exchange1.label",
"Exchange1.pin",
"Exchange1.text",
"Exchange2.drawing",
"Exchange2.label",
"Exchange2.pin",
"Exchange2.text",
"Exchange3.drawing",
"Exchange3.label",
"Exchange3.pin",
"Exchange3.text",
"Exchange4.drawing",
"Exchange4.label",
"Exchange4.pin",
"Exchange4.text",
"isoNWell.drawing",
]
# layers used for the analog pin check
layer_map_sky130 = {
"met4": (71, 20),
@@ -70,6 +435,41 @@ def analog_pin_pos_sky130(pin_number: int, uses_3v3: bool):
return 151.81 - 19.32 * pin_number - (15.64 if uses_3v3 else 0)
valid_layers = {"sky130": valid_layers_sky130}
valid_layers = {"sky130": valid_layers_sky130, "ihp-sg13g2": valid_layers_ihp_sg13g2}
layer_map = {"sky130": layer_map_sky130}
analog_pin_pos = {"sky130": analog_pin_pos_sky130}
lyp_filename = {"sky130": "sky130A.lyp", "ihp-sg13g2": "sg13g2.lyp"}
# TODO: read layer numbers from lyp file
valid_lef_port_layers = {
"sky130": {
"met1.pin": (68, 16),
"met2.pin": (69, 16),
"met3.pin": (70, 16),
"met4.pin": (71, 16),
},
"ihp-sg13g2": {
"Metal1.pin": (8, 2),
"Metal2.pin": (10, 2),
"Metal3.pin": (30, 2),
"Metal4.pin": (50, 2),
"Metal5.pin": (67, 2),
},
}
forbidden_layers = {
"sky130": ["met5.drawing", "met5.pin", "met5.label"],
"ihp-sg13g2": [
"TopMetal1.drawing",
"TopMetal1.pin",
"TopMetal1.label",
"TopMetal2.drawing",
"TopMetal2.pin",
"TopMetal2.label",
],
}
power_pins_layer = {
"sky130": "met4",
"ihp-sg13g2": "Metal5",
}
tech_names = ["sky130", "ihp-sg13g2"]
+15 -5
View File
@@ -477,14 +477,14 @@ def test_klayout_beol_fail(gds_fail_met1_poly: str):
def test_klayout_checks_pass(gds_valid: str):
precheck.klayout_checks(gds_valid, "TEST_valid")
precheck.klayout_checks(gds_valid, "TEST_valid", "sky130")
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, "TEST_met5_error")
precheck.klayout_checks(gds_fail_metal5_poly, "TEST_met5_error", "sky130")
def test_klayout_checks_fail_pr_boundary(gds_no_pr_boundary: str):
@@ -492,7 +492,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, "TEST_no_prboundary")
precheck.klayout_checks(gds_no_pr_boundary, "TEST_no_prboundary", "sky130")
def test_klayout_top_module_name(gds_invalid_macro_name: str):
@@ -500,7 +500,9 @@ 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, "TEST_invalid_macro_name")
precheck.klayout_checks(
gds_invalid_macro_name, "TEST_invalid_macro_name", "sky130"
)
def test_klayout_zero_area_drc_pass(gds_valid: str):
@@ -514,7 +516,7 @@ def test_klayout_zero_area_drc_fail(gds_zero_area: str):
def test_shapes_outside_area(gds_shapes_outside_area: str):
with pytest.raises(precheck.PrecheckFailure, match="Shapes outside project area"):
precheck.boundary_check(gds_shapes_outside_area)
precheck.boundary_check(gds_shapes_outside_area, "sky130")
def test_wrong_power_pins_1(verilog_lef_wrong_power_pins: tuple[str, str]):
@@ -558,6 +560,7 @@ def test_pin_analog_example(gds_lef_analog_example: tuple[str, str]):
"../def/analog/tt_analog_1x2.def",
"TEST_analog_example",
False,
"sky130",
)
@@ -569,6 +572,7 @@ def test_pin_analog_power_compat(gds_lef_analog_power_compat: tuple[str, str]):
"../def/analog/tt_analog_1x2.def",
"TEST_analog_power_compat",
False,
"sky130",
)
@@ -584,6 +588,7 @@ def test_pin_analog_wrong_vgnd(gds_lef_analog_wrong_vgnd: tuple[str, str]):
"../def/analog/tt_analog_1x2.def",
"TEST_analog_wrong_vgnd",
False,
"sky130",
)
@@ -599,6 +604,7 @@ def test_pin_analog_overlapping_vgnd(gds_lef_analog_overlapping_vgnd: tuple[str,
"../def/analog/tt_analog_1x2.def",
"TEST_analog_overlapping_vgnd",
False,
"sky130",
)
@@ -610,6 +616,7 @@ def test_pin_analog_compound_vgnd(gds_lef_analog_compound_vgnd: tuple[str, str])
"../def/analog/tt_analog_1x2.def",
"TEST_analog_compound_vgnd",
False,
"sky130",
)
@@ -621,6 +628,7 @@ def test_pin_analog_example_3v3(gds_lef_analog_example_3v3: tuple[str, str]):
"../def/analog/tt_analog_1x2.def",
"TEST_analog_example_3v3",
True,
"sky130",
)
@@ -636,6 +644,7 @@ def test_pin_analog_3v3_mismatch1(gds_lef_analog_example: tuple[str, str]):
"../def/analog/tt_analog_1x2.def",
"TEST_analog_example",
True,
"sky130",
)
@@ -651,6 +660,7 @@ def test_pin_analog_3v3_mismatch2(gds_lef_analog_example_3v3: tuple[str, str]):
"../def/analog/tt_analog_1x2.def",
"TEST_analog_example_3v3",
False,
"sky130",
)