#!/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()