| #!/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 argparse |
| import datetime |
| import progressbar |
| import os.path |
| import prjxray.lib |
| import pickle |
| import collections |
| import json |
| |
| |
| def build_node_index(fname): |
| node_index = {} |
| with open(fname, 'rb') as f: |
| f.seek(0, 2) |
| bytes = f.tell() |
| f.seek(0, 0) |
| with progressbar.ProgressBar(max_value=bytes) as bar: |
| end_of_line = 0 |
| for l in f: |
| parts = l.decode('utf8').split(' ') |
| pip, node = parts[0:2] |
| |
| if node not in node_index: |
| node_index[node] = [] |
| |
| node_index[node].append(end_of_line) |
| end_of_line = f.tell() |
| bar.update(end_of_line) |
| |
| return node_index |
| |
| |
| def read_node(expected_node, wire_file, node_index): |
| with open(wire_file, 'rb') as f: |
| for index in node_index: |
| f.seek(index, 0) |
| |
| parts = f.readline().decode('utf8').strip().split(' ') |
| |
| pip, node = parts[0:2] |
| wires = parts[2:] |
| |
| assert node == expected_node, repr((node, expected_node, index)) |
| |
| yield wires |
| |
| |
| def generate_edges(graph, root, graph_nodes): |
| """ Starting from root, generate an edge in dir and insert into graph. |
| |
| If the tree forks, simply insert a joins to indicate the split. |
| |
| """ |
| edge = [root] |
| prev_root = None |
| |
| while True: |
| outbound_edges = graph_nodes[root] |
| outbound_edges -= set((prev_root, )) |
| if len(outbound_edges) > 1: |
| graph['edges'].append(edge) |
| if root not in graph['joins']: |
| graph['joins'][root] = set() |
| graph['joins'][root] |= outbound_edges |
| |
| for element in graph_nodes[root]: |
| if element not in graph['joins']: |
| graph['joins'][element] = set() |
| graph['joins'][element].add(root) |
| |
| break |
| else: |
| if len(outbound_edges) == 0: |
| graph['edges'].append(edge) |
| break |
| |
| next_root = tuple(outbound_edges)[0] |
| edge.append(next_root) |
| prev_root, root = root, next_root |
| |
| |
| def create_ordered_wires_for_node(node, wires_in_node, downhill, uphill): |
| if len(wires_in_node) <= 2: |
| return {'edges': [wires_in_node], 'joins': {}} |
| |
| downhill = set(tuple(l) for l in downhill) |
| uphill = set(tuple(l) for l in uphill) |
| |
| roots = set() |
| all_wires = set() |
| |
| for wire in downhill: |
| if len(wire) > 0: |
| roots |= set((wire[0], wire[-1])) |
| all_wires |= set(wire) |
| |
| for wire in uphill: |
| if len(wire) > 0: |
| roots |= set((wire[0], wire[-1])) |
| all_wires |= set(wire) |
| |
| assert len(wires_in_node) >= len(all_wires), ( |
| len(wires_in_node), len(all_wires)) |
| |
| if len(all_wires) <= 2: |
| return {'edges': tuple(all_wires), 'joins': {}} |
| |
| graph_nodes = dict((wire, set()) for wire in all_wires) |
| |
| for wire in all_wires: |
| for down in downhill: |
| try: |
| idx = down.index(wire) |
| if idx + 1 < len(down): |
| graph_nodes[wire].add(down[idx + 1]) |
| if idx - 1 >= 0: |
| graph_nodes[wire].add(down[idx - 1]) |
| except ValueError: |
| continue |
| |
| for up in uphill: |
| try: |
| idx = up.index(wire) |
| if idx + 1 < len(up): |
| graph_nodes[wire].add(up[idx + 1]) |
| if idx - 1 >= 0: |
| graph_nodes[wire].add(up[idx - 1]) |
| except ValueError: |
| continue |
| |
| graph = {'edges': [], 'joins': {}} |
| |
| while len(roots) > 0: |
| root = roots.pop() |
| |
| if len(graph_nodes[root]) > 0: |
| generate_edges(graph, root, graph_nodes) |
| |
| # Dedup identical edges. |
| final_edges = set() |
| |
| for edge in graph['edges']: |
| edge1 = tuple(edge) |
| edge2 = tuple(edge[::-1]) |
| |
| if edge1 > edge2: |
| final_edges.add((edge2, edge1)) |
| else: |
| final_edges.add((edge1, edge2)) |
| |
| edges = [edge[0] for edge in final_edges] |
| |
| element_index = {} |
| for edge in edges: |
| for idx, element in enumerate(edge): |
| if element not in element_index: |
| element_index[element] = [] |
| element_index[element].append((idx, edge)) |
| |
| new_edges = [] |
| for edge in edges: |
| starts = element_index[edge[0]] |
| ends = element_index[edge[-1]] |
| |
| found_any = False |
| for start in starts: |
| start_idx, other_edge = start |
| if other_edge is edge: |
| continue |
| |
| for end in ends: |
| if other_edge is not end[1]: |
| continue |
| |
| found_any = True |
| end_idx, _ = end |
| # check if the interior elements are the same. |
| if start_idx > end_idx: |
| step = -1 |
| else: |
| step = 1 |
| |
| other_edge_slice = slice( |
| start_idx, end_idx + step if end_idx + step >= 0 else None, |
| step) |
| if edge != other_edge[other_edge_slice]: |
| new_edges.append(edge) |
| |
| if not found_any: |
| new_edges.append(edge) |
| |
| output = { |
| 'edges': |
| new_edges, |
| 'joins': |
| dict((key, tuple(value)) for key, value in graph['joins'].items()), |
| 'wires': |
| wires_in_node, |
| } |
| |
| all_wires_in_output = set() |
| for edge in output['edges']: |
| all_wires_in_output |= set(edge) |
| |
| for element in output['joins']: |
| all_wires_in_output.add(element) |
| |
| return output |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description="") |
| parser.add_argument('--dump_all_root_dir', required=True) |
| parser.add_argument('--ordered_wires_root_dir', required=True) |
| parser.add_argument('--output_dir', required=True) |
| |
| args = parser.parse_args() |
| |
| downhill_wires = os.path.join( |
| args.ordered_wires_root_dir, 'downhill_wires.txt') |
| uphill_wires = os.path.join( |
| args.ordered_wires_root_dir, 'uphill_wires.txt') |
| |
| assert os.path.exists(downhill_wires) |
| assert os.path.exists(uphill_wires) |
| |
| print('{} Reading root.csv'.format(datetime.datetime.now())) |
| tiles, nodes = prjxray.lib.read_root_csv(args.dump_all_root_dir) |
| |
| print('{} Loading node<->wire mapping'.format(datetime.datetime.now())) |
| node_lookup = prjxray.lib.NodeLookup() |
| node_lookup_file = os.path.join(args.output_dir, 'nodes.pickle') |
| if os.path.exists(node_lookup_file): |
| node_lookup.load_from_file(node_lookup_file) |
| else: |
| node_lookup.load_from_root_csv(nodes) |
| node_lookup.save_to_file(node_lookup_file) |
| |
| wire_index_file = os.path.join(args.output_dir, 'wire_index.pickle') |
| if os.path.exists(wire_index_file): |
| print('{} Reading wire<->node index'.format(datetime.datetime.now())) |
| with open(wire_index_file, 'rb') as f: |
| wire_index = pickle.load(f) |
| |
| downhill_wire_node_index = wire_index['downhill'] |
| uphill_wire_node_index = wire_index['uphill'] |
| else: |
| print('{} Creating wire<->node index'.format(datetime.datetime.now())) |
| downhill_wire_node_index = build_node_index(downhill_wires) |
| uphill_wire_node_index = build_node_index(uphill_wires) |
| |
| with open(wire_index_file, 'wb') as f: |
| pickle.dump( |
| { |
| 'downhill': downhill_wire_node_index, |
| 'uphill': uphill_wire_node_index, |
| }, f) |
| |
| print('{} Creating node tree'.format(datetime.datetime.now())) |
| nodes = collections.OrderedDict() |
| for node in progressbar.progressbar(sorted(node_lookup.nodes)): |
| nodes[node] = create_ordered_wires_for_node( |
| node, tuple(wire['wire'] for wire in node_lookup.nodes[node]), |
| tuple( |
| read_node( |
| node, downhill_wires, downhill_wire_node_index[node] |
| if node in downhill_wire_node_index else [])), |
| tuple( |
| read_node( |
| node, uphill_wires, uphill_wire_node_index[node] |
| if node in uphill_wire_node_index else []))) |
| |
| print('{} Writing node tree'.format(datetime.datetime.now())) |
| with open(os.path.join(args.output_dir, 'node_tree.json'), 'w') as f: |
| json.dump(nodes, f, indent=2, sort_keys=True) |
| |
| |
| if __name__ == '__main__': |
| main() |