diff --git a/precheck/pin_check.py b/precheck/pin_check.py index aa85fa5..b7fac07 100644 --- a/precheck/pin_check.py +++ b/precheck/pin_check.py @@ -112,14 +112,14 @@ def parse_dbu_to_nm(value: str, dbu_per_micron: int): def pin_check( - gds: str, lef: str, template_def: str, toplevel: str, uses_3v3: bool, tech: str + gds: str, lef: str, template_def: str, toplevel: str, uses_vapwr: bool, tech: str ): logging.info("Running pin check...") logging.info(f"* gds: {gds}") logging.info(f"* lef: {lef}") logging.info(f"* template_def: {template_def}") logging.info(f"* toplevel: {toplevel}") - logging.info(f"* uses_3v3: {uses_3v3}") + logging.info(f"* uses_vapwr: {uses_vapwr}") # parse pins from template def # def syntax: https://coriolis.lip6.fr/doc/lefdef/lefdefref/DEFSyntax.html @@ -201,7 +201,7 @@ def pin_check( rect_re = re.compile(r"RECT (\S+) (\S+) (\S+) (\S+) ;") power_pins = ["VGND", "VDPWR"] - if uses_3v3: + if uses_vapwr: power_pins.append("VAPWR") compat_pins = {"VPWR": "VDPWR"} diff --git a/precheck/precheck.py b/precheck/precheck.py index 68bc1fa..5ab5444 100644 --- a/precheck/precheck.py +++ b/precheck/precheck.py @@ -181,9 +181,9 @@ def boundary_check(gds: str, tech: str): raise PrecheckFailure("Shapes outside project area") -def power_pin_check(verilog: str, lef: str, uses_3v3: bool): +def power_pin_check(verilog: str, lef: str, uses_vapwr: bool): """Ensure that VPWR / VGND are present and have USE definitions, - and that VAPWR is present if and only if 'uses_3v3' is set.""" + and that VAPWR is present if and only if 'uses_vapwr' is set.""" verilog_s = open(verilog).read().replace("VPWR", "VDPWR") lef_s = open(lef).read().replace("VPWR", "VDPWR") @@ -198,7 +198,7 @@ def power_pin_check(verilog: str, lef: str, uses_3v3: bool): ) for ft, s in (("Verilog", verilog_s), ("LEF", lef_s)): - for pwr, ex in (("VGND", True), ("VDPWR", True), ("VAPWR", uses_3v3)): + for pwr, ex in (("VGND", True), ("VDPWR", True), ("VAPWR", uses_vapwr)): if (pwr in s) and not ex: raise PrecheckFailure(f"{ft} contains {pwr}") if not (pwr in s) and ex: @@ -269,7 +269,12 @@ def urpm_nwell_check(gds: str, top_module: str): def analog_pin_check( - gds: str, tech: str, is_analog: bool, uses_3v3: bool, analog_pins: int, pinout: dict + gds: str, + tech: str, + is_analog: bool, + uses_vapwr: bool, + analog_pins: int, + pinout: dict, ): """Check that every analog pin connects to a piece of metal if and only if the pin is used according to info.yaml.""" @@ -279,7 +284,7 @@ def analog_pin_check( filtered = {} for pin, (rect, pin_layer, via_layers) in enumerate( - analog_pin_rects(tech, uses_3v3) + analog_pin_rects(tech, uses_vapwr) ): for layer in [pin_layer] + via_layers: if layer not in filtered: @@ -390,17 +395,19 @@ def main(): top_module = yaml_data["project"].get("top_module", f"tt_um_wokwi_{wokwi_id}") assert top_module == os.path.basename(gds_stem) - tiles = yaml_data.get("project", {}).get("tiles", "1x1") - analog_pins = yaml_data.get("project", {}).get("analog_pins", 0) + project_cfg = yaml_data.get("project", {}) + tiles = project_cfg.get("tiles", "1x1") + analog_pins = project_cfg.get("analog_pins", 0) is_analog = analog_pins > 0 - uses_3v3 = bool(yaml_data.get("project", {}).get("uses_3v3", False)) + # "uses_3v3" is the legacy name for "uses_vapwr"; still accepted for compat. + uses_vapwr = bool(project_cfg.get("uses_vapwr", project_cfg.get("uses_3v3", False))) pinout = yaml_data.get("pinout", {}) - if uses_3v3 and not is_analog: - raise PrecheckFailure("Projects with 3v3 power need at least one analog pin") + if uses_vapwr and not is_analog: + raise PrecheckFailure("Projects with VAPWR power need at least one analog pin") def_root = f"../tech/{tech}/def" if is_analog: analog_def = f"{def_root}/analog/tt_analog_{tiles}" - if uses_3v3: + if uses_vapwr: # gf180's second supply rail is "pgvaa" (its core is already 3v3, so # "_3v3" is a misnomer); fall back to "_3v3" for techs that use it. template_def = f"{analog_def}_pgvaa.def" @@ -465,13 +472,13 @@ def main(): { "name": "Pin check", "check": lambda: pin_check( - gds_file, lef_file, template_def, top_module, uses_3v3, tech + gds_file, lef_file, template_def, top_module, uses_vapwr, 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), + "check": lambda: power_pin_check(verilog_file, lef_file, uses_vapwr), "techs": ["sky130A", "gf180mcuD"], }, {"name": "Layer check", "check": lambda: layer_check(gds_file, tech)}, @@ -489,7 +496,7 @@ def main(): { "name": "Analog pin check", "check": lambda: analog_pin_check( - gds_file, tech, is_analog, uses_3v3, analog_pins, pinout + gds_file, tech, is_analog, uses_vapwr, analog_pins, pinout ), "techs": ["sky130A", "ihp-sg13g2", "gf180mcuD"], }, diff --git a/precheck/tech_data.py b/precheck/tech_data.py index a1a177c..20540cb 100644 --- a/precheck/tech_data.py +++ b/precheck/tech_data.py @@ -96,12 +96,12 @@ valid_layers_gf180mcuD = [ # fmt: on -def analog_pin_rects(tech: str, uses_3v3: bool): +def analog_pin_rects(tech: str, uses_vapwr: bool): if tech == "sky130A": pin_layer = (71, 20) # met4 via_layers = [(70, 44)] # via3 for pin_number in range(8): - if uses_3v3: + if uses_vapwr: x1, y1 = 136.17 - 19.32 * pin_number, 0.0 else: x1, y1 = 151.81 - 19.32 * pin_number, 0.0 @@ -112,7 +112,7 @@ def analog_pin_rects(tech: str, uses_3v3: bool): pin_layer = (126, 0) # TopMetal1 via_layers = [(125, 0)] # TopVia1 for pin_number in range(8): - if uses_3v3: + if uses_vapwr: raise NotImplementedError else: x1, y1 = 190.165 - 24.48 * pin_number, 0.0 @@ -123,7 +123,7 @@ def analog_pin_rects(tech: str, uses_3v3: bool): pin_layer = (46, 0) # Metal4 via_layers = [(40, 0)] # Via3 for pin_number in range(8): - if uses_3v3: + if uses_vapwr: x1, y1 = 307.90 - 43.68 * pin_number, 0.0 else: x1, y1 = 325.82 - 43.68 * pin_number, 0.0 diff --git a/precheck/test_precheck.py b/precheck/test_precheck.py index be394e5..8a5ae0e 100644 --- a/precheck/test_precheck.py +++ b/precheck/test_precheck.py @@ -697,13 +697,13 @@ def test_shapes_outside_area(gds_shapes_outside_area: str): def test_wrong_power_pins_1(verilog_lef_wrong_power_pins: tuple[str, str]): verilog_file, lef_file = verilog_lef_wrong_power_pins with pytest.raises(precheck.PrecheckFailure, match="Verilog contains VAPWR"): - precheck.power_pin_check(verilog_file, lef_file, uses_3v3=False) + precheck.power_pin_check(verilog_file, lef_file, uses_vapwr=False) def test_wrong_power_pins_2(verilog_lef_wrong_power_pins: tuple[str, str]): verilog_file, lef_file = verilog_lef_wrong_power_pins with pytest.raises(precheck.PrecheckFailure, match="LEF doesn't contain VAPWR"): - precheck.power_pin_check(verilog_file, lef_file, uses_3v3=True) + precheck.power_pin_check(verilog_file, lef_file, uses_vapwr=True) def test_missing_use_power(verilog_lef_missing_use_power: tuple[str, str]): @@ -712,7 +712,7 @@ def test_missing_use_power(verilog_lef_missing_use_power: tuple[str, str]): precheck.PrecheckFailure, match="VDPWR does not have a corresponding 'USE POWER ;'", ): - precheck.power_pin_check(verilog_file, lef_file, uses_3v3=False) + precheck.power_pin_check(verilog_file, lef_file, uses_vapwr=False) def test_missing_use_ground(verilog_lef_missing_use_ground: tuple[str, str]): @@ -721,7 +721,7 @@ def test_missing_use_ground(verilog_lef_missing_use_ground: tuple[str, str]): precheck.PrecheckFailure, match="VGND does not have a corresponding 'USE GROUND ;'", ): - precheck.power_pin_check(verilog_file, lef_file, uses_3v3=False) + precheck.power_pin_check(verilog_file, lef_file, uses_vapwr=False) def test_invalid_layer(gds_invalid_layer: str): diff --git a/project.py b/project.py index 1f637fe..6854e21 100644 --- a/project.py +++ b/project.py @@ -203,7 +203,7 @@ class Project: [("input", "inout"), "VGND", 1], [("input", "inout"), "VDPWR", 1], ] - if self.info.uses_3v3: + if self.info.uses_vapwr: required_ports += [ [("input", "inout"), "VAPWR", 1], ] @@ -653,7 +653,7 @@ class Project: for i, desc in enumerate(self.info.pinout.ua) ], "is_analog": self.info.is_analog, - "uses_3v3": self.info.uses_3v3, + "uses_vapwr": self.info.uses_vapwr, } ) diff --git a/project_info.py b/project_info.py index c886a04..1d78d53 100644 --- a/project_info.py +++ b/project_info.py @@ -131,10 +131,13 @@ class ProjectInfo: self.analog_pins: int = analog_pins self.is_analog: bool = self.analog_pins > 0 - uses_3v3: bool = project_section.get("uses_3v3", False) - self.uses_3v3: bool = uses_3v3 - if uses_3v3 and not self.is_analog: - errors.append("Projects with 3v3 power need at least one analog pin") + # "uses_3v3" is the legacy name for "uses_vapwr"; still accepted for compat. + uses_vapwr: bool = project_section.get( + "uses_vapwr", project_section.get("uses_3v3", False) + ) + self.uses_vapwr: bool = uses_vapwr + if uses_vapwr and not self.is_analog: + errors.append("Projects with VAPWR power need at least one analog pin") language = project_section.get("language") if language is None: diff --git a/shuttle.py b/shuttle.py index 21df574..d341f58 100644 --- a/shuttle.py +++ b/shuttle.py @@ -101,7 +101,7 @@ class ShuttleConfig: "width": width, "height": height, "analog": {i: None for i in range(project.info.analog_pins)}, - "pg_vaa": project.info.uses_3v3, + "pg_vaa": project.info.uses_vapwr, } module_config["modules"].append(module_entry) if self.config.get("no_power_gating", False): diff --git a/test_project_info.py b/test_project_info.py index 58bdb64..b199948 100644 --- a/test_project_info.py +++ b/test_project_info.py @@ -15,7 +15,7 @@ def valid_yaml_data(): "description": "Test Description", "tiles": "1x1", "analog_pins": 0, - "uses_3v3": False, + "uses_vapwr": False, "language": "Verilog", "top_module": "tt_um_test_project", "source_files": ["test.v"], @@ -167,12 +167,12 @@ class TestProjectInfoProjectSection: ): ProjectInfo(valid_yaml_data, tile_sizes) - def test_uses_3v3_without_analog_pins(self, valid_yaml_data, tile_sizes): - valid_yaml_data["project"]["uses_3v3"] = True + def test_uses_vapwr_without_analog_pins(self, valid_yaml_data, tile_sizes): + valid_yaml_data["project"]["uses_vapwr"] = True valid_yaml_data["project"]["analog_pins"] = 0 with pytest.raises( ProjectYamlError, - match="Projects with 3v3 power need at least one analog pin", + match="Projects with VAPWR power need at least one analog pin", ): ProjectInfo(valid_yaml_data, tile_sizes) @@ -375,7 +375,7 @@ class TestProjectInfoValidCombinations: def test_valid_analog_project(self, valid_yaml_data, tile_sizes): valid_yaml_data["project"]["analog_pins"] = 2 - valid_yaml_data["project"]["uses_3v3"] = True + valid_yaml_data["project"]["uses_vapwr"] = True # Include all required ua pins for i in range(6): valid_yaml_data["pinout"][f"ua[{i}]"] = f"analog_{i}" if i < 2 else "" @@ -383,13 +383,24 @@ class TestProjectInfoValidCombinations: project_info = ProjectInfo(valid_yaml_data, tile_sizes) assert project_info.analog_pins == 2 assert project_info.is_analog is True - assert project_info.uses_3v3 is True + assert project_info.uses_vapwr is True + + def test_uses_vapwr_legacy_uses_3v3_alias(self, valid_yaml_data, tile_sizes): + # The legacy "uses_3v3" key is still accepted as an alias for uses_vapwr. + valid_yaml_data["project"]["analog_pins"] = 2 + del valid_yaml_data["project"]["uses_vapwr"] + valid_yaml_data["project"]["uses_3v3"] = True + for i in range(6): + valid_yaml_data["pinout"][f"ua[{i}]"] = f"analog_{i}" if i < 2 else "" + + project_info = ProjectInfo(valid_yaml_data, tile_sizes) + assert project_info.uses_vapwr is True def test_valid_digital_project(self, valid_yaml_data, tile_sizes): project_info = ProjectInfo(valid_yaml_data, tile_sizes) assert project_info.analog_pins == 0 assert project_info.is_analog is False - assert project_info.uses_3v3 is False + assert project_info.uses_vapwr is False def test_optional_fields(self, valid_yaml_data, tile_sizes): # Test that optional fields work @@ -406,11 +417,11 @@ class TestProjectInfoValidCombinations: project_info = ProjectInfo(valid_yaml_data, tile_sizes) assert project_info.analog_pins == 0 - def test_default_uses_3v3(self, valid_yaml_data, tile_sizes): - # Test that uses_3v3 defaults to False when not specified - del valid_yaml_data["project"]["uses_3v3"] + def test_default_uses_vapwr(self, valid_yaml_data, tile_sizes): + # Test that uses_vapwr defaults to False when not specified + del valid_yaml_data["project"]["uses_vapwr"] project_info = ProjectInfo(valid_yaml_data, tile_sizes) - assert project_info.uses_3v3 is False + assert project_info.uses_vapwr is False def test_all_tile_sizes(self, valid_yaml_data, tile_sizes): # Test all valid tile sizes