diff options
Diffstat (limited to 'kicad/lib/tools/lattice2kipart.py')
| -rwxr-xr-x | kicad/lib/tools/lattice2kipart.py | 107 |
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() |
