aboutsummaryrefslogtreecommitdiff
path: root/kicad/lib/tools/derive_symbol.py
diff options
context:
space:
mode:
authorFriedrich Beckmann <friedrich.beckmann@tha.de>2026-11-08 18:14:54 +0100
committerFriedrich Beckmann <friedrich.beckmann@tha.de>2026-11-08 18:14:54 +0100
commit5129a5a5448012a53cb9ddae1349c2a2c8571339 (patch)
tree41d60bbce7fe0de57cdd92b232682831759de1a8 /kicad/lib/tools/derive_symbol.py
parent9e4911662b81bad61183cca74b8c1905f84e60e4 (diff)
Add KiCad 9 schematic, project libraries and datasheetsHEADmaster
Schematic (kicad/), one sheet per function as on ULX3S: - power: TLV62569 bucks for +1V1 / +3V3, AP2112K LDO for +2V5 - usb: USB-C with PTC and TVS, CH347F for JTAG and UART - fpga_power, fpga_config, fpga_banks_01/23/67: ECP5 LFE5U-25F caBGA256 (pin compatible with the 45F), quad SPI flash, 50 MHz CLOCK_50, ball assignment as in PINPLAN.md - ethernet: RTL8211F gigabit PHY, HR911130A RJ45, shield via 1M || 1nF 2kV - sdram: 32 MB W9825G6KH-6 - hdmi: TMDS from the FPGA (LVCMOS33D), AC coupled, ESD protected - user_io: SW, KEY (debounced), LEDR, LEDG, HEX0-HEX3 with DE1 names - gpio: two DE1 compatible 2x20 headers (THDB-ADA on GPIO_0), Pmod A/B, ESD arrays - testpoints: bring-up pads, M3 mounting holes All parts have LCSC numbers, MPN and manufacturer for JLCPCB assembly; footprints are cross-checked against the EasyEDA data. Libraries: ECP5 symbols generated from the Lattice pinout CSVs (kipart), CH347F, RTL8211F, SM410561N symbols, HR911130A symbol and footprint from KiCad library merge requests, generator scripts in lib/tools/. Datasheets of all parts except resistors and capacitors in datasheets/ (manufacturer copyright, not covered by the project licenses).
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:])