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