1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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()
|