blob: 911c30fc4a1a54a82949babe84ebe739a281bfda [file] [log] [blame]
""" 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
import copy
from utils import util, lib
from utils.xjson import extract_numbers
def get_tile_grid_info(fname):
with open(fname, 'r') as f:
tile = json5.load(f)
tile_type = tile['type']
return {
tile['tile']: {
'type': tile_type,
'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 edge_overlap(low1, high1, low2, high2):
""" Returns true if two lines have >0 overlap
>>> edge_overlap(0, 1, 1, 2)
False
>>> edge_overlap(0, 2, 1, 2)
True
>>> edge_overlap(1, 2, 1, 2)
True
>>> edge_overlap(1, 2, 0, 1)
False
>>> edge_overlap(1, 2, 0, 2)
True
>>> edge_overlap(0, 1, 0, 1)
True
"""
if low1 < low2:
return low2 < high1
else:
return low1 < high2
def box_share_edge(box1, box2):
""" Return true if the two boxes share any edge.
>>> box_share_edge(((0, 1), (0, 1)), ((0, 1), (1, 2)))
True
>>> box_share_edge(((0, 1), (0, 1)), ((1, 2), (1, 2)))
False
>>> box_share_edge(((0, 1), (0, 3)), ((1, 2), (2, 5)))
True
>>> box_share_edge(((0, 1), (0, 3)), ((1, 2), (3, 6)))
False
>>> box_share_edge(((0, 3), (0, 1)), ((2, 5), (1, 2)))
True
>>> box_share_edge(((0, 3), (0, 1)), ((3, 6), (1, 2)))
False
>>> box_share_edge(((0, 3), (0, 3)), ((0, 3), (3, 6)))
True
>>> box_share_edge(((0, 3), (0, 3)), ((3, 6), (0, 3)))
True
>>> box_share_edge(((0, 3), (0, 3)), ((3, 6), (3, 6)))
False
"""
((box1_xlow, box1_xhigh), (box1_ylow, box1_yhigh)) = box1
((box2_xlow, box2_xhigh), (box2_ylow, box2_yhigh)) = box2
if box1_xlow == box2_xhigh or box2_xlow == box1_xhigh:
# box 1 left edge may touch box 2 right edge
# or
# box 2 left edge may touch box 1 right edge
if edge_overlap(box1_ylow, box1_yhigh, box2_ylow, box2_yhigh):
return True
if box1_ylow == box2_yhigh or box2_ylow == box1_yhigh:
# box 1 bottom edge may touch box 1 top edge
# or
# box 2 bottom edge may touch box 2 top edge
if edge_overlap(box1_xlow, box1_xhigh, box2_xlow, box2_xhigh):
return True
return False
def tiles_are_adjcent(tile1, tile2, tile_type_sizes):
width1, height1 = tile_type_sizes[tile1['type']]
tile1_xlow = tile1['grid_x']
tile1_ylow = tile1['grid_y'] - height1
tile1_xhigh = tile1['grid_x'] + width1
tile1_yhigh = tile1['grid_y']
width2, height2 = tile_type_sizes[tile2['type']]
tile2_xlow = tile2['grid_x']
tile2_ylow = tile2['grid_y'] - height2
tile2_xhigh = tile2['grid_x'] + width2
tile2_yhigh = tile2['grid_y']
return box_share_edge(
((tile1_xlow, tile1_xhigh), (tile1_ylow, tile1_yhigh)),
((tile2_xlow, tile2_xhigh), (tile2_ylow, tile2_yhigh)),
)
def process_node(tileconn, key_history, node, wire_map, node_tree, tiles, tile_type_sizes):
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]
tile1 = tiles[wire_info1['tile']]
tile2 = tiles[wire_info2['tile']]
if not tiles_are_adjcent(tile1, tile2, tile_type_sizes):
continue
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, tile_type_sizes):
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, tile_type_sizes)
bar.update(idx + 1)
tileconn = flatten_tile_conn(tileconn)
return tileconn, raw_node_data
def max_size_for_tile(tile, grid, tile_by_loc, rclk_rows):
""" Guess maximum size for a tile. """
tile_type = grid[tile]['type']
if tile_type== 'NULL':
return (1, 1)
# Pos X, Neg Y
base_grid_x = grid[tile]['grid_x']
base_grid_y = grid[tile]['grid_y']
# Walk up X
grid_x = base_grid_x
grid_y = base_grid_y
while True:
try:
next_tile = tile_by_loc[(grid_x+1, grid_y)]
except KeyError:
break
if grid[next_tile]['type'] != 'NULL':
break
grid_x += 1
max_grid_x = grid_x
# Walk down Y
grid_x = base_grid_x
grid_y = base_grid_y
while True:
# Most tiles don't control the RCLK row, but a handful of tiles do!
if grid_y-1 in rclk_rows and tile_type not in [
"CFRM_AMS_CFGIO",
"PSS_ALTO",
"CFG_CONFIG",
]:
break
try:
next_tile = tile_by_loc[(grid_x, grid_y-1)]
except KeyError:
break
if grid[next_tile]['type'] != 'NULL':
break
grid_y -= 1
max_grid_y = grid_y
return (max_grid_x-base_grid_x+1), (base_grid_y-max_grid_y+1)
def generate_tile_type_sizes(grid):
""" Generate tile sizes for the given grid using max_size_for_tile. """
tile_type_sizes = {}
tile_by_loc = {}
rclk_rows = set()
for tile, gridinfo in grid.items():
key = gridinfo['grid_x'], gridinfo['grid_y']
assert key not in tile_by_loc
tile_by_loc[key] = tile
if gridinfo['type'] in ['RCLK_INT_R', 'RCLK_INT_L']:
rclk_rows.add(gridinfo['grid_y'])
for tile, gridinfo in grid.items():
size_x, size_y = max_size_for_tile(tile, grid, tile_by_loc, rclk_rows)
if gridinfo['type'] not in tile_type_sizes:
tile_type_sizes[gridinfo['type']] = (size_x, size_y)
else:
old_size_x, old_size_y = tile_type_sizes[gridinfo['type']]
tile_type_sizes[gridinfo['type']] = (
min(size_x, old_size_x),
min(size_y, old_size_y),
)
return tile_type_sizes
def commit_tile_type(grid, tile_type, size):
""" First verify that expanding the specified tile type by the size
specified works, then modify the tile grid to reflect the expanded sizes.
"""
gridinfo_by_loc = {}
locs = []
for tile in grid:
gridinfo = grid[tile]
key = gridinfo['grid_x'], gridinfo['grid_y']
assert key not in gridinfo_by_loc
gridinfo_by_loc[key] = gridinfo
if gridinfo['type'] == tile_type:
locs.append(key)
for loc in locs:
for dx in range(size[0]):
for dy in range(size[1]):
key = loc[0] + dx, loc[1] - dy
if dx == 0 and dy == 0:
assert gridinfo_by_loc[key]['type'] == tile_type
else:
assert gridinfo_by_loc[key]['type'] == 'NULL', (
loc, key, tile_type, gridinfo_by_loc[key]['type'])
for loc in locs:
for dx in range(size[0]):
for dy in range(size[1]):
gridinfo_by_loc[key]['type'] = 'expanded_' + tile_type
def guess_tile_type_sizes(grid):
""" Guess tile type sizes by expanding, and then blocking by largest."""
grid = copy.deepcopy(grid)
tile_type_sizes = generate_tile_type_sizes(grid)
commited_types = set()
# Commit longest or widest tiles first
tile_type_by_size = sorted(tile_type_sizes, key=lambda k: max(tile_type_sizes[k][0],tile_type_sizes[k][1]), reverse=True)
for tile_type in tile_type_by_size:
if tile_type_sizes[tile_type] == (1, 1):
continue
recompute_sizes = False
try:
# Attempt to commit this tile type
commit_tile_type(grid, tile_type, tile_type_sizes[tile_type])
except AssertionError:
# Commit failed, try to recompute and then re-commit.
recompute_sizes = True
if recompute_sizes:
new_tile_type_sizes = generate_tile_type_sizes(grid)
for tile_type in new_tile_type_sizes:
if tile_type in commited_types:
continue
if tile_type.startswith('expanded_'):
continue
tile_type_sizes[tile_type] = new_tile_type_sizes[tile_type]
commit_tile_type(grid, tile_type, tile_type_sizes[tile_type])
commited_types.add(tile_type)
return tile_type_sizes
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_part(),
'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]:
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)
tile_type_sizes = guess_tile_type_sizes(grid)
print('{} Creating tile connections'.format(datetime.datetime.now()))
tileconn, raw_node_data = generate_tileconn(pool, node_tree, nodes,
wire_map, grid, tile_type_sizes)
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()