mirror of
https://github.com/TinyTapeout/tt-support-tools.git
synced 2026-11-03 04:36:09 +01:00
feat(precheck): check if power pins have USE in definition (#139)
* feat(precheck): check if power pins have `USE` in definition * fix: expect zero or more spaces, instead of one or more * feat(precheck): check for missing `USE POWER` * feat(precheck): check for missing `USE GROUND` * feat(precheck): add simple README with link to LEF/DEF reference manual
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
# Precheck
|
||||
|
||||
## Documentation reference
|
||||
- [LEF/DEF Language Reference (cadence) (November 2009)](https://www.ispd.cc/contests/18/lefdefref.pdf) ([web archive](https://web.archive.org/web/20250809120606/https://www.ispd.cc/contests/18/lefdefref.pdf))
|
||||
+26
-1
@@ -154,7 +154,7 @@ def boundary_check(gds: str, tech: str):
|
||||
|
||||
|
||||
def power_pin_check(verilog: str, lef: str, uses_3v3: bool):
|
||||
"""Ensure that VPWR / VGND are present,
|
||||
"""Ensure that VPWR / VGND are present and have USE definitions,
|
||||
and that VAPWR is present if and only if 'uses_3v3' is set."""
|
||||
verilog_s = open(verilog).read().replace("VPWR", "VDPWR")
|
||||
lef_s = open(lef).read().replace("VPWR", "VDPWR")
|
||||
@@ -163,6 +163,12 @@ def power_pin_check(verilog: str, lef: str, uses_3v3: bool):
|
||||
verilog_s = re.sub("//.*", "", verilog_s)
|
||||
verilog_s = re.sub("/\\*.*?\\*/", "", verilog_s, flags=(re.DOTALL | re.MULTILINE))
|
||||
|
||||
# this looks for a line beginning with "PIN", captures the name of the pin and the body up until its "END"
|
||||
PIN_PATTERN = re.compile(
|
||||
r"^\s*PIN (VPWR|VDPWR|VAPWR|VGND)\s*([\s\S]+?(?=^\s*END \1))",
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
|
||||
for ft, s in (("Verilog", verilog_s), ("LEF", lef_s)):
|
||||
for pwr, ex in (("VGND", True), ("VDPWR", True), ("VAPWR", uses_3v3)):
|
||||
if (pwr in s) and not ex:
|
||||
@@ -170,6 +176,25 @@ def power_pin_check(verilog: str, lef: str, uses_3v3: bool):
|
||||
if not (pwr in s) and ex:
|
||||
raise PrecheckFailure(f"{ft} doesn't contain {pwr}")
|
||||
|
||||
for match in PIN_PATTERN.finditer(lef_s):
|
||||
pin, definition = match.groups()
|
||||
|
||||
match pin:
|
||||
case "VPWR" | "VDPWR" | "VAPWR":
|
||||
if "USE POWER" not in definition:
|
||||
raise PrecheckFailure(
|
||||
f"{pin} does not have a corresponding 'USE POWER ;'"
|
||||
)
|
||||
|
||||
case "VGND":
|
||||
if "USE GROUND" not in definition:
|
||||
raise PrecheckFailure(
|
||||
f"{pin} does not have a corresponding 'USE GROUND ;'"
|
||||
)
|
||||
|
||||
case _:
|
||||
raise PrecheckFailure(f"unhandled {pin}")
|
||||
|
||||
|
||||
def layer_check(gds: str, tech: str):
|
||||
"""Check that there are no invalid layers in the GDS file."""
|
||||
|
||||
@@ -167,6 +167,94 @@ def verilog_lef_wrong_power_pins(tmp_path_factory: pytest.TempPathFactory):
|
||||
return str(verilog_file), str(lef_file)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def verilog_lef_missing_use_power(tmp_path_factory: pytest.TempPathFactory):
|
||||
"""Creates a Verilog & LEF file with wrong power pins."""
|
||||
verilog_file = tmp_path_factory.mktemp("verilog") / "TEST_missing_use_power.v"
|
||||
verilog_data = """
|
||||
`default_nettype none
|
||||
module TEST_wrong_power_pins (
|
||||
input wire VGND,
|
||||
input wire VDPWR
|
||||
);
|
||||
endmodule
|
||||
"""
|
||||
open(verilog_file, "w").write(textwrap.dedent(verilog_data))
|
||||
lef_file = tmp_path_factory.mktemp("lef") / "TEST_missing_use_power.lef"
|
||||
lef_data = """
|
||||
VERSION 5.7 ;
|
||||
NOWIREEXTENSIONATPIN ON ;
|
||||
DIVIDERCHAR "/" ;
|
||||
BUSBITCHARS "[]" ;
|
||||
MACRO TEST_missing_use_power
|
||||
CLASS BLOCK ;
|
||||
FOREIGN TEST_missing_use_power ;
|
||||
ORIGIN 0.000 0.000 ;
|
||||
SIZE 161.000 BY 111.520 ;
|
||||
PIN VGND
|
||||
DIRECTION INOUT ;
|
||||
USE GROUND ;
|
||||
PORT
|
||||
LAYER met4 ;
|
||||
RECT 21.580 2.480 23.180 109.040 ;
|
||||
END
|
||||
END VGND
|
||||
PIN VDPWR
|
||||
DIRECTION INOUT ;
|
||||
PORT
|
||||
LAYER met4 ;
|
||||
RECT 18.280 2.480 19.880 109.040 ;
|
||||
END
|
||||
END VDPWR
|
||||
"""
|
||||
open(lef_file, "w").write(textwrap.dedent(lef_data))
|
||||
return str(verilog_file), str(lef_file)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def verilog_lef_missing_use_ground(tmp_path_factory: pytest.TempPathFactory):
|
||||
"""Creates a Verilog & LEF file with wrong power pins."""
|
||||
verilog_file = tmp_path_factory.mktemp("verilog") / "TEST_missing_use_ground.v"
|
||||
verilog_data = """
|
||||
`default_nettype none
|
||||
module TEST_wrong_power_pins (
|
||||
input wire VGND,
|
||||
input wire VDPWR
|
||||
);
|
||||
endmodule
|
||||
"""
|
||||
open(verilog_file, "w").write(textwrap.dedent(verilog_data))
|
||||
lef_file = tmp_path_factory.mktemp("lef") / "TEST_missing_use_ground.lef"
|
||||
lef_data = """
|
||||
VERSION 5.7 ;
|
||||
NOWIREEXTENSIONATPIN ON ;
|
||||
DIVIDERCHAR "/" ;
|
||||
BUSBITCHARS "[]" ;
|
||||
MACRO TEST_missing_use_ground
|
||||
CLASS BLOCK ;
|
||||
FOREIGN TEST_missing_use_ground ;
|
||||
ORIGIN 0.000 0.000 ;
|
||||
SIZE 161.000 BY 111.520 ;
|
||||
PIN VGND
|
||||
DIRECTION INOUT ;
|
||||
PORT
|
||||
LAYER met4 ;
|
||||
RECT 21.580 2.480 23.180 109.040 ;
|
||||
END
|
||||
END VGND
|
||||
PIN VDPWR
|
||||
DIRECTION INOUT ;
|
||||
USE POWER ;
|
||||
PORT
|
||||
LAYER met4 ;
|
||||
RECT 18.280 2.480 19.880 109.040 ;
|
||||
END
|
||||
END VDPWR
|
||||
"""
|
||||
open(lef_file, "w").write(textwrap.dedent(lef_data))
|
||||
return str(verilog_file), str(lef_file)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def gds_invalid_layer(tmp_path_factory: pytest.TempPathFactory):
|
||||
"""Creates a GDS with a layer/datatype not defined in sky130."""
|
||||
@@ -531,6 +619,24 @@ def test_wrong_power_pins_2(verilog_lef_wrong_power_pins: tuple[str, str]):
|
||||
precheck.power_pin_check(verilog_file, lef_file, uses_3v3=True)
|
||||
|
||||
|
||||
def test_missing_use_power(verilog_lef_missing_use_power: tuple[str, str]):
|
||||
verilog_file, lef_file = verilog_lef_missing_use_power
|
||||
with pytest.raises(
|
||||
precheck.PrecheckFailure,
|
||||
match="VDPWR does not have a corresponding 'USE POWER ;'",
|
||||
):
|
||||
precheck.power_pin_check(verilog_file, lef_file, uses_3v3=False)
|
||||
|
||||
|
||||
def test_missing_use_ground(verilog_lef_missing_use_ground: tuple[str, str]):
|
||||
verilog_file, lef_file = verilog_lef_missing_use_ground
|
||||
with pytest.raises(
|
||||
precheck.PrecheckFailure,
|
||||
match="VGND does not have a corresponding 'USE GROUND ;'",
|
||||
):
|
||||
precheck.power_pin_check(verilog_file, lef_file, uses_3v3=False)
|
||||
|
||||
|
||||
def test_invalid_layer(gds_invalid_layer: str):
|
||||
with pytest.raises(precheck.PrecheckFailure, match="Invalid layers in GDS"):
|
||||
precheck.layer_check(gds_invalid_layer, "sky130A")
|
||||
|
||||
Reference in New Issue
Block a user