| #!/usr/bin/env python3 | 
 | # -*- coding: utf-8 -*- | 
 | # | 
 | # Copyright (C) 2017-2020  The Project X-Ray Authors. | 
 | # | 
 | # Use of this source code is governed by a ISC-style | 
 | # license that can be found in the LICENSE file or at | 
 | # https://opensource.org/licenses/ISC | 
 | # | 
 | # SPDX-License-Identifier: ISC | 
 |  | 
 | import sys, os, json | 
 | import pickle | 
 |  | 
 | from prjxray.util import OpenSafeFile | 
 |  | 
 | class MergeFind: | 
 |     def __init__(self): | 
 |         self.db = dict() | 
 |  | 
 |     def merge(self, a, b): | 
 |         a = self.find(a) | 
 |         b = self.find(b) | 
 |         if a != b: | 
 |             self.db[a] = b | 
 |  | 
 |     def find(self, a): | 
 |         if a in self.db: | 
 |             c = self.find(self.db[a]) | 
 |             self.db[a] = c | 
 |             return c | 
 |         return a | 
 |  | 
 |  | 
 | def db_gen(): | 
 |     print("Reading database..") | 
 |  | 
 |     with OpenSafeFile("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"), | 
 |                                        os.getenv("XRAY_DATABASE")), "r") as f: | 
 |         tilegrid = json.load(f) | 
 |  | 
 |     with OpenSafeFile("%s/%s/tileconn.json" % (os.getenv("XRAY_DATABASE_DIR"), | 
 |                                        os.getenv("XRAY_DATABASE")), "r") as f: | 
 |         tileconn = json.load(f) | 
 |  | 
 |     type_to_tiles = dict() | 
 |     grid_to_tile = dict() | 
 |     nodes = MergeFind() | 
 |  | 
 |     for tile, tiledata in tilegrid.items(): | 
 |         if tiledata["type"] not in type_to_tiles: | 
 |             type_to_tiles[tiledata["type"]] = list() | 
 |         type_to_tiles[tiledata["type"]].append(tile) | 
 |         grid_to_tile[(tiledata["grid_x"], tiledata["grid_y"])] = tile | 
 |  | 
 |     print("Processing tileconn..") | 
 |  | 
 |     for entry in tileconn: | 
 |         type_a, type_b = entry["tile_types"] | 
 |         for tile_a in type_to_tiles[type_a]: | 
 |             tiledata_a = tilegrid[tile_a] | 
 |             grid_a = (tiledata_a["grid_x"], tiledata_a["grid_y"]) | 
 |             grid_b = ( | 
 |                 grid_a[0] + entry["grid_deltas"][0], | 
 |                 grid_a[1] + entry["grid_deltas"][1]) | 
 |  | 
 |             if grid_b not in grid_to_tile: | 
 |                 continue | 
 |  | 
 |             tile_b = grid_to_tile[grid_b] | 
 |             tiledata_b = tilegrid[tile_b] | 
 |  | 
 |             if tiledata_b["type"] != type_b: | 
 |                 continue | 
 |  | 
 |             for pair in entry["wire_pairs"]: | 
 |                 nodes.merge((tile_a, pair[0]), (tile_b, pair[1])) | 
 |  | 
 |     print("Processing PIPs..") | 
 |  | 
 |     node_node_pip = dict() | 
 |     reverse_node_node = dict() | 
 |  | 
 |     for tile_type in ["int_l", "int_r"]: | 
 |         with OpenSafeFile("%s/%s/segbits_%s.db" % | 
 |                   (os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), | 
 |                    tile_type), "r") as f: | 
 |             for line in f: | 
 |                 _, dst, src = line.split()[0].split(".") | 
 |                 for tile in type_to_tiles[tile_type.upper()]: | 
 |                     src_node = nodes.find((tile, src)) | 
 |                     dst_node = nodes.find((tile, dst)) | 
 |                     if src_node not in node_node_pip: | 
 |                         node_node_pip[src_node] = dict() | 
 |                     if dst_node not in reverse_node_node: | 
 |                         reverse_node_node[dst_node] = set() | 
 |                     node_node_pip[src_node][dst_node] = "%s.%s.%s" % ( | 
 |                         tile, dst, src) | 
 |                     reverse_node_node[dst_node].add(src_node) | 
 |  | 
 |     return type_to_tiles, grid_to_tile, nodes, node_node_pip, reverse_node_node | 
 |  | 
 |  | 
 | def db_load(): | 
 |     # Takes a while. Speed things up | 
 |     picklef = os.getenv('XRAY_DIR') + '/tools/simpleroute.p' | 
 |     if os.path.exists(picklef): | 
 |         #print('Pickle: load') | 
 |         db_res = pickle.load(open(picklef, 'rb')) | 
 |     else: | 
 |         #print('Pickle: rebuilding') | 
 |         db_res = db_gen() | 
 |         #print('Pickle: save') | 
 |         pickle.dump(db_res, open(picklef, 'wb')) | 
 |     return db_res | 
 |  | 
 |  | 
 | def route(args): | 
 |     type_to_tiles, grid_to_tile, nodes, node_node_pip, reverse_node_node = db_load( | 
 |     ) | 
 |  | 
 |     active_pips = set() | 
 |     blocked_nodes = set() | 
 |  | 
 |     for argidx in range((len(args)) // 2): | 
 |         src_tile, src_wire = args[2 * argidx].split("/") | 
 |         dst_tile, dst_wire = args[2 * argidx + 1].split("/") | 
 |  | 
 |         src_node = nodes.find((src_tile, src_wire)) | 
 |         dst_node = nodes.find((dst_tile, dst_wire)) | 
 |  | 
 |         print("Routing %s -> %s:" % (src_node, dst_node)) | 
 |  | 
 |         node_scores = dict() | 
 |  | 
 |         def write_scores(nodes, count): | 
 |             next_nodes = set() | 
 |             for n in nodes: | 
 |                 if n in node_scores: | 
 |                     continue | 
 |                 node_scores[n] = count | 
 |                 if n == src_node: | 
 |                     return | 
 |                 if n in reverse_node_node: | 
 |                     for nn in reverse_node_node[n]: | 
 |                         if nn not in node_scores and nn not in blocked_nodes: | 
 |                             next_nodes.add(nn) | 
 |             write_scores(next_nodes, count + 1) | 
 |  | 
 |         try: | 
 |             write_scores(set([dst_node]), 1) | 
 |         except RecursionError as e: | 
 |             raise Exception( | 
 |                 "Could not find route for node %s" % (dst_node, )) from None | 
 |         print("  route length: %d" % node_scores[src_node]) | 
 |  | 
 |         count = 0 | 
 |         c = src_node | 
 |         blocked_nodes.add(c) | 
 |         print("  %4d: %s" % (count, c)) | 
 |  | 
 |         score = node_scores[src_node] | 
 |         while c != dst_node: | 
 |             nn = None | 
 |             for n in node_node_pip[c].keys(): | 
 |                 if n in node_scores and node_scores[n] < score: | 
 |                     nn, score = n, node_scores[n] | 
 |  | 
 |             pip = node_node_pip[c][nn] | 
 |             active_pips.add(pip) | 
 |             print("        %s" % pip) | 
 |  | 
 |             count += 1 | 
 |             c = nn | 
 |             blocked_nodes.add(c) | 
 |             print("  %4d: %s" % (count, c)) | 
 |  | 
 |     print("====") | 
 |     pipnames = list() | 
 |  | 
 |     for pip in sorted(active_pips): | 
 |         tile, dst, src = pip.split(".") | 
 |         pipnames.append("%s/%s.%s->>%s" % (tile, tile[0:5], src, dst)) | 
 |  | 
 |     print( | 
 |         "highlight_objects -color orange [get_nodes -of_objects [get_wires {%s}]]" | 
 |         % " ".join(["%s/%s" % n for n in sorted(blocked_nodes)])) | 
 |     print( | 
 |         "highlight_objects -color orange [get_pips {%s}]" % " ".join(pipnames)) | 
 |  | 
 |     print("====") | 
 |     for pip in sorted(active_pips): | 
 |         print(pip) | 
 |     return active_pips | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     if len(sys.argv) == 1 or (len(sys.argv) % 2) != 1: | 
 |         print() | 
 |         print("Usage: %s src1 dst1 [src2 dst2 [...]]" % sys.argv[0]) | 
 |         print("Where entires as tile/wire") | 
 |         print() | 
 |         print( | 
 |             "Example: %s VBRK_X29Y140/VBRK_ER1BEG2 VFRAME_X47Y113/VFRAME_EL1BEG2" | 
 |             % sys.argv[0]) | 
 |         print() | 
 |         sys.exit(1) | 
 |     route(sys.argv[1:]) |