aboutsummaryrefslogtreecommitdiff
path: root/kicad/lib/tools/lattice2kipart.py
diff options
context:
space:
mode:
Diffstat (limited to 'kicad/lib/tools/lattice2kipart.py')
-rwxr-xr-xkicad/lib/tools/lattice2kipart.py107
1 files changed, 107 insertions, 0 deletions
diff --git a/kicad/lib/tools/lattice2kipart.py b/kicad/lib/tools/lattice2kipart.py
new file mode 100755
index 0000000..3e10074
--- /dev/null
+++ b/kicad/lib/tools/lattice2kipart.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-3.0-or-later
+"""Convert a Lattice ECP5 pinout CSV into a kipart input CSV.
+
+Usage:
+ lattice2kipart.py PINOUT.csv PACKAGE PARTNAME FOOTPRINT LUTS > part.csv
+ kipart -b -o out.kicad_sym part.csv
+
+One unit per I/O bank, one unit for configuration/JTAG, one unit for
+core/aux power and ground. Dual functions are appended to the pin name
+(e.g. PL32A/PCLKT7_1) so clock and config pins are visible in the schematic.
+"""
+import csv
+import os
+import re
+import sys
+
+IO_RE = re.compile(r"P[TBLR]\d+[A-D]$")
+
+# Dedicated pins: name -> (kicad name, type, side, unit)
+DEDICATED = {
+ "PROGRAMN": ("~{PROGRAMN}", "input", "left", "Config"),
+ "INITN": ("~{INITN}", "open_collector", "left", "Config"),
+ "DONE": ("DONE", "open_collector", "left", "Config"),
+ "CCLK": ("CCLK", "bidirectional", "left", "Config"),
+ "CFG_0": ("CFG_0", "input", "left", "Config"),
+ "CFG_1": ("CFG_1", "input", "left", "Config"),
+ "CFG_2": ("CFG_2", "input", "left", "Config"),
+ "TCK": ("TCK", "input", "right", "Config"),
+ "TMS": ("TMS", "input", "right", "Config"),
+ "TDI": ("TDI", "input", "right", "Config"),
+ "TDO": ("TDO", "output", "right", "Config"),
+ "VCC": ("VCC", "power_in", "top", "Power"),
+ "VCCAUX": ("VCCAUX", "power_in", "top", "Power"),
+ "GND": ("GND", "power_in", "bottom", "Power"),
+}
+
+
+def main():
+ src, package, part, footprint, luts = sys.argv[1:6]
+ rows = [r for r in csv.reader(open(src, newline=""))
+ if r and any(c.strip() for c in r) and not r[0].startswith("#")]
+ header = rows[0]
+ pins = [dict(zip(header, r)) for r in rows[1:]]
+ if package not in header:
+ sys.exit(f"package {package} not in {header}")
+ pins = [p for p in pins if p[package].strip() not in ("", "-")]
+
+ # Split each bank's IOs: first half left side, second half right side.
+ banks = {}
+ for p in pins:
+ if IO_RE.match(p["Pin/Ball Function"]):
+ banks.setdefault(p["Bank"], []).append(p)
+ side_of = {}
+ for b, lst in banks.items():
+ half = (len(lst) + 1) // 2
+ for i, p in enumerate(lst):
+ side_of[id(p)] = "left" if i < half else "right"
+
+ out = csv.writer(sys.stdout)
+ out.writerow([part])
+ out.writerow(["Footprint:", footprint])
+ out.writerow(["Datasheet:",
+ "https://www.latticesemi.com/view_document?document_id=50461"])
+ out.writerow(["Description:",
+ f"ECP5 FPGA, {luts} LUTs, 1.1V core, {package}"])
+ out.writerow(["Source*:", f"Lattice pinout CSV {os.path.basename(src)}, package {package}"])
+ out.writerow(["Keywords:", "FPGA programmable logic Lattice ECP5"])
+ out.writerow(["Filters:", "BGA*256*14.0x14.0mm*P0.8mm*"])
+ out.writerow(["pin", "name", "type", "side", "unit"])
+
+ rows_out = []
+ seen = set()
+ for p in pins:
+ ball = p[package].strip()
+ if ball in seen:
+ sys.exit(f"duplicate ball {ball}")
+ seen.add(ball)
+ func = p["Pin/Ball Function"].strip()
+ dual = p["Dual Function"].strip()
+ bank = p["Bank"].strip()
+ if IO_RE.match(func):
+ name = func if dual in ("", "-") else f"{func}/{dual}"
+ rows_out.append([ball, name, "bidirectional", side_of[id(p)],
+ f"Bank{bank}"])
+ elif func.startswith("VCCIO"):
+ rows_out.append([ball, func, "power_in", "top", f"Bank{bank}"])
+ elif func in DEDICATED:
+ name, typ, side, unit = DEDICATED[func]
+ if func == "CCLK" and dual not in ("", "-"):
+ name = f"CCLK/{dual}"
+ rows_out.append([ball, name, typ, side, unit])
+ else:
+ sys.exit(f"unhandled pin {func} ({ball})")
+
+ # kipart numbers units in order of first appearance: banks ascending,
+ # then Config, then Power. Pin order inside a unit stays as in the CSV.
+ def unit_key(unit):
+ if unit.startswith("Bank"):
+ return (0, int(unit[4:]))
+ return (1, ["Config", "Power"].index(unit))
+ rows_out.sort(key=lambda r: unit_key(r[4]))
+ out.writerows(rows_out)
+
+
+if __name__ == "__main__":
+ main()