| """ |
| Function to get the routed path of a net out of the ncl file |
| """ |
| |
| import re |
| import sys |
| |
| |
| def slice_pin_to_net(site, pin): |
| if is_slice(site): |
| rc = site[:-1] |
| slice = site[-1] |
| assert slice in "ABCD" |
| slicen = "ABCD".index(slice) |
| if pin in ("A0", "A1", "B0", "B1", "C0", "C1", "D0", "D1", "DI0", "DI1", "M0", "M1", "F0", "F1", "Q0", "Q1"): |
| pin_idx = int(pin[-1]) |
| pin_name = pin[:-1] |
| return "{}_{}{}_SLICE".format(rc, pin_name, 2 * slicen + pin_idx) |
| elif pin in ("CLK", "CE", "LSR", "WCK", "WRE"): |
| return "{}_{}{}_SLICE".format(rc, pin, slicen) |
| elif pin == "FCI": |
| if slice == "A": |
| return "{}_FCI_SLICE".format(rc) |
| else: |
| return "{}_FCI{}_SLICE".format(rc, slice) |
| elif pin == "FCO": |
| if slice == "D": |
| return "{}_FCO_SLICE".format(rc) |
| else: |
| return "{}_FCO{}_SLICE".format(rc, slice) |
| elif pin in ("FXA", "FXB") or pin.startswith("WAD") or pin.startswith("WD"): |
| return "{}_{}{}_SLICE".format(rc, pin, slice) |
| elif pin == "OFX0": |
| return "{}_F5{}_SLICE".format(rc, pin, slice) |
| elif pin == "OFX1": |
| return "{}_FX{}_SLICE".format(rc, pin, slice) |
| else: |
| assert False, "unhandled pin {} on slice {}".format(pin, slice) |
| elif is_ebr(site): |
| rc = site.split("_")[1] |
| return "{}_J{}_EBR".format(rc, pin) |
| # TODO: Add other bel types? |
| |
| |
| def is_slice(site): |
| return re.match(r'R\d+C\d+[ABCD]', site) |
| |
| def is_ebr(site): |
| return re.match(r'EBR_R\d+C\d+', site) |
| |
| |
| # Convert a parsed net to dict form (sink -> source) |
| def net_to_dict(signal, bels): |
| drivers, loads, route = signal |
| has_slice_driver = False |
| has_slice_load = False |
| route_map = {} |
| for driver in drivers: |
| if is_slice(bels[driver[0]]) or is_ebr(bels[driver[0]]): |
| has_slice_driver = True |
| route_map[slice_pin_to_net(bels[driver[0]], driver[1])] = driver |
| if not has_slice_driver: |
| return None |
| for load in loads: |
| if is_slice(bels[load[0]]) or is_ebr(bels[load[0]]): |
| has_slice_load = True |
| route_map[load] = slice_pin_to_net(bels[load[0]], load[1]) |
| if not has_slice_load: |
| return None |
| for arc in route: |
| route_map[arc[1]] = arc[0] |
| return route_map |
| |
| |
| # Print the route for a given net for a given source/sink pair |
| def print_route(net_dict, driver, load): |
| route = [] |
| cursor = load |
| route.append(cursor) |
| while cursor in net_dict: |
| cursor = net_dict[cursor] |
| route.append(cursor) |
| if cursor != driver: |
| return |
| route[0] = ".".join(route[0]) |
| route[-1] = ".".join(route[-1]) |
| print("\t" + " -> ".join(reversed(route))) |
| |
| |
| # Not a full parser, just for standard Diamond NCLs |
| def parse_ncl(filename): |
| signals = {} # name -> (drivers, loads, route) |
| bels = {} |
| with open(filename, 'r') as f: |
| def discard_block(): |
| while True: |
| line = f.readline().strip() |
| if "{" in line: |
| discard_block() |
| elif "}" in line: |
| return |
| |
| def process_comp(name): |
| while True: |
| line = f.readline().strip() |
| if "{" in line: |
| discard_block() |
| elif "}" in line: |
| return |
| if line.startswith("site"): |
| bels[name] = line.split(' ', 1)[1].replace(";", "").strip() |
| |
| def process_signal(name): |
| drivers = [] |
| loads = [] |
| route = [] |
| |
| def process_pins(): |
| is_drv = True |
| while True: |
| line = f.readline().strip() |
| if line.endswith("\","): |
| line += f.readline().strip() |
| if "// loads" in line: |
| is_drv = False |
| elif "// drivers" in line: |
| continue |
| else: |
| m = re.match(r'\("?(?P<bel>[^,"]+)"?,\s*"?(?P<pin>[^,"]+)"?\)', line) |
| assert m, line |
| pin = (m.group("bel"), m.group("pin")) |
| if is_drv: |
| drivers.append(pin) |
| else: |
| loads.append(pin) |
| if ";" in line: |
| return |
| |
| def process_route(): |
| while True: |
| line = f.readline().strip() |
| m = re.match(r'(?P<a>[A-Za-z0-9_]+)\.(?P<b>[A-Za-z0-9_]+)', line) |
| assert m |
| route.append((m.group("a"), m.group("b"))) |
| if ";" in line: |
| return |
| |
| while True: |
| line = f.readline().strip() |
| if "{" in line: |
| discard_block() |
| elif "}" in line: |
| signals[name] = (drivers, loads, route) |
| return |
| elif "signal-pins" in line: |
| process_pins() |
| elif "route" in line: |
| process_route() |
| |
| while True: |
| line = f.readline() |
| if line == "": |
| break |
| line = line.strip() |
| if line.startswith("comp"): |
| while "{" not in f.readline().strip(): |
| continue |
| process_comp(line.split(' ', 1)[1].strip().replace('"', '')) |
| elif line.startswith("signal"): |
| assert "{" in f.readline().strip() |
| process_signal(line.split(' ', 1)[1].strip().replace('"', '')) |
| elif line.startswith("device") or line.startswith("property"): |
| assert "{" in f.readline().strip() |
| discard_block() |
| else: |
| continue |
| return signals, bels |
| |
| |
| def main(): |
| signals, bels = parse_ncl(sys.argv[1]) |
| for name, sig in sorted(signals.items()): |
| nd = net_to_dict(sig, bels) |
| drivers, loads, route = sig |
| if nd is not None: |
| print("Signal {}:".format(name)) |
| for load in loads: |
| print_route(nd, drivers[0], load) |
| print() |
| |
| |
| if __name__ == "__main__": |
| main() |