main
Rithesh03 Update 2: schematic completed and reviewed (rev 2) a5b0bed 13d ago
"""Check the KiCad-exported netlist against the intended design.

Usage: python3 check_netlist.py <netlist.xml>
Exit code 0 = every expected connection found and no unexpected merges.
"""
import sys
import xml.etree.ElementTree as ET

tree = ET.parse(sys.argv[1])
nets = {}
for net in tree.getroot().iter("net"):
    name = net.get("name").split("/")[-1]
    nodes = {(n.get("ref"), n.get("pin")) for n in net.iter("node")}
    nets[name] = nets.get(name, set()) | nodes

pin_net = {}
for name, nodes in nets.items():
    for node in nodes:
        pin_net[node] = name

EXPECT = {
    "VBUS": ["J1.A4", "J1.A9", "J1.B4", "J1.B9", "D1.1", "U1.5", "C1.1", "U2.3", "U2.4", "R4.1", "TP1.1"],
    "GND": ["J1.A1", "J1.S1", "R1.2", "R2.2", "D1.2", "U1.2", "C1.2", "U2.8", "U2.9", "R5.2", "C2.2",
            "R3.2", "U3.3", "U3.5", "U3.7", "C7.2", "C8.2", "TP4.1", "TP5.1", "U4.1", "U4.49", "C9.2",
            "C10.2", "C11.2", "C13.2", "C14.2", "SW1.2", "SW2.2", "SW3.2", "U5.1", "U5.3", "R12.2", "C12.2",
            "LED1.3", "LED107.3", "C101.2", "C207.2", "C301.2", "C304.2"],
    "+5V": ["U2.5", "U3.6", "U3.4", "C7.1", "TP2.1", "U5.5", "C12.1", "LED1.1", "LED54.1",
            "LED107.1", "C101.1", "C207.1", "C301.1", "C302.1", "C303.1", "C304.1"],
    "+3V3": ["U3.1", "U3.2", "C8.1", "TP3.1", "R6.1", "U4.3", "C9.1", "C10.1", "R7.1", "R8.1",
             "R9.1", "R10.1", "R11.1"],
    "CC1": ["J1.A5", "R1.1", "U4.12"],
    "CC2": ["J1.B5", "R2.1", "U4.13"],
    "USB_DP": ["J1.A6", "J1.B6", "U1.1", "U1.6", "R14.1"],
    "USB_DN": ["J1.A7", "J1.B7", "U1.3", "U1.4", "R15.1"],
    "USB_DP_MCU": ["R14.2", "U4.27", "C13.1"],
    "USB_DN_MCU": ["R15.2", "U4.26", "C14.1"],
    "U0RXD": ["U4.30", "TP9.1"],
    "U0TXD": ["U4.31", "R16.1"],
    "U0TXD_TP": ["R16.2", "TP10.1"],
    "EN_UVLO": ["U2.2", "R4.2", "R5.1"],
    "DVDT": ["U2.1", "C2.1"],
    "ILM": ["U2.7", "R3.1", "TP8.1"],
    "~{FLT}": ["U2.6", "R6.2", "TP7.1"],
    "EN": ["U4.8", "R7.2", "C11.1", "SW1.1"],
    "BOOT": ["U4.23", "R8.2", "SW2.1"],
    "BTN_ONOFF": ["U4.19", "R11.2", "SW3.1"],
    "IO8_STRAP": ["U4.22", "R9.2"],
    "IO2_STRAP": ["U4.5", "R10.2"],
    "LED_DATA_3V3": ["U4.16", "U5.2", "R12.1"],
    "LED_DATA_5V": ["U5.4", "R13.1"],
    "LED_DIN": ["R13.2", "TP6.1", "LED1.4"],
}

errors = []
for name, pins in EXPECT.items():
    found = {pin_net.get(tuple(p.split("."))) for p in pins}
    if len(found) != 1 or None in found:
        errors.append(f"{name}: pins spread over nets {sorted(map(str, found))}")

# LED data chain: LEDn.DOUT(2) must share a net with LED(n+1).DIN(4) and nothing else.
for n in range(1, 107):
    a, b = pin_net.get((f"LED{n}", "2")), pin_net.get((f"LED{n + 1}", "4"))
    if a is None or a != b or len(nets[a]) != 2:
        errors.append(f"chain break LED{n}->LED{n + 1}: {a} / {b}")
if pin_net.get(("LED107", "2")) and len(nets[pin_net[("LED107", "2")]]) > 1:
    errors.append("LED107 DOUT should be unconnected")

# every LED and per-LED capacitor on +5V/GND
for n in range(1, 108):
    if pin_net.get((f"LED{n}", "1")) != "+5V" or pin_net.get((f"LED{n}", "3")) != "GND":
        errors.append(f"LED{n} power pins wrong")
for n in range(101, 208):
    if pin_net.get((f"C{n}", "1")) != "+5V" or pin_net.get((f"C{n}", "2")) != "GND":
        errors.append(f"C{n} not across +5V/GND")

# single-pin nets (dangling) other than intentional no-connects
for name, nodes in nets.items():
    if len(nodes) < 2 and not name.startswith("unconnected-"):
        errors.append(f"single-pin net {name}: {sorted(nodes)}")

refs = {r for r, _ in pin_net}
print(f"{len(nets)} nets, {len(refs)} parts")
for key in ["VBUS", "+5V", "+3V3", "GND"]:
    print(f"  {key}: {len(nets[key])} pins")
if errors:
    print("PROBLEMS:")
    for e in errors:
        print("  " + e)
    sys.exit(1)
print("All expected connections present; LED chain LED1..LED107 continuous.")