diff --git a/precheck/precheck.py b/precheck/precheck.py index a71a60a..0fa7865 100644 --- a/precheck/precheck.py +++ b/precheck/precheck.py @@ -17,10 +17,9 @@ 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, + analog_pin_rects, boundary_layer, forbidden_layers, - layer_map, lyp_filename, tech_names, valid_layers, @@ -261,42 +260,50 @@ def analog_pin_check( if is_analog: lib = gdstk.read_gds(gds) top = lib.top_level()[0] - met4 = top.copy("test_met4") - met4.flatten() - met4.filter([layer_map[tech]["met4"]], False) - via3 = top.copy("test_via3") - via3.flatten() - via3.filter([layer_map[tech]["via3"]], False) + filtered = {} - for pin in range(8): - x = analog_pin_pos[tech](pin, uses_3v3) - pin_over = gdstk.rectangle((x, 0), (x + 0.9, 1.0)) - pin_around = gdstk.boolean( - gdstk.offset(pin_over, 0.5), gdstk.offset(pin_over, 0.1), "not" + for pin, (rect, pin_layer, via_layers) in enumerate( + analog_pin_rects(tech, uses_3v3) + ): + for layer in [pin_layer] + via_layers: + if layer not in filtered: + i = len(filtered) + lf = top.copy(f"test_lf_{i}") + lf.flatten() + lf.filter([layer], False) + filtered[layer] = lf + + pin_rect = gdstk.rectangle(*rect) + pin_ring = gdstk.boolean( + gdstk.offset(pin_rect, 0.5), gdstk.offset(pin_rect, 0.1), "not" ) - via3_over = gdstk.boolean(via3.polygons, pin_over, "and") - met4_around = gdstk.boolean(met4.polygons, pin_around, "and") - connected = bool(via3_over) or bool(met4_around) + pin_layer_polygons = filtered[pin_layer].polygons + connected = bool(gdstk.boolean(pin_layer_polygons, pin_ring, "and")) + for via_layer in via_layers: + via_layer_polygons = filtered[via_layer].polygons + connected = connected or bool( + gdstk.boolean(via_layer_polygons, pin_rect, "and") + ) expected_pc = pin < analog_pins expected_pd = bool(pinout.get(f"ua[{pin}]", "")) if connected and not expected_pc: raise PrecheckFailure( - f"Analog pin `ua[{pin}]` is connected to some metal but `analog_pins` is set to {analog_pins} in `info.yaml`. Either increase `analog_pins` to at least {pin+1}, or remove any metal4 or via3 adjacent to `ua[{pin}]`." + f"Analog pin `ua[{pin}]` is connected to some metal but `analog_pins` is set to {analog_pins} in `info.yaml`. Either increase `analog_pins` to at least {pin+1}, or remove any metal or via adjacent to `ua[{pin}]`." ) elif connected and not expected_pd: raise PrecheckFailure( - f"Analog pin `ua[{pin}]` is connected to some metal but the description of `ua[{pin}]` in the pinout section of `info.yaml` is empty. Either add a description or remove any metal4 or via3 adjacent to `ua[{pin}]`." + f"Analog pin `ua[{pin}]` is connected to some metal but the description of `ua[{pin}]` in the pinout section of `info.yaml` is empty. Either add a description or remove any metal or via adjacent to `ua[{pin}]`." ) elif not connected and expected_pc: raise PrecheckFailure( - f"Analog pin `ua[{pin}]` is not connected to any adjacent metal but `analog_pins` is set to {analog_pins} in `info.yaml`. Either wire up `ua[{pin}]` to your design using metal4 or via3, or decrease `analog_pins` to {pin}." + f"Analog pin `ua[{pin}]` is not connected to any adjacent metal but `analog_pins` is set to {analog_pins} in `info.yaml`. Either wire up `ua[{pin}]` to your design or decrease `analog_pins` to {pin}." ) elif not connected and expected_pd: raise PrecheckFailure( - f"Analog pin `ua[{pin}]` is not connected to any adjacent metal but the description of `ua[{pin}]` in the pinout section of `info.yaml` is non-empty. Either wire up `ua[{pin}]` to your design using metal4 or via3, or remove the description for the disconnected pin." + f"Analog pin `ua[{pin}]` is not connected to any adjacent metal but the description of `ua[{pin}]` in the pinout section of `info.yaml` is non-empty. Either wire up `ua[{pin}]` to your design or remove the description for the disconnected pin." ) @@ -437,7 +444,7 @@ def main(): "check": lambda: analog_pin_check( gds_file, tech, is_analog, uses_3v3, analog_pins, pinout ), - "techs": ["sky130A"], + "techs": ["sky130A", "ihp-sg13g2"], }, ] diff --git a/precheck/tech_data.py b/precheck/tech_data.py index 3ae78af..adb1e81 100644 --- a/precheck/tech_data.py +++ b/precheck/tech_data.py @@ -1,3 +1,5 @@ +tech_names = ["sky130A", "ihp-sg13g2", "gf180mcuD"] + # fmt: off valid_layers_sky130A = [ 'cldntm.mask', 'ldntm.drawing', 'cncm.mask', 'ctunm.mask', 'cnwm.mask', 'cnsm.mask', 'cfom.drawing', 'cfom.maskAdd', @@ -93,15 +95,34 @@ valid_layers_gf180mcuD = [ ] # fmt: on -# layers used for the analog pin check -layer_map_sky130A = { - "met4": (71, 20), - "via3": (70, 44), -} - -def analog_pin_pos_sky130A(pin_number: int, uses_3v3: bool): - return 151.81 - 19.32 * pin_number - (15.64 if uses_3v3 else 0) +def analog_pin_rects(tech: str, uses_3v3: bool): + if tech == "sky130A": + pin_layer = (71, 20) # met4 + via_layers = [(70, 44)] # via3 + for pin_number in range(8): + if uses_3v3: + x1, y1 = 136.17 - 19.32 * pin_number, 0.0 + else: + x1, y1 = 151.81 - 19.32 * pin_number, 0.0 + x2, y2 = x1 + 0.9, y1 + 1.0 + rect = ((x1, y1), (x2, y2)) + yield (rect, pin_layer, via_layers) + elif tech == "ihp-sg13g2": + pin_layer = (126, 0) # TopMetal1 + via_layers = [(125, 0)] # TopVia1 + for pin_number in range(8): + if uses_3v3: + raise NotImplementedError + else: + x1, y1 = 190.165 - 24.48 * pin_number, 0.0 + x2, y2 = x1 + 1.75, y1 + 2.0 + rect = ((x1, y1), (x2, y2)) + yield (rect, pin_layer, via_layers) + elif tech in tech_names: + raise NotImplementedError + else: + raise ValueError(f"unexpected tech {tech}") valid_layers = { @@ -109,8 +130,6 @@ valid_layers = { "ihp-sg13g2": valid_layers_ihp_sg13g2, "gf180mcuD": valid_layers_gf180mcuD, } -layer_map = {"sky130A": layer_map_sky130A} -analog_pin_pos = {"sky130A": analog_pin_pos_sky130A} lyp_filename = { "sky130A": "sky130A.lyp", "ihp-sg13g2": "sg13g2.lyp", @@ -165,5 +184,3 @@ boundary_layer = { "ihp-sg13g2": "prBoundary.boundary", "gf180mcuD": "PR_bndry", } - -tech_names = ["sky130A", "ihp-sg13g2", "gf180mcuD"]