From c350c2ed16c483ce268ef4449ab47d66980addfc Mon Sep 17 00:00:00 2001 From: Uri Shaked Date: Wed, 16 Apr 2025 08:28:51 +0300 Subject: [PATCH] feat(precheck): initial IHP support still missing the IHP Klayout DRC deck --- precheck/klayout_tools.py | 4 +- precheck/pin_check.py | 15 +- precheck/precheck.py | 149 ++++++++++---- precheck/tech_data.py | 402 +++++++++++++++++++++++++++++++++++++- precheck/test_precheck.py | 20 +- 5 files changed, 532 insertions(+), 58 deletions(-) diff --git a/precheck/klayout_tools.py b/precheck/klayout_tools.py index 9b130a8..df46ded 100644 --- a/precheck/klayout_tools.py +++ b/precheck/klayout_tools.py @@ -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 ( diff --git a/precheck/pin_check.py b/precheck/pin_check.py index 9c2ba31..c8e8ee3 100644 --- a/precheck/pin_check.py +++ b/precheck/pin_check.py @@ -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: diff --git a/precheck/precheck.py b/precheck/precheck.py index 811ea54..1a9fc27 100644 --- a/precheck/precheck.py +++ b/precheck/precheck.py @@ -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))) diff --git a/precheck/tech_data.py b/precheck/tech_data.py index 3bc456f..820bbe6 100644 --- a/precheck/tech_data.py +++ b/precheck/tech_data.py @@ -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"] diff --git a/precheck/test_precheck.py b/precheck/test_precheck.py index 2e5448b..e745bd7 100644 --- a/precheck/test_precheck.py +++ b/precheck/test_precheck.py @@ -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", )