From 5129a5a5448012a53cb9ddae1349c2a2c8571339 Mon Sep 17 00:00:00 2001 From: Friedrich Beckmann Date: Sun, 8 Nov 2026 18:14:54 +0100 Subject: Add KiCad 9 schematic, project libraries and datasheets 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). --- kicad/lib/tools/lattice2kipart.py | 107 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100755 kicad/lib/tools/lattice2kipart.py (limited to 'kicad/lib/tools/lattice2kipart.py') 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() -- cgit v1.2.3