aboutsummaryrefslogtreecommitdiff
path: root/kicad/lib/tools/derive_symbol.py
diff options
context:
space:
mode:
Diffstat (limited to 'kicad/lib/tools/derive_symbol.py')
-rwxr-xr-xkicad/lib/tools/derive_symbol.py74
1 files changed, 74 insertions, 0 deletions
diff --git a/kicad/lib/tools/derive_symbol.py b/kicad/lib/tools/derive_symbol.py
new file mode 100755
index 0000000..1278128
--- /dev/null
+++ b/kicad/lib/tools/derive_symbol.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-3.0-or-later
+"""Add a pin-compatible variant of a symbol to a KiCad symbol library.
+
+The variant keeps the geometry of the base symbol (every pin number at the
+same position), so "Change symbol" in the schematic swaps the two without
+breaking wires. Pin names and the description come from the variant's kipart
+CSV (as written by lattice2kipart.py).
+
+ derive_symbol.py LIB.kicad_sym BASE_NAME VARIANT.csv > OUT.kicad_sym
+"""
+import csv
+import re
+import sys
+
+
+def match_paren(t, i):
+ depth = 0
+ j = i
+ while True:
+ c = t[j]
+ if c == '"':
+ j += 1
+ while t[j] != '"':
+ j += 2 if t[j] == "\\" else 1
+ elif c == "(":
+ depth += 1
+ elif c == ")":
+ depth -= 1
+ if depth == 0:
+ return j + 1
+ j += 1
+
+
+lib_path, base, var_csv = sys.argv[1:4]
+lib = open(lib_path).read()
+
+rows = list(csv.reader(open(var_csv, newline="")))
+variant = rows[0][0]
+props = {r[0][:-1].replace("*", ""): r[1] for r in rows[1:] if r and r[0].endswith(":")}
+hdr = next(i for i, r in enumerate(rows) if r and r[0] == "pin")
+cols = rows[hdr]
+pins = {r[cols.index("pin")]: (r[cols.index("name")], r[cols.index("type")])
+ for r in rows[hdr + 1:] if r and r[0]}
+
+m = re.search(r'\(symbol "' + re.escape(base) + r'"', lib)
+start = m.start()
+block = lib[start:match_paren(lib, start)]
+base_types = {n: t for t, n in re.findall(r'\(pin (\w+) \w+ .*?\(number "([^"]+)"', block, re.S)}
+
+new = block.replace(f'(symbol "{base}"', f'(symbol "{variant}"', 1)
+new = new.replace(f'(symbol "{base}_', f'(symbol "{variant}_')
+new = re.sub(r'(\(property "Value" )"[^"]*"', rf'\1"{variant}"', new, count=1)
+for key in ("Description", "Source"):
+ if key in props:
+ new = re.sub(r'(\(property "' + key + r'" )"[^"]*"', lambda mm: f'{mm.group(1)}"{props[key]}"',
+ new, count=1)
+
+
+def rename(mm):
+ num = mm.group(3)
+ name, typ = pins[num]
+ if typ != base_types[num] and not (base_types[num] == "passive"): # stacked pins are passive
+ sys.exit(f"pin {num}: type {typ} differs from {base_types[num]}")
+ return f'{mm.group(1)}(name "{name}"{mm.group(2)}(number "{num}"'
+
+
+new, n = re.subn(r'(\(pin [^\n]*\n(?:(?!\(pin ).)*?)\(name "[^"]*"(.*?)\(number "([^"]+)"',
+ rename, new, flags=re.S)
+if n != len(pins):
+ sys.exit(f"renamed {n} pins, variant has {len(pins)}")
+
+end = match_paren(lib, start)
+sys.stdout.write(lib[:end] + "\n " + new + lib[end:])