"""Export the netlist with kicad-cli and compare it with PARTS in gen_kicad.py. python hardware/kicad/check_netlist.py [path/to/kicad-cli] """ import re import subprocess import sys from pathlib import Path from gen_kicad import PARTS, PROJECT, pin_cache_for, read_sexpr_block HERE = Path(__file__).parent CLI = sys.argv[1] if len(sys.argv) > 1 else "C:/Users/patri/AppData/Local/Programs/KiCad/10.0/bin/kicad-cli.exe" net_file = HERE / f"{PROJECT}.net" subprocess.run([CLI, "sch", "export", "netlist", "-o", str(net_file), str(HERE / f"{PROJECT}.kicad_sch")], check=True, capture_output=True) text = net_file.read_text(encoding="utf-8") net_file.unlink() # actual: (ref, pin number) -> net name actual = {} for m in re.finditer(r"\(net\s+\(code", text): blk = read_sexpr_block(text, m.start()) name = re.search(r'\(name "([^"]*)"\)', blk).group(1).lstrip("/") for ref, pin in re.findall(r'\(node\s+\(ref "([^"]+)"\)\s+\(pin "([^"]+)"\)', blk): actual[(ref, pin)] = name errors = 0 expected = {} for ref, lib_id, value, fp, conns, g in PARTS: for num, name, *_ in pin_cache_for(lib_id): key = num if num in conns else (name if name in conns else None) if key is None or conns[key] is None: continue expected[(ref, num)] = conns[key] for k, net in expected.items(): got = actual.get(k) if got != net: errors += 1 print(f"MISMATCH {k}: expected {net}, got {got}") # every net should join at least two pins by_net = {} for k, net in actual.items(): by_net.setdefault(net, []).append(k) for net, nodes in sorted(by_net.items()): if len(nodes) < 2 and not net.startswith("unconnected"): errors += 1 print(f"SINGLE-PIN NET {net}: {nodes}") print(f"{len(expected)} pins checked, {len(by_net)} nets, {errors} problem(s)") sys.exit(1 if errors else 0)