|  | #!/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 | 
|  | """ Generate grid from database dump """ | 
|  |  | 
|  | from __future__ import print_function | 
|  | import argparse | 
|  | import pyjson5 as json5 | 
|  | import multiprocessing | 
|  | import progressbar | 
|  | import os.path | 
|  | import json | 
|  | import datetime | 
|  | import pickle | 
|  | import sys | 
|  |  | 
|  | from prjxray import util, lib | 
|  | from prjxray.xjson import extract_numbers | 
|  |  | 
|  |  | 
|  | def get_tile_grid_info(fname): | 
|  | with open(fname, 'r') as f: | 
|  | tile = json5.load(f) | 
|  |  | 
|  | tile_type = tile['type'] | 
|  | ignored = int(tile['ignored']) != 0 | 
|  |  | 
|  | if ignored: | 
|  | tile_type = 'NULL' | 
|  |  | 
|  | return { | 
|  | tile['tile']: { | 
|  | 'type': tile_type, | 
|  | 'ignored': ignored, | 
|  | 'grid_x': tile['x'], | 
|  | 'grid_y': tile['y'], | 
|  | 'sites': dict( | 
|  | (site['site'], site['type']) for site in tile['sites']), | 
|  | 'wires': set((wire['wire'] for wire in tile['wires'])) | 
|  | }, | 
|  | } | 
|  |  | 
|  |  | 
|  | def read_json5(fname): | 
|  | with open(fname, 'r') as f: | 
|  | return json5.load(f) | 
|  |  | 
|  |  | 
|  | def is_edge_shared(edge1, edge2): | 
|  | """ Returns true if edge1 or edge2 overlap | 
|  |  | 
|  | >>> is_edge_shared((0, 1), (0, 1)) | 
|  | True | 
|  | >>> is_edge_shared((0, 2), (0, 1)) | 
|  | True | 
|  | >>> is_edge_shared((0, 1), (0, 2)) | 
|  | True | 
|  | >>> is_edge_shared((1, 2), (0, 3)) | 
|  | True | 
|  | >>> is_edge_shared((0, 3), (1, 2)) | 
|  | True | 
|  | >>> is_edge_shared((1, 2), (0, 2)) | 
|  | True | 
|  | >>> is_edge_shared((0, 2), (1, 2)) | 
|  | True | 
|  | >>> is_edge_shared((0, 2), (1, 3)) | 
|  | True | 
|  | >>> is_edge_shared((1, 3), (0, 2)) | 
|  | True | 
|  | >>> is_edge_shared((0, 1), (1, 2)) | 
|  | False | 
|  | >>> is_edge_shared((1, 2), (0, 1)) | 
|  | False | 
|  | >>> is_edge_shared((0, 1), (2, 3)) | 
|  | False | 
|  | >>> is_edge_shared((2, 3), (0, 1)) | 
|  | False | 
|  | """ | 
|  | assert edge1[0] < edge1[1], edge1 | 
|  | assert edge2[0] < edge2[1], edge2 | 
|  |  | 
|  | if edge1[0] <= edge2[0]: | 
|  | return edge2[0] < edge1[1] | 
|  | else: | 
|  | return edge1[0] < edge2[1] | 
|  |  | 
|  |  | 
|  | def share_edge(a, b): | 
|  | """ Returns true if box defined by a and b share any edge. | 
|  |  | 
|  | Box is defined as (x-min, y-min, x-max, y-max). | 
|  |  | 
|  | >>> share_edge((0, 0, 1, 1), (1, 0, 2, 1)) | 
|  | True | 
|  | >>> share_edge((1, 0, 2, 1), (0, 0, 1, 1)) | 
|  | True | 
|  | >>> share_edge((0, 0, 1, 1), (0, 1, 1, 2)) | 
|  | True | 
|  | >>> share_edge((0, 1, 1, 2), (0, 0, 1, 1)) | 
|  | True | 
|  | >>> share_edge((0, 0, 1, 3), (1, 0, 2, 1)) | 
|  | True | 
|  | >>> share_edge((1, 0, 2, 1), (0, 0, 1, 3)) | 
|  | True | 
|  | >>> share_edge((0, 0, 3, 1), (0, 1, 1, 2)) | 
|  | True | 
|  | >>> share_edge((0, 1, 1, 2), (0, 0, 3, 1)) | 
|  | True | 
|  | >>> share_edge((0, 0, 1, 1), (1, 1, 2, 2)) | 
|  | False | 
|  | >>> share_edge((1, 1, 2, 2), (0, 0, 1, 1)) | 
|  | False | 
|  | >>> share_edge((0, 0, 1, 3), (1, 3, 2, 4)) | 
|  | False | 
|  | >>> share_edge((0, 0, 1, 3), (1, 2, 2, 4)) | 
|  | True | 
|  | """ | 
|  |  | 
|  | a_x_min, a_y_min, a_x_max, a_y_max = a | 
|  | b_x_min, b_y_min, b_x_max, b_y_max = b | 
|  |  | 
|  | if a_x_min == b_x_max or a_x_max == b_x_min: | 
|  | return is_edge_shared((a_y_min, a_y_max), (b_y_min, b_y_max)) | 
|  | if a_y_min == b_y_max or a_y_max == b_y_min: | 
|  | return is_edge_shared((a_x_min, a_x_max), (b_x_min, b_x_max)) | 
|  |  | 
|  |  | 
|  | def next_wire_in_dimension( | 
|  | wire1, tile1, wire2, tile2, tiles, x_wires, y_wires, wire_map, | 
|  | wires_in_node): | 
|  | """ next_wire_in_dimension returns true if tile1 and tile2 are in the same | 
|  | row and column, and must be adjcent. | 
|  | """ | 
|  | tile1_info = tiles[tile1] | 
|  | tile2_info = tiles[tile2] | 
|  |  | 
|  | tile1_x = tile1_info['grid_x'] | 
|  | tile2_x = tile2_info['grid_x'] | 
|  | tile1_y = tile1_info['grid_y'] | 
|  | tile2_y = tile2_info['grid_y'] | 
|  |  | 
|  | # All wires are in the same row or column or if the each wire lies in its own | 
|  | # row or column. | 
|  | if len(y_wires) == 1 or len(x_wires) == len(wires_in_node) or abs( | 
|  | tile1_y - tile2_y) == 0: | 
|  | ordered_wires = sorted(x_wires.keys()) | 
|  |  | 
|  | idx1 = ordered_wires.index(tile1_x) | 
|  | idx2 = ordered_wires.index(tile2_x) | 
|  |  | 
|  | if len(x_wires[tile1_x]) == 1 and len(x_wires[tile2_x]) == 1: | 
|  | return abs(idx1 - idx2) == 1 | 
|  |  | 
|  | if len(x_wires) == 1 or len(y_wires) == len(wires_in_node) or abs( | 
|  | tile1_x - tile2_x) == 0: | 
|  | ordered_wires = sorted(y_wires.keys()) | 
|  |  | 
|  | idx1 = ordered_wires.index(tile1_y) | 
|  | idx2 = ordered_wires.index(tile2_y) | 
|  |  | 
|  | if len(y_wires[tile1_y]) == 1 and len(y_wires[tile2_y]) == 1: | 
|  | return abs(idx1 - idx2) == 1 | 
|  |  | 
|  | return None | 
|  |  | 
|  |  | 
|  | def only_wire(tile1, tile2, tiles, x_wires, y_wires): | 
|  | """ only_wire returns true if tile1 and tile2 only have 1 wire in their respective x or y dimension. | 
|  | """ | 
|  | tile1_info = tiles[tile1] | 
|  | tile2_info = tiles[tile2] | 
|  |  | 
|  | tile1_x = tile1_info['grid_x'] | 
|  | tile2_x = tile2_info['grid_x'] | 
|  |  | 
|  | tiles_x_adjacent = abs(tile1_x - tile2_x) == 1 | 
|  | if tiles_x_adjacent and len(x_wires[tile1_x]) == 1 and len( | 
|  | x_wires[tile2_x]) == 1: | 
|  | return True | 
|  |  | 
|  | tile1_y = tile1_info['grid_y'] | 
|  | tile2_y = tile2_info['grid_y'] | 
|  |  | 
|  | tiles_y_adjacent = abs(tile1_y - tile2_y) == 1 | 
|  | if tiles_y_adjacent and len(y_wires[tile1_y]) == 1 and len( | 
|  | y_wires[tile2_y]) == 1: | 
|  | return True | 
|  |  | 
|  | return None | 
|  |  | 
|  |  | 
|  | def is_directly_connected(node, node_tree, wire1, wire2): | 
|  | if 'wires' in node_tree: | 
|  | node_tree_wires = node_tree['wires'] | 
|  | else: | 
|  | if len(node_tree['edges']) == 1 and len(node_tree['joins']) == 0: | 
|  | node_tree_wires = node_tree['edges'][0] | 
|  | else: | 
|  | return None | 
|  |  | 
|  | if wire1 not in node_tree_wires: | 
|  | return None | 
|  | if wire2 not in node_tree_wires: | 
|  | return None | 
|  |  | 
|  | # Is there than edge that has wire1 next to wire2? | 
|  | for edge in node_tree['edges']: | 
|  | idx1 = None | 
|  | idx2 = None | 
|  | try: | 
|  | idx1 = edge.index(wire1) | 
|  | except ValueError: | 
|  | pass | 
|  |  | 
|  | try: | 
|  | idx2 = edge.index(wire2) | 
|  | except ValueError: | 
|  | pass | 
|  |  | 
|  | if idx1 is not None and idx2 is not None: | 
|  | return abs(idx1 - idx2) == 1 | 
|  |  | 
|  | if idx1 is not None and (idx1 != 0 and idx1 != len(edge) - 1): | 
|  | return False | 
|  |  | 
|  | if idx2 is not None and (idx2 != 0 and idx2 != len(edge) - 1): | 
|  | return False | 
|  |  | 
|  | # Is there a join of nodes between wire1 and wire2? | 
|  | if wire1 in node_tree['joins']: | 
|  | return wire2 in node_tree['joins'][wire1] | 
|  |  | 
|  | if wire2 in node_tree['joins']: | 
|  | assert wire1 not in node_tree['joins'][wire2] | 
|  |  | 
|  | return None | 
|  |  | 
|  |  | 
|  | def is_connected( | 
|  | wire1, tile1, wire2, tile2, node, wires_in_tiles, wire_map, node_tree, | 
|  | tiles, x_wires, y_wires, wires_in_node): | 
|  | """ Check if two wires are directly connected. """ | 
|  |  | 
|  | next_wire_in_dim = next_wire_in_dimension( | 
|  | wire1, tile1, wire2, tile2, tiles, x_wires, y_wires, wire_map, | 
|  | wires_in_node) | 
|  | if next_wire_in_dim is not None: | 
|  | return next_wire_in_dim | 
|  |  | 
|  | # Because there are multiple possible wire connections between these two | 
|  | # tiles, consult the node_tree to determine if the two wires are actually connected. | 
|  | # | 
|  | # Warning: The node_tree is incomplete because it is not know how to extract | 
|  | # ordered wire information from the node. | 
|  | # | 
|  | # Example node CLK_BUFG_REBUF_X60Y142/CLK_BUFG_REBUF_R_CK_GCLK0_BOT | 
|  | # It does not appear to be possible to get ordered wire connection information | 
|  | # for the first two wires connected to PIP | 
|  | # CLK_BUFG_REBUF_X60Y117/CLK_BUFG_REBUF.CLK_BUFG_REBUF_R_CK_GCLK0_BOT<<->>CLK_BUFG_REBUF_R_CK_GCLK0_TOP | 
|  | # | 
|  | # However, it happens to be that theses wires are the only wires in their | 
|  | # tiles, so the earlier "only wires in tile" check will pass. | 
|  |  | 
|  | connected = is_directly_connected( | 
|  | node['node'], node_tree[node['node']], wire1, wire2) | 
|  | if connected is not None: | 
|  | return connected | 
|  |  | 
|  | is_only_wire = only_wire(tile1, tile2, tiles, x_wires, y_wires) | 
|  | if is_only_wire is not None: | 
|  | return is_only_wire | 
|  |  | 
|  | # The node_tree didn't specify these wires, and the wires are not | 
|  | # unambiguously connected. | 
|  | return False | 
|  |  | 
|  |  | 
|  | def process_node(tileconn, key_history, node, wire_map, node_tree, tiles): | 
|  | wires = [wire['wire'] for wire in node['wires']] | 
|  |  | 
|  | wires_in_tiles = {} | 
|  | x_wires = {} | 
|  | y_wires = {} | 
|  | for wire in wires: | 
|  | wire_info = wire_map[wire] | 
|  |  | 
|  | if wire_info['tile'] not in wires_in_tiles: | 
|  | wires_in_tiles[wire_info['tile']] = [] | 
|  | wires_in_tiles[wire_info['tile']].append(wire) | 
|  |  | 
|  | grid_x = tiles[wire_info['tile']]['grid_x'] | 
|  | if grid_x not in x_wires: | 
|  | x_wires[grid_x] = [] | 
|  | x_wires[grid_x].append(wire) | 
|  |  | 
|  | grid_y = tiles[wire_info['tile']]['grid_y'] | 
|  | if grid_y not in y_wires: | 
|  | y_wires[grid_y] = [] | 
|  | y_wires[grid_y].append(wire) | 
|  |  | 
|  | if len(wires) == 2: | 
|  | wire1 = wires[0] | 
|  | wire_info1 = wire_map[wire1] | 
|  | wire2 = wires[1] | 
|  | wire_info2 = wire_map[wire2] | 
|  | update_tile_conn( | 
|  | tileconn, key_history, wire1, wire_info1, wire2, wire_info2, tiles) | 
|  | return | 
|  |  | 
|  | for idx, wire1 in enumerate(wires): | 
|  | wire_info1 = wire_map[wire1] | 
|  | for wire2 in wires[idx + 1:]: | 
|  | wire_info2 = wire_map[wire2] | 
|  |  | 
|  | if not is_connected(wire1, wire_info1['tile'], wire2, | 
|  | wire_info2['tile'], node, wires_in_tiles, | 
|  | wire_map, node_tree, tiles, x_wires, y_wires, | 
|  | wires): | 
|  | continue | 
|  |  | 
|  | update_tile_conn( | 
|  | tileconn, key_history, wire1, wire_info1, wire2, wire_info2, | 
|  | tiles) | 
|  |  | 
|  |  | 
|  | def update_tile_conn( | 
|  | tileconn, key_history, wirename1, wire1, wirename2, wire2, tiles): | 
|  | # Ensure that (wire1, wire2) is sorted, so we can easy check if a connection | 
|  | # already exists. | 
|  |  | 
|  | tile1 = tiles[wire1['tile']] | 
|  | tile2 = tiles[wire2['tile']] | 
|  | if ((wire1['type'], wire1['shortname'], tile1['grid_x'], tile1['grid_y']) > | 
|  | (wire2['type'], wire2['shortname'], tile2['grid_x'], tile2['grid_y'])): | 
|  | wire1, tile1, wire2, tile2 = wire2, tile2, wire1, tile1 | 
|  |  | 
|  | tileconn.append( | 
|  | { | 
|  | "grid_deltas": [ | 
|  | tile2['grid_x'] - tile1['grid_x'], | 
|  | tile2['grid_y'] - tile1['grid_y'], | 
|  | ], | 
|  | "tile_types": [ | 
|  | tile1['type'], | 
|  | tile2['type'], | 
|  | ], | 
|  | "wire_pair": [ | 
|  | wire1['shortname'], | 
|  | wire2['shortname'], | 
|  | ], | 
|  | }) | 
|  |  | 
|  |  | 
|  | def flatten_tile_conn(tileconn): | 
|  | """ Convert tileconn that is key'd to identify specific wire pairs between tiles | 
|  | key (tile1_type, wire1_name, tile2_type, wire2_name) to flat tile connect list | 
|  | that relates tile types and relative coordinates and a full list of wires to | 
|  | connect. """ | 
|  | flat_tileconn = {} | 
|  |  | 
|  | for conn in tileconn: | 
|  | key = (tuple(conn['tile_types']), tuple(conn['grid_deltas'])) | 
|  |  | 
|  | if key not in flat_tileconn: | 
|  | flat_tileconn[key] = { | 
|  | 'tile_types': conn['tile_types'], | 
|  | 'grid_deltas': conn['grid_deltas'], | 
|  | 'wire_pairs': set() | 
|  | } | 
|  |  | 
|  | flat_tileconn[key]['wire_pairs'].add(tuple(conn['wire_pair'])) | 
|  |  | 
|  | def inner(): | 
|  | for output in flat_tileconn.values(): | 
|  | yield { | 
|  | 'tile_types': output['tile_types'], | 
|  | 'grid_deltas': output['grid_deltas'], | 
|  | 'wire_pairs': tuple(output['wire_pairs']), | 
|  | } | 
|  |  | 
|  | return tuple(inner()) | 
|  |  | 
|  |  | 
|  | def is_tile_type(tiles, coord_to_tile, coord, tile_type): | 
|  | if coord not in coord_to_tile: | 
|  | return False | 
|  |  | 
|  | target_tile = tiles[coord_to_tile[coord]] | 
|  | return target_tile['type'] == tile_type | 
|  |  | 
|  |  | 
|  | def get_connections(wire, wire_info, conn, idx, coord_to_tile, tiles): | 
|  | """ Yields (tile_coord, wire) for each wire that should be connected to specified wire. """ | 
|  | pair = conn['wire_pairs'][idx] | 
|  | wire_tile_type = wire_info['type'] | 
|  | tile_types = conn['tile_types'] | 
|  | shortname = wire_info['shortname'] | 
|  | grid_deltas = conn['grid_deltas'] | 
|  |  | 
|  | wire1 = tile_types[0] == wire_tile_type and shortname == pair[0] | 
|  | wire2 = tile_types[1] == wire_tile_type and shortname == pair[1] | 
|  | assert wire1 or wire2, (wire, conn) | 
|  |  | 
|  | tile_of_wire = wire_info['tile'] | 
|  | start_coord_x = tiles[tile_of_wire]['grid_x'] | 
|  | start_coord_y = tiles[tile_of_wire]['grid_y'] | 
|  | if wire1: | 
|  | target_coord_x = start_coord_x + grid_deltas[0] | 
|  | target_coord_y = start_coord_y + grid_deltas[1] | 
|  | target_tile_type = tile_types[1] | 
|  |  | 
|  | target_wire = pair[1] | 
|  | target_tile = (target_coord_x, target_coord_y) | 
|  |  | 
|  | if is_tile_type(tiles, coord_to_tile, target_tile, target_tile_type): | 
|  | yield target_tile, target_wire | 
|  |  | 
|  | if wire2: | 
|  | target_coord_x = start_coord_x - grid_deltas[0] | 
|  | target_coord_y = start_coord_y - grid_deltas[1] | 
|  | target_tile_type = tile_types[0] | 
|  |  | 
|  | target_wire = pair[0] | 
|  | target_tile = (target_coord_x, target_coord_y) | 
|  |  | 
|  | if is_tile_type(tiles, coord_to_tile, target_tile, target_tile_type): | 
|  | yield target_tile, target_wire | 
|  |  | 
|  |  | 
|  | def make_connection(wire_nodes, wire1, wire2): | 
|  | if wire_nodes[wire1] is wire_nodes[wire2]: | 
|  | assert wire1 in wire_nodes[wire1] | 
|  | assert wire2 in wire_nodes[wire2] | 
|  | return | 
|  |  | 
|  | new_node = wire_nodes[wire1] | wire_nodes[wire2] | 
|  |  | 
|  | for wire in new_node: | 
|  | wire_nodes[wire] = new_node | 
|  |  | 
|  |  | 
|  | def create_coord_to_tile(tiles): | 
|  | coord_to_tile = {} | 
|  | for tile, tileinfo in tiles.items(): | 
|  | coord_to_tile[(tileinfo['grid_x'], tileinfo['grid_y'])] = tile | 
|  |  | 
|  | return coord_to_tile | 
|  |  | 
|  |  | 
|  | def connect_wires(tiles, tileconn, wire_map): | 
|  | """ Connect individual wires into groups of wires called nodes. """ | 
|  |  | 
|  | # Initialize all nodes to originally only contain the wire by itself. | 
|  | wire_nodes = {} | 
|  | for wire in wire_map: | 
|  | wire_nodes[wire] = set([wire]) | 
|  |  | 
|  | wire_connection_map = {} | 
|  | for conn in tileconn: | 
|  | for idx, (wire1, wire2) in enumerate(conn['wire_pairs']): | 
|  | key1 = (conn['tile_types'][0], wire1) | 
|  | if key1 not in wire_connection_map: | 
|  | wire_connection_map[key1] = [] | 
|  | wire_connection_map[key1].append((conn, idx)) | 
|  |  | 
|  | key2 = (conn['tile_types'][1], wire2) | 
|  | if key2 not in wire_connection_map: | 
|  | wire_connection_map[key2] = [] | 
|  | wire_connection_map[key2].append((conn, idx)) | 
|  |  | 
|  | coord_to_tile = create_coord_to_tile(tiles) | 
|  |  | 
|  | for wire, wire_info in progressbar.progressbar(wire_map.items()): | 
|  | key = (wire_info['type'], wire_info['shortname']) | 
|  | if key not in wire_connection_map: | 
|  | continue | 
|  |  | 
|  | for conn, idx in wire_connection_map[key]: | 
|  | for target_tile, target_wire in get_connections( | 
|  | wire, wire_info, conn, idx, coord_to_tile, tiles): | 
|  |  | 
|  | full_wire_name = coord_to_tile[target_tile] + '/' + target_wire | 
|  | assert wire_map[full_wire_name]['shortname'] == target_wire, ( | 
|  | target_tile, target_wire, wire, conn) | 
|  | assert wire_map[full_wire_name]['tile'] == coord_to_tile[ | 
|  | target_tile], ( | 
|  | wire_map[full_wire_name]['tile'], | 
|  | coord_to_tile[target_tile]) | 
|  |  | 
|  | make_connection(wire_nodes, wire, full_wire_name) | 
|  |  | 
|  | # Find unique nodes | 
|  | nodes = {} | 
|  | for node in wire_nodes.values(): | 
|  | nodes[id(node)] = node | 
|  |  | 
|  | # Flatten to list of lists. | 
|  | return tuple(tuple(node) for node in nodes.values()) | 
|  |  | 
|  |  | 
|  | def generate_tilegrid(pool, tiles): | 
|  | wire_map = {} | 
|  |  | 
|  | grid = {} | 
|  |  | 
|  | num_tiles = 0 | 
|  | for tile_type in tiles: | 
|  | num_tiles += len(tiles[tile_type]) | 
|  |  | 
|  | idx = 0 | 
|  | with progressbar.ProgressBar(max_value=num_tiles) as bar: | 
|  | for tile_type in tiles: | 
|  | for tile in pool.imap_unordered( | 
|  | get_tile_grid_info, | 
|  | tiles[tile_type], | 
|  | chunksize=20, | 
|  | ): | 
|  | bar.update(idx) | 
|  |  | 
|  | assert len(tile) == 1, tile | 
|  | tilename = tuple(tile.keys())[0] | 
|  |  | 
|  | for wire in tile[tilename]['wires']: | 
|  | assert wire not in wire_map, (wire, wire_map) | 
|  | assert wire.startswith(tilename + '/'), (wire, tilename) | 
|  |  | 
|  | wire_map[wire] = { | 
|  | 'tile': tilename, | 
|  | 'type': tile[tilename]['type'], | 
|  | 'shortname': wire[len(tilename) + 1:], | 
|  | } | 
|  |  | 
|  | del tile[tilename]['wires'] | 
|  | grid.update(tile) | 
|  |  | 
|  | idx += 1 | 
|  | bar.update(idx) | 
|  |  | 
|  | return grid, wire_map | 
|  |  | 
|  |  | 
|  | def generate_tileconn(pool, node_tree, nodes, wire_map, grid): | 
|  | tileconn = [] | 
|  | key_history = {} | 
|  | raw_node_data = [] | 
|  | with progressbar.ProgressBar(max_value=len(nodes)) as bar: | 
|  | for idx, node in enumerate(pool.imap_unordered( | 
|  | read_json5, | 
|  | nodes, | 
|  | chunksize=20, | 
|  | )): | 
|  | bar.update(idx) | 
|  | raw_node_data.append(node) | 
|  | process_node( | 
|  | tileconn, key_history, node, wire_map, node_tree, grid) | 
|  | bar.update(idx + 1) | 
|  |  | 
|  | tileconn = flatten_tile_conn(tileconn) | 
|  |  | 
|  | return tileconn, raw_node_data | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser( | 
|  | description= | 
|  | "Reduces raw database dump into prototype tiles, grid, and connections." | 
|  | ) | 
|  | parser.add_argument('--root_dir', required=True) | 
|  | parser.add_argument('--output_dir', required=True) | 
|  | parser.add_argument('--verify_only', action='store_true') | 
|  | parser.add_argument('--ignored_wires') | 
|  | parser.add_argument('--max_cpu', type=int, default=10) | 
|  |  | 
|  | args = parser.parse_args() | 
|  |  | 
|  | tiles, nodes = lib.read_root_csv(args.root_dir) | 
|  |  | 
|  | processes = min(multiprocessing.cpu_count(), args.max_cpu) | 
|  | print('{} Running {} processes'.format(datetime.datetime.now(), processes)) | 
|  | pool = multiprocessing.Pool(processes=processes) | 
|  |  | 
|  | node_tree_file = os.path.join(args.output_dir, 'node_tree.json') | 
|  |  | 
|  | tileconn_file = os.path.join(args.output_dir, 'tileconn.json') | 
|  | wire_map_file = os.path.join(args.output_dir, 'wiremap.pickle') | 
|  |  | 
|  | print('{} Reading tilegrid'.format(datetime.datetime.now())) | 
|  | with open(os.path.join(util.get_db_root(), util.get_fabric(), | 
|  | 'tilegrid.json')) as f: | 
|  | grid = json.load(f) | 
|  |  | 
|  | if not args.verify_only: | 
|  | print('{} Creating tile map'.format(datetime.datetime.now())) | 
|  | grid2, wire_map = generate_tilegrid(pool, tiles) | 
|  |  | 
|  | # Make sure tilegrid from 005-tilegrid matches tilegrid from | 
|  | # generate_tilegrid. | 
|  | db_grid_keys = set(grid.keys()) | 
|  | generated_grid_keys = set(grid2.keys()) | 
|  | assert db_grid_keys == generated_grid_keys, ( | 
|  | db_grid_keys ^ generated_grid_keys) | 
|  |  | 
|  | for tile in db_grid_keys: | 
|  | for k in grid2[tile]: | 
|  | if k == 'ignored': | 
|  | continue | 
|  |  | 
|  | if k == 'sites' and grid2[tile]['ignored']: | 
|  | continue | 
|  |  | 
|  | assert k in grid[tile], k | 
|  | assert grid[tile][k] == grid2[tile][k], ( | 
|  | tile, k, grid[tile][k], grid2[tile][k]) | 
|  |  | 
|  | with open(wire_map_file, 'wb') as f: | 
|  | pickle.dump(wire_map, f) | 
|  |  | 
|  | print('{} Reading node tree'.format(datetime.datetime.now())) | 
|  | with open(node_tree_file) as f: | 
|  | node_tree = json.load(f) | 
|  |  | 
|  | print('{} Creating tile connections'.format(datetime.datetime.now())) | 
|  | tileconn, raw_node_data = generate_tileconn( | 
|  | pool, node_tree, nodes, wire_map, grid) | 
|  |  | 
|  | for data in tileconn: | 
|  | data['wire_pairs'] = tuple( | 
|  | sorted( | 
|  | data['wire_pairs'], | 
|  | key=lambda x: tuple(extract_numbers(s) for s in x))) | 
|  |  | 
|  | tileconn = tuple( | 
|  | sorted( | 
|  | tileconn, key=lambda x: (x['tile_types'], x['grid_deltas']))) | 
|  |  | 
|  | print('{} Writing tileconn'.format(datetime.datetime.now())) | 
|  | with open(tileconn_file, 'w') as f: | 
|  | json.dump(tileconn, f, indent=2, sort_keys=True) | 
|  | else: | 
|  | with open(wire_map_file, 'rb') as f: | 
|  | wire_map = pickle.load(f) | 
|  |  | 
|  | print('{} Reading raw_node_data'.format(datetime.datetime.now())) | 
|  | raw_node_data = [] | 
|  | with progressbar.ProgressBar(max_value=len(nodes)) as bar: | 
|  | for idx, node in enumerate(pool.imap_unordered( | 
|  | read_json5, | 
|  | nodes, | 
|  | chunksize=20, | 
|  | )): | 
|  | bar.update(idx) | 
|  | raw_node_data.append(node) | 
|  | bar.update(idx + 1) | 
|  |  | 
|  | print('{} Reading tileconn'.format(datetime.datetime.now())) | 
|  | with open(tileconn_file) as f: | 
|  | tileconn = json.load(f) | 
|  |  | 
|  | wire_nodes_file = os.path.join(args.output_dir, 'wire_nodes.pickle') | 
|  | if os.path.exists(wire_nodes_file) and args.verify_only: | 
|  | with open(wire_nodes_file, 'rb') as f: | 
|  | wire_nodes = pickle.load(f) | 
|  | else: | 
|  | print( | 
|  | "{} Connecting wires to verify tileconn".format( | 
|  | datetime.datetime.now())) | 
|  | wire_nodes = connect_wires(grid, tileconn, wire_map) | 
|  | with open(wire_nodes_file, 'wb') as f: | 
|  | pickle.dump(wire_nodes, f) | 
|  |  | 
|  | print('{} Verifing tileconn'.format(datetime.datetime.now())) | 
|  | error_nodes = [] | 
|  | lib.verify_nodes( | 
|  | [ | 
|  | (node['node'], tuple(wire['wire'] | 
|  | for wire in node['wires'])) | 
|  | for node in raw_node_data | 
|  | ], wire_nodes, error_nodes) | 
|  |  | 
|  | if len(error_nodes) > 0: | 
|  | error_nodes_file = os.path.join(args.output_dir, 'error_nodes.json') | 
|  | with open(error_nodes_file, 'w') as f: | 
|  | json.dump(error_nodes, f, indent=2, sort_keys=True) | 
|  |  | 
|  | ignored_wires = [] | 
|  | ignored_wires_file = args.ignored_wires | 
|  | if os.path.exists(ignored_wires_file): | 
|  | with open(ignored_wires_file) as f: | 
|  | ignored_wires = set(l.strip() for l in f) | 
|  |  | 
|  | if not lib.check_errors(error_nodes, ignored_wires): | 
|  | print( | 
|  | '{} errors detected, see {} for details.'.format( | 
|  | len(error_nodes), error_nodes_file)) | 
|  | sys.exit(1) | 
|  | else: | 
|  | print( | 
|  | '{} errors ignored because of {}\nSee {} for details.'.format( | 
|  | len(error_nodes), ignored_wires_file, error_nodes_file)) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |