80 lines
2.4 KiB
Python
Executable File
80 lines
2.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Turn KiCad netlist into a PCF file for icestorm
|
|
|
|
import argparse
|
|
import re
|
|
import shlex
|
|
from collections import OrderedDict
|
|
|
|
def sanitize_net_name(s):
|
|
s = s.replace("~", "")
|
|
s = re.sub(r"(\d+)$", r"[\1]", s)
|
|
return s
|
|
|
|
def natural_key(s):
|
|
return tuple(int(x) if x.isdigit() else x for x in re.split(r"(\d+)", s))
|
|
|
|
def extract_nets(fh, ref, filters):
|
|
nets = []
|
|
|
|
name = None
|
|
namevalid = False
|
|
|
|
for l in fh:
|
|
m = re.match(r"^\s*\(net \([^\)]*\) \(name ([^\)]+)\)", l)
|
|
if m:
|
|
name = m.group(1).strip().strip("/").lower()
|
|
namevalid = not any(name.startswith(s) for s in [
|
|
"\"net", "+", "-", "gnd", "vcc", "vdd", "vss", *filters
|
|
])
|
|
elif namevalid:
|
|
m = re.match(r"^\s*\(node \(ref ([^\)]+)\) \(pin ([^\)]+)\)", l)
|
|
if m and m.group(1) == ref:
|
|
nets.append((name, m.group(2)))
|
|
return nets
|
|
|
|
def align_buses(nets):
|
|
buses = OrderedDict()
|
|
onets = []
|
|
# First figure out what is/isn't a bus
|
|
for net in nets:
|
|
if "[" in net[0]:
|
|
busname = net[0].split("[")[0]
|
|
if busname not in buses:
|
|
buses[busname] = []
|
|
buses[busname].append(net)
|
|
else:
|
|
onets.append(net)
|
|
# Then right-justify the buses
|
|
for busname, bus in buses.items():
|
|
indices = list(int(net[0].split('[')[1].strip(']')) for net in bus)
|
|
lsb = min(indices)
|
|
for index, net in zip(indices, bus):
|
|
onets.append(("{}[{}]".format(busname, index - lsb), net[1]))
|
|
return onets
|
|
|
|
|
|
def write_pcf(fh, nets):
|
|
fh.write("# Generated with extract_ref_nets\n\n")
|
|
for name, loc in sorted(nets, key = lambda x: natural_key(x[0])):
|
|
fh.write("set_io {:<20} {}\n".format(name, loc))
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
parser.add_argument("netfile", help="Path to KiCad .net file")
|
|
parser.add_argument("ref", help="Component reference whose named nets will be extracted")
|
|
parser.add_argument("--output", "-o", help="Output file name")
|
|
parser.add_argument("--filters", "-f", help="-f \"abc xyz\": filter out nets beginning with abc or xyz")
|
|
args = parser.parse_args()
|
|
opath = "chip.pcf" if args.output is None else args.output
|
|
filters = [] if args.filters is None else shlex.split(args.filters)
|
|
with open(args.netfile) as ifile:
|
|
nets = extract_nets(ifile, args.ref, filters)
|
|
nets = map(lambda n: (sanitize_net_name(n[0]), n[1]), nets)
|
|
nets = align_buses(nets)
|
|
with open(opath, "w") as ofile:
|
|
write_pcf(ofile, nets)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|