def main(): parser = argparse.ArgumentParser() parser.add_argument('--db_root', help='Project X-Ray Database', required=True) parser.add_argument('--connection_database', help='Database of fabric connectivity', required=True) parser.add_argument( '--pin_assignments', help= 'Output JSON assigning pins to tile types and direction connections', required=True) args = parser.parse_args() db = prjxray.db.Database(args.db_root) edge_assignments = {} with DatabaseCache(args.connection_database, read_only=True) as conn: c = conn.cursor() edge_assignments, wires_in_tile_types = initialize_edge_assignments( db, conn) direct_connections = set() print('{} Processing direct connections.'.format(now())) handle_direction_connections(conn, direct_connections, edge_assignments) wires_not_in_channels = {} c = conn.cursor() print('{} Processing non-channel nodes.'.format(now())) for node_pkey, classification in progressbar_utils.progressbar( c.execute( """ SELECT pkey, classification FROM node WHERE classification != ?; """, (NodeClassification.CHANNEL.value, ))): reason = NodeClassification(classification) for (tile_type, wire) in yield_logical_wire_info_from_node(conn, node_pkey): key = (tile_type, wire) # Sometimes nodes in particular tile instances are disconnected, # disregard classification changes if this is the case. if reason != NodeClassification.NULL: if key not in wires_not_in_channels: wires_not_in_channels[key] = reason else: other_reason = wires_not_in_channels[key] assert reason == other_reason, (tile_type, wire, reason, other_reason) if key in wires_in_tile_types: wires_in_tile_types.remove(key) # List of nodes that are channels. channel_nodes = [] # Map of (tile, wire) to track. This will be used to find channels for pips # that come from EDGES_TO_CHANNEL. channel_wires_to_tracks = {} # Generate track models and verify that wires are either in a channel # or not in a channel. print('{} Creating models from tracks.'.format(now())) for node_pkey, track_pkey in progressbar_utils.progressbar( c.execute( """ SELECT pkey, track_pkey FROM node WHERE classification = ?; """, (NodeClassification.CHANNEL.value, ))): assert track_pkey is not None tracks_model, _ = get_track_model(conn, track_pkey) channel_nodes.append(tracks_model) channel_wires_to_tracks[track_pkey] = tracks_model for (tile_type, wire) in yield_logical_wire_info_from_node(conn, node_pkey): key = (tile_type, wire) # Make sure all wires in channels always are in channels assert key not in wires_not_in_channels if key in wires_in_tile_types: wires_in_tile_types.remove(key) # Make sure all wires appear to have been assigned. if len(wires_in_tile_types) > 0: for tile_type, wire in sorted(wires_in_tile_types): print(tile_type, wire) assert len(wires_in_tile_types) == 0 # Verify that all tracks are sane. for node in channel_nodes: node.verify_tracks() null_tile_wires = set() # Verify that all nodes that are classified as edges to channels have at # least one site, and at least one live connection to a channel. # # If no live connections from the node are present, this node should've # been marked as NULL during channel formation. print('{} Handling edges to channels.'.format(now())) handle_edges_to_channels(conn, null_tile_wires, edge_assignments, channel_wires_to_tracks) print('{} Processing edge assignments.'.format(now())) final_edge_assignments = {} for key, available_pins in progressbar_utils.progressbar( edge_assignments.items()): (tile_type, wire) = key available_pins = [pins for pins in available_pins if len(pins) > 0] if len(available_pins) == 0: if (tile_type, wire) not in null_tile_wires: # TODO: Figure out what is going on with these wires. Appear to # tile internal connections sometimes? print((tile_type, wire)) final_edge_assignments[key] = [tracks.Direction.RIGHT] continue pins = set(available_pins[0]) for p in available_pins[1:]: pins &= set(p) if len(pins) > 0: final_edge_assignments[key] = [list(pins)[0]] else: # More than 2 pins are required, final the minimal number of pins pins = set() for p in available_pins: pins |= set(p) while len(pins) > 2: pins = list(pins) prev_len = len(pins) for idx in range(len(pins)): pins_subset = list(pins) del pins_subset[idx] pins_subset = set(pins_subset) bad_subset = False for p in available_pins: if len(pins_subset & set(p)) == 0: bad_subset = True break if not bad_subset: pins = list(pins_subset) break # Failed to remove any pins, stop. if len(pins) == prev_len: break final_edge_assignments[key] = pins for key, available_pins in edge_assignments.items(): (tile_type, wire) = key pins = set(final_edge_assignments[key]) for required_pins in available_pins: if len(required_pins) == 0: continue assert len(pins & set(required_pins)) > 0, (tile_type, wire, pins, required_pins, available_pins) pin_directions = {} for key, pins in progressbar_utils.progressbar( final_edge_assignments.items()): (tile_type, wire) = key if tile_type not in pin_directions: pin_directions[tile_type] = {} pin_directions[tile_type][wire] = [pin._name_ for pin in pins] with open(args.pin_assignments, 'w') as f: json.dump( { 'pin_directions': pin_directions, 'direct_connections': [d._asdict() for d in direct_connections], }, f, indent=2) print('{} Flushing database back to file "{}"'.format( now(), args.connection_database))
def handle_edges_to_channels(conn, null_tile_wires, edge_assignments, channel_wires_to_tracks): c = conn.cursor() c.execute(""" SELECT vcc_track_pkey, gnd_track_pkey FROM constant_sources; """) vcc_track_pkey, gnd_track_pkey = c.fetchone() const_tracks = { 0: gnd_track_pkey, 1: vcc_track_pkey, } for node_pkey, classification in progressbar_utils.progressbar( c.execute( """ SELECT pkey, classification FROM node WHERE classification != ?; """, (NodeClassification.CHANNEL.value, ))): reason = NodeClassification(classification) if reason == NodeClassification.NULL: for (tile_type, wire) in yield_logical_wire_info_from_node(conn, node_pkey): null_tile_wires.add((tile_type, wire)) if reason != NodeClassification.EDGES_TO_CHANNEL: continue c2 = conn.cursor() for wire_pkey, phy_tile_pkey, tile_pkey, wire_in_tile_pkey in c2.execute( """ SELECT pkey, phy_tile_pkey, tile_pkey, wire_in_tile_pkey FROM wire WHERE node_pkey = ?; """, (node_pkey, )): c3 = conn.cursor() c3.execute(""" SELECT grid_x, grid_y FROM tile WHERE pkey = ?;""", (tile_pkey, )) (grid_x, grid_y) = c3.fetchone() c3.execute( """ SELECT name FROM tile_type WHERE pkey = ( SELECT tile_type_pkey FROM tile WHERE pkey = ? ); """, (tile_pkey, )) (tile_type, ) = c3.fetchone() wire = get_pin_name_of_wire(conn, wire_pkey) if wire is None: # This node has no site pin, don't need to assign pin direction. continue for other_phy_tile_pkey, other_wire_in_tile_pkey, pip_pkey, pip in c3.execute( """ WITH wires_from_node(wire_in_tile_pkey, phy_tile_pkey) AS ( SELECT wire_in_tile_pkey, phy_tile_pkey FROM wire WHERE node_pkey = ? AND phy_tile_pkey IS NOT NULL ), other_wires(other_phy_tile_pkey, pip_pkey, other_wire_in_tile_pkey) AS ( SELECT wires_from_node.phy_tile_pkey, undirected_pips.pip_in_tile_pkey, undirected_pips.other_wire_in_tile_pkey FROM undirected_pips INNER JOIN wires_from_node ON undirected_pips.wire_in_tile_pkey = wires_from_node.wire_in_tile_pkey) SELECT other_wires.other_phy_tile_pkey, other_wires.other_wire_in_tile_pkey, pip_in_tile.pkey, pip_in_tile.name FROM other_wires INNER JOIN pip_in_tile ON pip_in_tile.pkey == other_wires.pip_pkey WHERE pip_in_tile.is_directional = 1 AND pip_in_tile.is_pseudo = 0; """, (node_pkey, )): # Need to walk from the wire_in_tile table, to the wire table, # to the node table and get track_pkey. # other_wire_in_tile_pkey -> wire pkey -> node_pkey -> track_pkey c4 = conn.cursor() c4.execute( """ SELECT track_pkey, classification FROM node WHERE pkey = ( SELECT node_pkey FROM wire WHERE phy_tile_pkey = ? AND wire_in_tile_pkey = ? );""", (other_phy_tile_pkey, other_wire_in_tile_pkey)) result = c4.fetchone() assert result is not None, (wire_pkey, pip_pkey, tile_pkey, wire_in_tile_pkey, other_wire_in_tile_pkey) (track_pkey, classification) = result # Some pips do connect to a track at all, e.g. null node if track_pkey is None: # TODO: Handle weird connections. #other_node_class = NodeClassification(classification) #assert other_node_class == NodeClassification.NULL, ( # node_pkey, pip_pkey, pip, other_node_class) continue tracks_model = channel_wires_to_tracks[track_pkey] available_pins = set( tracks_model.get_tracks_for_wire_at_coord( (grid_x, grid_y)).keys()) edge_assignments[(tile_type, wire)].append(available_pins) for constant in yield_ties_to_wire(wire): tracks_model = channel_wires_to_tracks[ const_tracks[constant]] available_pins = set( tracks_model.get_tracks_for_wire_at_coord( (grid_x, grid_y)).keys()) edge_assignments[(tile_type, wire)].append(available_pins)
def handle_direction_connections(conn, direct_connections, edge_assignments): # Edges with mux should have one source tile and one destination_tile. # The pin from the source_tile should face the destination_tile. # # It is expected that all edges_with_mux will lies in a line (e.g. X only or # Y only). c = conn.cursor() for src_wire_pkey, dest_wire_pkey, pip_in_tile_pkey, switch_pkey in progressbar_utils.progressbar( c.execute(""" SELECT src_wire_pkey, dest_wire_pkey, pip_in_tile_pkey, switch_pkey FROM edge_with_mux;""" )): c2 = conn.cursor() # Get the node that is attached to the source. c2.execute(""" SELECT node_pkey FROM wire WHERE pkey = ?""", (src_wire_pkey, )) (src_node_pkey, ) = c2.fetchone() # Find the wire connected to the source. src_wire = list(node_to_site_pins(conn, src_node_pkey)) assert len(src_wire) == 1 source_wire_pkey, src_tile_pkey, src_wire_in_tile_pkey = src_wire[0] c2.execute( """ SELECT tile_type_pkey, grid_x, grid_y FROM tile WHERE pkey = ?""", (src_tile_pkey, )) src_tile_type_pkey, source_loc_grid_x, source_loc_grid_y = c2.fetchone( ) c2.execute(""" SELECT name FROM tile_type WHERE pkey = ?""", (src_tile_type_pkey, )) (source_tile_type, ) = c2.fetchone() source_wire = get_pin_name_of_wire(conn, source_wire_pkey) # Get the node that is attached to the sink. c2.execute(""" SELECT node_pkey FROM wire WHERE pkey = ?""", (dest_wire_pkey, )) (dest_node_pkey, ) = c2.fetchone() # Find the wire connected to the sink. dest_wire = list(node_to_site_pins(conn, dest_node_pkey)) assert len(dest_wire) == 1 destination_wire_pkey, dest_tile_pkey, dest_wire_in_tile_pkey = dest_wire[ 0] c2.execute( """ SELECT tile_type_pkey, grid_x, grid_y FROM tile WHERE pkey = ?;""", (dest_tile_pkey, )) dest_tile_type_pkey, destination_loc_grid_x, destination_loc_grid_y = c2.fetchone( ) c2.execute(""" SELECT name FROM tile_type WHERE pkey = ?""", (dest_tile_type_pkey, )) (destination_tile_type, ) = c2.fetchone() destination_wire = get_pin_name_of_wire(conn, destination_wire_pkey) c2.execute("SELECT name FROM switch WHERE pkey = ?" "", (switch_pkey, )) switch_name = c2.fetchone()[0] direct_connections.add( DirectConnection( from_pin='{}.{}'.format(source_tile_type, source_wire), to_pin='{}.{}'.format(destination_tile_type, destination_wire), switch_name=switch_name, x_offset=destination_loc_grid_x - source_loc_grid_x, y_offset=destination_loc_grid_y - source_loc_grid_y, )) if destination_loc_grid_x == source_loc_grid_x: if destination_loc_grid_y > source_loc_grid_y: source_dir = tracks.Direction.TOP destination_dir = tracks.Direction.BOTTOM else: source_dir = tracks.Direction.BOTTOM destination_dir = tracks.Direction.TOP else: if destination_loc_grid_x > source_loc_grid_x: source_dir = tracks.Direction.RIGHT destination_dir = tracks.Direction.LEFT else: source_dir = tracks.Direction.LEFT destination_dir = tracks.Direction.RIGHT edge_assignments[(source_tile_type, source_wire)].append( (source_dir, )) edge_assignments[(destination_tile_type, destination_wire)].append( (destination_dir, ))
def import_tracks(conn, alive_tracks, node_mapping, graph, default_segment_id): cur = conn.cursor() cur2 = conn.cursor() for (graph_node_pkey, track_pkey, graph_node_type, x_low, x_high, y_low, y_high, ptc, capacitance, resistance) in progressbar_utils.progressbar( cur.execute(""" SELECT pkey, track_pkey, graph_node_type, x_low, x_high, y_low, y_high, ptc, capacitance, resistance FROM graph_node WHERE track_pkey IS NOT NULL;""")): if track_pkey not in alive_tracks: continue cur2.execute( """ SELECT name FROM segment WHERE pkey = ( SELECT segment_pkey FROM track WHERE pkey = ? )""", (track_pkey, )) result = cur2.fetchone() if result is not None: segment_name = result[0] segment_id = graph.get_segment_id_from_name(segment_name) else: segment_id = default_segment_id node_type = graph2.NodeType(graph_node_type) if node_type == graph2.NodeType.CHANX: direction = 'X' x_low = max(x_low, 1) elif node_type == graph2.NodeType.CHANY: direction = 'Y' y_low = max(y_low, 1) else: assert False, node_type canonical_loc = None cur2.execute( """ SELECT grid_x, grid_y FROM phy_tile WHERE pkey = ( SELECT canon_phy_tile_pkey FROM track WHERE pkey = ? )""", (track_pkey, )) result = cur2.fetchone() if result: canonical_loc = graph2.CanonicalLoc(x=result[0], y=result[1]) track = tracks.Track( direction=direction, x_low=x_low, x_high=x_high, y_low=y_low, y_high=y_high, ) assert graph_node_pkey not in node_mapping node_mapping[graph_node_pkey] = graph.add_track( track=track, segment_id=segment_id, ptc=ptc, timing=graph2.NodeTiming( r=resistance, c=capacitance, ), canonical_loc=canonical_loc)
def create_channels(self, pad_segment, pool=None): """ Pack tracks into channels and return Channels definition for tracks.""" assert len(self.tracks) > 0 xs = [] ys = [] for track in self.tracks: track_node = self.nodes[track] xs.append(track_node.loc.x_low) xs.append(track_node.loc.x_high) ys.append(track_node.loc.y_low) ys.append(track_node.loc.y_high) x_tracks = {} y_tracks = {} for track in self.tracks: track_node = self.nodes[track] if track_node.type == NodeType.CHANX: assert track_node.loc.y_low == track_node.loc.y_high x1, x2 = sorted((track_node.loc.x_low, track_node.loc.x_high)) if track_node.loc.y_low not in x_tracks: x_tracks[track_node.loc.y_low] = [] x_tracks[track_node.loc.y_low].append((x1, x2, track)) elif track_node.type == NodeType.CHANY: assert track_node.loc.x_low == track_node.loc.x_high y1, y2 = sorted((track_node.loc.y_low, track_node.loc.y_high)) if track_node.loc.x_low not in y_tracks: y_tracks[track_node.loc.x_low] = [] y_tracks[track_node.loc.x_low].append((y1, y2, track)) else: assert False, track_node x_list = [] y_list = [] x_channel_models = {} y_channel_models = {} if pool is not None: for y in x_tracks: x_channel_models[y] = pool.apply_async(process_track, (x_tracks[y], )) for x in y_tracks: y_channel_models[x] = pool.apply_async(process_track, (y_tracks[x], )) for y in progressbar_utils.progressbar(range(max(x_tracks) + 1)): if y in x_tracks: if pool is None: x_channel_models[y] = process_track(x_tracks[y]) else: x_channel_models[y] = x_channel_models[y].get() x_list.append(len(x_channel_models[y].trees)) for idx, tree in enumerate(x_channel_models[y].trees): for i in tree: self.set_track_ptc(track=i[2], ptc=idx) else: x_list.append(0) for x in progressbar_utils.progressbar(range(max(y_tracks) + 1)): if x in y_tracks: if pool is None: y_channel_models[x] = process_track(y_tracks[x]) else: y_channel_models[x] = y_channel_models[x].get() y_list.append(len(y_channel_models[x].trees)) for idx, tree in enumerate(y_channel_models[x].trees): for i in tree: self.set_track_ptc(track=i[2], ptc=idx) else: y_list.append(0) x_min = min(xs) y_min = min(ys) x_max = max(xs) y_max = max(ys) num_padding = 0 for chan, channel_model in x_channel_models.items(): for ptc, start, end in channel_model.fill_empty( max(x_min, 1), x_max): num_padding += 1 self.add_track(track=Track( direction='X', x_low=start, y_low=chan, x_high=end, y_high=chan, ), segment_id=pad_segment, capacity=0, timing=None, ptc=ptc) for chan, channel_model in y_channel_models.items(): for ptc, start, end in channel_model.fill_empty( max(y_min, 1), y_max): num_padding += 1 self.add_track(track=Track( direction='Y', x_low=chan, y_low=start, x_high=chan, y_high=end, ), segment_id=pad_segment, capacity=0, timing=None, ptc=ptc) print('Number padding nodes {}'.format(num_padding)) return Channels( chan_width_max=max(max(x_list), max(y_list)), x_min=x_min, y_min=y_min, x_max=x_max, y_max=y_max, x_list=[ChannelList(idx, info) for idx, info in enumerate(x_list)], y_list=[ChannelList(idx, info) for idx, info in enumerate(y_list)], )
def main(): parser = argparse.ArgumentParser() parser.add_argument('--db_root', required=True, help='Project X-Ray Database') parser.add_argument('--connection_database', help='Database of fabric connectivity', required=True) parser.add_argument('--pin_assignments', help='Pin assignments JSON', required=True) parser.add_argument( '--synth_tiles', help= 'If using an ROI, synthetic tile defintion from prjxray-arch-import') args = parser.parse_args() pool = multiprocessing.Pool(20) db = prjxray.db.Database(args.db_root) grid = db.grid() with DatabaseCache(args.connection_database) as conn: with open(args.pin_assignments) as f: pin_assignments = json.load(f) tile_wires = [] for tile_type, wire_map in pin_assignments['pin_directions'].items(): for wire in wire_map.keys(): tile_wires.append((tile_type, wire)) for tile_type, wire in progressbar_utils.progressbar(tile_wires): pins = [ direction_to_enum(pin) for pin in pin_assignments['pin_directions'][tile_type][wire] ] add_graph_nodes_for_pins(conn, tile_type, wire, pins) if args.synth_tiles: use_roi = True with open(args.synth_tiles) as f: synth_tiles = json.load(f) roi = Roi( db=db, x1=synth_tiles['info']['GRID_X_MIN'], y1=synth_tiles['info']['GRID_Y_MIN'], x2=synth_tiles['info']['GRID_X_MAX'], y2=synth_tiles['info']['GRID_Y_MAX'], ) print('{} generating routing graph for ROI.'.format(now())) else: use_roi = False output_only_nodes = set() input_only_nodes = set() find_pip = create_find_pip(conn) find_wire = create_find_wire(conn) find_connector = create_find_connector(conn) const_connectors = create_const_connectors(conn) print('{} Finding nodes belonging to ROI'.format(now())) if use_roi: for loc in progressbar_utils.progressbar(grid.tile_locations()): gridinfo = grid.gridinfo_at_loc(loc) tile_name = grid.tilename_at_loc(loc) if tile_name in synth_tiles['tiles']: assert len(synth_tiles['tiles'][tile_name]['pins']) == 1 for pin in synth_tiles['tiles'][tile_name]['pins']: if pin['port_type'] not in ['input', 'output']: continue _, _, _, node_pkey = find_wire(tile_name, gridinfo.tile_type, pin['wire']) if pin['port_type'] == 'input': # This track can output be used as a sink. input_only_nodes |= set((node_pkey, )) elif pin['port_type'] == 'output': # This track can output be used as a src. output_only_nodes |= set((node_pkey, )) else: assert False, pin write_cur = conn.cursor() write_cur.execute('SELECT pkey FROM switch WHERE name = ?;', ('__vpr_delayless_switch__', )) delayless_switch_pkey = write_cur.fetchone()[0] edges = [] edge_set = set() for loc in progressbar_utils.progressbar(grid.tile_locations()): gridinfo = grid.gridinfo_at_loc(loc) tile_name = grid.tilename_at_loc(loc) # Not a synth node, check if in ROI. if use_roi and not roi.tile_in_roi(loc): continue tile_type = db.get_tile_type(gridinfo.tile_type) for pip in tile_type.get_pips(): if pip.is_pseudo: continue connections = make_connection( conn=conn, input_only_nodes=input_only_nodes, output_only_nodes=output_only_nodes, find_pip=find_pip, find_wire=find_wire, find_connector=find_connector, tile_name=tile_name, tile_type=gridinfo.tile_type, pip=pip, delayless_switch_pkey=delayless_switch_pkey, const_connectors=const_connectors) if connections: for connection in connections: key = tuple(connection[0:3]) if key in edge_set: continue edge_set.add(key) edges.append(connection) print('{} Created {} edges, inserting'.format(now(), len(edges))) write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") for edge in progressbar_utils.progressbar(edges): write_cur.execute( """ INSERT INTO graph_edge( src_graph_node_pkey, dest_graph_node_pkey, switch_pkey, phy_tile_pkey, pip_in_tile_pkey, backward) VALUES (?, ?, ?, ?, ?, ?)""", edge) write_cur.execute("""COMMIT TRANSACTION;""") print('{} Inserted edges'.format(now())) write_cur.execute( """CREATE INDEX src_node_index ON graph_edge(src_graph_node_pkey);""" ) write_cur.execute( """CREATE INDEX dest_node_index ON graph_edge(dest_graph_node_pkey);""" ) write_cur.connection.commit() print('{} Indices created, marking track liveness'.format(now())) alive_tracks = set() mark_track_liveness(conn, pool, input_only_nodes, output_only_nodes, alive_tracks) print('{} Flushing database back to file "{}"'.format( now(), args.connection_database)) with DatabaseCache(args.connection_database, read_only=True) as conn: verify_channels(conn, alive_tracks) print("{}: Channels verified".format(datetime.datetime.now()))
def build_channels(conn, pool, active_tracks): write_cur = conn.cursor() xs = [] ys = [] x_tracks = {} y_tracks = {} for pkey, track_pkey, graph_node_type, x_low, x_high, y_low, y_high in write_cur.execute( """ SELECT pkey, track_pkey, graph_node_type, x_low, x_high, y_low, y_high FROM graph_node WHERE track_pkey IS NOT NULL;"""): if track_pkey not in active_tracks: continue xs.append(x_low) xs.append(x_high) ys.append(y_low) ys.append(y_high) node_type = graph2.NodeType(graph_node_type) if node_type == graph2.NodeType.CHANX: assert y_low == y_high, (pkey, track_pkey) if y_low not in x_tracks: x_tracks[y_low] = [] x_tracks[y_low].append((x_low, x_high, pkey)) elif node_type == graph2.NodeType.CHANY: assert x_low == x_high, (pkey, track_pkey) if x_low not in y_tracks: y_tracks[x_low] = [] y_tracks[x_low].append((y_low, y_high, pkey)) else: assert False, node_type x_list = [] y_list = [] x_channel_models = {} y_channel_models = {} for y in x_tracks: x_channel_models[y] = pool.apply_async(graph2.process_track, (x_tracks[y], )) for x in y_tracks: y_channel_models[x] = pool.apply_async(graph2.process_track, (y_tracks[x], )) write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") for y in progressbar_utils.progressbar(range(max(x_tracks) + 1)): if y in x_tracks: x_channel_models[y] = x_channel_models[y].get() x_list.append(len(x_channel_models[y].trees)) for idx, tree in enumerate(x_channel_models[y].trees): for i in tree: write_cur.execute( 'UPDATE graph_node SET ptc = ? WHERE pkey = ?;', (idx, i[2])) else: x_list.append(0) for x in progressbar_utils.progressbar(range(max(y_tracks) + 1)): if x in y_tracks: y_channel_models[x] = y_channel_models[x].get() y_list.append(len(y_channel_models[x].trees)) for idx, tree in enumerate(y_channel_models[x].trees): for i in tree: write_cur.execute( 'UPDATE graph_node SET ptc = ? WHERE pkey = ?;', (idx, i[2])) else: y_list.append(0) x_min = min(xs) y_min = min(ys) x_max = max(xs) y_max = max(ys) write_cur.execute( """ INSERT INTO channel(chan_width_max, x_min, x_max, y_min, y_max) VALUES (?, ?, ?, ?, ?);""", (max(max(x_list), max(y_list)), x_min, x_max, y_min, y_max)) for idx, info in enumerate(x_list): write_cur.execute( """ INSERT INTO x_list(idx, info) VALUES (?, ?);""", (idx, info)) for idx, info in enumerate(y_list): write_cur.execute( """ INSERT INTO y_list(idx, info) VALUES (?, ?);""", (idx, info)) write_cur.execute("""COMMIT TRANSACTION;""")
def main(): # Parse arguments parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("--vpr-db", type=str, required=True, help="VPR database file") parser.add_argument("--rr-graph-in", type=str, required=True, help="Input RR graph XML file") parser.add_argument("--rr-graph-out", type=str, default="rr_graph.xml", help="Output RR graph XML file (def. rr_graph.xml)") args = parser.parse_args() # Load data from the database print("Loading database...") with open(args.vpr_db, "rb") as fp: db = pickle.load(fp) vpr_quadrants = db["vpr_quadrants"] vpr_clock_cells = db["vpr_clock_cells"] loc_map = db["loc_map"] vpr_tile_types = db["vpr_tile_types"] vpr_tile_grid = db["vpr_tile_grid"] vpr_switchbox_types = db["vpr_switchbox_types"] vpr_switchbox_grid = db["vpr_switchbox_grid"] connections = db["connections"] switches = db["switches"] # Load the routing graph, build SOURCE -> OPIN and IPIN -> SINK edges. print("Loading rr graph...") xml_graph = rr_xml.Graph(input_file_name=args.rr_graph_in, output_file_name=args.rr_graph_out, progressbar=progressbar_utils.progressbar) # Add back the switches that were unused in the arch.xml and got pruned # byt VPR. for switch in switches: try: xml_graph.graph.get_switch_id(switch.name) continue except KeyError: xml_graph.add_switch( rr.Switch( id=None, name=switch.name, type=rr.SwitchType[switch.type.upper()], timing=rr.SwitchTiming( r=switch.r, c_in=switch.c_in, c_out=switch.c_out, c_internal=switch.c_int, t_del=switch.t_del, ), sizing=rr.SwitchSizing( mux_trans_size=0, buf_size=0, ), )) print("Building maps...") # Add a switch map to the graph switch_map = {} for switch in xml_graph.graph.switches: assert switch.id not in switch_map, switch switch_map[switch.id] = switch xml_graph.graph.switch_map = switch_map # Build node id to node map nodes_by_id = {node.id: node for node in xml_graph.graph.nodes} # Build tile pin names to rr node ids map tile_pin_to_node = build_tile_pin_to_node_map(xml_graph.graph, nodes_by_id, vpr_tile_types, vpr_tile_grid) # Add const network const_node_map = {} for const in ["VCC", "GND"]: m = add_tracks_for_const_network(xml_graph.graph, const, vpr_tile_grid) const_node_map[const] = m # Connection loc (endpoint) to node map. Map ConnectionLoc objects to VPR # rr graph node ids. connection_loc_to_node = {} # Build a map of connections to/from tiles and rr nodes. The map points # to an IPIN/OPIN node for a connection loc that mentions it. node_map = build_tile_connection_map(xml_graph.graph, nodes_by_id, vpr_tile_grid, connections) connection_loc_to_node.update(node_map) # Build the global clock network print("Building the global clock network...") # GMUX to QMUX and QMUX to CAND tracks node_map = create_quadrant_clock_tracks(xml_graph.graph, connections, connection_loc_to_node) connection_loc_to_node.update(node_map) # Clock column tracks cand_node_map = create_column_clock_tracks(xml_graph.graph, vpr_clock_cells, vpr_quadrants) # Add switchbox models. print("Building switchbox models...") switchbox_models = {} # Gather QMUX cells qmux_cells = {} for cell in vpr_clock_cells.values(): if cell.type == "QMUX": loc = cell.loc if loc not in qmux_cells: qmux_cells[loc] = {} qmux_cells[loc][cell.name] = cell # Create the models for loc, type in vpr_switchbox_grid.items(): phy_loc = loc_map.bwd[loc] # QMUX switchbox model if loc in qmux_cells: switchbox_models[loc] = QmuxSwitchboxModel( graph=xml_graph.graph, loc=loc, phy_loc=phy_loc, switchbox=vpr_switchbox_types[type], qmux_cells=qmux_cells[loc], connections=[c for c in connections if is_clock(c)]) # Regular switchbox model else: switchbox_models[loc] = SwitchboxModel( graph=xml_graph.graph, loc=loc, phy_loc=phy_loc, switchbox=vpr_switchbox_types[type], ) # Build switchbox models for switchbox_model in progressbar_utils.progressbar( switchbox_models.values()): switchbox_model.build() # Build the global clock network cell models print("Building QMUX and CAND models...") # Add QMUX and CAND models for cell in progressbar_utils.progressbar(vpr_clock_cells.values()): phy_loc = loc_map.bwd[cell.loc] if cell.type == "QMUX": QmuxModel(graph=xml_graph.graph, cell=cell, phy_loc=phy_loc, switchbox_model=switchbox_models[cell.loc], connections=connections, node_map=connection_loc_to_node) if cell.type == "CAND": CandModel(graph=xml_graph.graph, cell=cell, phy_loc=phy_loc, connections=connections, node_map=connection_loc_to_node, cand_node_map=cand_node_map) # Populate connections to the switchbox models print("Populating connections...") populate_hop_connections(xml_graph.graph, switchbox_models, connections) populate_tile_connections(xml_graph.graph, switchbox_models, connections, connection_loc_to_node) populate_direct_connections(xml_graph.graph, connections, connection_loc_to_node) populate_cand_connections(xml_graph.graph, switchbox_models, cand_node_map) populate_const_connections(xml_graph.graph, switchbox_models, vpr_tile_types, vpr_tile_grid, tile_pin_to_node, const_node_map) # Create channels from tracks pad_segment_id = xml_graph.graph.get_segment_id_from_name("pad") channels_obj = xml_graph.graph.create_channels(pad_segment=pad_segment_id) # Remove padding channels print("Removing padding nodes...") xml_graph.graph.nodes = [ n for n in xml_graph.graph.nodes if n.capacity > 0 ] # Build node id to node map again since there have been new nodes added. nodes_by_id = {node.id: node for node in xml_graph.graph.nodes} # Sanity check edges print("Sanity checking edges...") node_ids = set([n.id for n in xml_graph.graph.nodes]) for edge in xml_graph.graph.edges: assert edge.src_node in node_ids, edge assert edge.sink_node in node_ids, edge assert edge.src_node != edge.sink_node, edge # Sanity check IPIN/OPIN connections. There must be no tile completely # disconnected from the routing network print("Sanity checking tile connections...") connected_locs = set() for edge in xml_graph.graph.edges: src = nodes_by_id[edge.src_node] dst = nodes_by_id[edge.sink_node] if src.type == rr.NodeType.OPIN: loc = (src.loc.x_low, src.loc.y_low) connected_locs.add(loc) if dst.type == rr.NodeType.IPIN: loc = (src.loc.x_low, src.loc.y_low) connected_locs.add(loc) non_empty_locs = set((loc.x, loc.y) for loc in xml_graph.graph.grid if loc.block_type_id > 0) unconnected_locs = non_empty_locs - connected_locs for loc in unconnected_locs: block_type = xml_graph.graph.block_type_at_loc(loc) print(" ERROR: Tile '{}' at ({}, {}) is not connected!".format( block_type, loc[0], loc[1])) # Write the routing graph nodes_obj = xml_graph.graph.nodes edges_obj = xml_graph.graph.edges print("Serializing the rr graph...") xml_graph.serialize_to_xml( channels_obj=channels_obj, nodes_obj=nodes_obj, edges_obj=yield_edges(edges_obj), node_remap=lambda x: x, )
def insert_tracks(conn, tracks_to_insert): write_cur = conn.cursor() write_cur.execute('SELECT pkey FROM switch WHERE name = "short";') short_pkey = write_cur.fetchone()[0] track_graph_nodes = {} track_pkeys = [] for node, tracks_list, track_connections, tracks_model in progressbar_utils.progressbar( tracks_to_insert): write_cur.execute("""INSERT INTO track DEFAULT VALUES""") track_pkey = write_cur.lastrowid track_pkeys.append(track_pkey) write_cur.execute("""UPDATE node SET track_pkey = ? WHERE pkey = ?""", (track_pkey, node)) track_graph_node_pkey = [] for idx, track in enumerate(tracks_list): if track.direction == 'X': node_type = graph2.NodeType.CHANX elif track.direction == 'Y': node_type = graph2.NodeType.CHANY else: assert False, track.direction if idx == 0: capacitance, resistance = get_node_rc(conn, node) else: capacitance = 0 resistance = 0 write_cur.execute( """ INSERT INTO graph_node( graph_node_type, track_pkey, node_pkey, x_low, x_high, y_low, y_high, capacity, capacitance, resistance ) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?)""", (node_type.value, track_pkey, node, track.x_low, track.x_high, track.y_low, track.y_high, capacitance, resistance)) track_graph_node_pkey.append(write_cur.lastrowid) track_graph_nodes[node] = track_graph_node_pkey for connection in track_connections: write_cur.execute( """ INSERT INTO graph_edge( src_graph_node_pkey, dest_graph_node_pkey, switch_pkey, track_pkey ) VALUES (?, ?, ?, ?), (?, ?, ?, ?)""", ( track_graph_node_pkey[connection[0]], track_graph_node_pkey[connection[1]], short_pkey, track_pkey, track_graph_node_pkey[connection[1]], track_graph_node_pkey[connection[0]], short_pkey, track_pkey, )) conn.commit() wire_to_graph = {} for node, tracks_list, track_connections, tracks_model in progressbar_utils.progressbar( tracks_to_insert): track_graph_node_pkey = track_graph_nodes[node] write_cur.execute( """ WITH wires_from_node(wire_pkey, tile_pkey) AS ( SELECT pkey, tile_pkey FROM wire WHERE node_pkey = ? ) SELECT wires_from_node.wire_pkey, tile.grid_x, tile.grid_y FROM tile INNER JOIN wires_from_node ON tile.pkey = wires_from_node.tile_pkey; """, (node, )) wires = write_cur.fetchall() for wire_pkey, grid_x, grid_y in wires: connections = list( tracks_model.get_tracks_for_wire_at_coord((grid_x, grid_y))) assert len(connections) > 0, (wire_pkey, track_pkey, grid_x, grid_y) graph_node_pkey = track_graph_node_pkey[connections[0][0]] wire_to_graph[wire_pkey] = graph_node_pkey for wire_pkey, graph_node_pkey in progressbar_utils.progressbar( wire_to_graph.items()): write_cur.execute( """ UPDATE wire SET graph_node_pkey = ? WHERE pkey = ?""", (graph_node_pkey, wire_pkey)) conn.commit() write_cur.execute( """CREATE INDEX graph_node_nodes ON graph_node(node_pkey);""") write_cur.execute( """CREATE INDEX graph_node_tracks ON graph_node(track_pkey);""") write_cur.execute( """CREATE INDEX graph_edge_tracks ON graph_edge(track_pkey);""") conn.commit() return track_pkeys
def classify_nodes(conn, get_switch_timing): write_cur = conn.cursor() # Nodes are NULL if they they only have either a site pin or 1 pip, but # nothing else. write_cur.execute( """ UPDATE node SET classification = ? WHERE (node.site_wire_pkey IS NULL AND node.number_pips <= 1) OR (node.site_wire_pkey IS NOT NULL AND node.number_pips == 0) ;""", (NodeClassification.NULL.value, )) write_cur.execute( """ UPDATE node SET classification = ? WHERE node.number_pips > 1 and node.site_wire_pkey IS NULL;""", (NodeClassification.CHANNEL.value, )) write_cur.execute( """ UPDATE node SET classification = ? WHERE node.number_pips > 1 and node.site_wire_pkey IS NOT NULL;""", (NodeClassification.EDGES_TO_CHANNEL.value, )) null_nodes = [] edges_to_channel = [] edge_with_mux = [] cur = conn.cursor() cur.execute(""" SELECT count(pkey) FROM node WHERE number_pips == 1 AND site_wire_pkey IS NOT NULL;""") num_nodes = cur.fetchone()[0] with progressbar_utils.ProgressBar(max_value=num_nodes) as bar: bar.update(0) for idx, (node, site_wire_pkey) in enumerate( cur.execute(""" SELECT pkey, site_wire_pkey FROM node WHERE number_pips == 1 AND site_wire_pkey IS NOT NULL;""")): bar.update(idx) write_cur.execute( """ WITH wire_in_node( wire_pkey, phy_tile_pkey, wire_in_tile_pkey ) AS ( SELECT wire.pkey, wire.phy_tile_pkey, wire.wire_in_tile_pkey FROM wire WHERE wire.node_pkey = ? ) SELECT pip_in_tile.pkey, pip_in_tile.src_wire_in_tile_pkey, pip_in_tile.dest_wire_in_tile_pkey, wire_in_node.wire_pkey, wire_in_node.wire_in_tile_pkey, wire_in_node.phy_tile_pkey FROM wire_in_node INNER JOIN pip_in_tile WHERE pip_in_tile.is_pseudo = 0 AND ( pip_in_tile.src_wire_in_tile_pkey = wire_in_node.wire_in_tile_pkey OR pip_in_tile.dest_wire_in_tile_pkey = wire_in_node.wire_in_tile_pkey) LIMIT 1; """, (node, )) (pip_pkey, src_wire_in_tile_pkey, dest_wire_in_tile_pkey, wire_in_node_pkey, wire_in_tile_pkey, phy_tile_pkey) = write_cur.fetchone() assert write_cur.fetchone() is None, node assert (wire_in_tile_pkey == src_wire_in_tile_pkey or wire_in_tile_pkey == dest_wire_in_tile_pkey), (wire_in_tile_pkey, pip_pkey) if src_wire_in_tile_pkey == wire_in_tile_pkey: other_wire = dest_wire_in_tile_pkey else: other_wire = src_wire_in_tile_pkey write_cur.execute( """ SELECT node_pkey FROM wire WHERE wire_in_tile_pkey = ? AND phy_tile_pkey = ?; """, (other_wire, phy_tile_pkey)) (other_node_pkey, ) = write_cur.fetchone() assert write_cur.fetchone() is None assert other_node_pkey is not None, (other_wire, phy_tile_pkey) write_cur.execute( """ SELECT site_wire_pkey, number_pips FROM node WHERE pkey = ?; """, (other_node_pkey, )) result = write_cur.fetchone() assert result is not None, other_node_pkey other_site_wire_pkey, other_number_pips = result assert write_cur.fetchone() is None if other_site_wire_pkey is not None and other_number_pips == 1: if src_wire_in_tile_pkey == wire_in_tile_pkey: src_wire_pkey = site_wire_pkey dest_wire_pkey = other_site_wire_pkey else: src_wire_pkey = other_site_wire_pkey dest_wire_pkey = site_wire_pkey edge_with_mux.append(((node, other_node_pkey), src_wire_pkey, dest_wire_pkey, pip_pkey)) elif other_site_wire_pkey is None and other_number_pips == 1: null_nodes.append(node) null_nodes.append(other_node_pkey) pass else: edges_to_channel.append(node) for nodes, src_wire_pkey, dest_wire_pkey, pip_pkey in progressbar_utils.progressbar( edge_with_mux): assert len(nodes) == 2 switch_pkey = check_edge_with_mux_timing(conn, get_switch_timing, src_wire_pkey, dest_wire_pkey, pip_pkey) write_cur.execute( """ UPDATE node SET classification = ? WHERE pkey IN (?, ?);""", (NodeClassification.EDGE_WITH_MUX.value, nodes[0], nodes[1])) write_cur.execute( """ INSERT INTO edge_with_mux(src_wire_pkey, dest_wire_pkey, pip_in_tile_pkey, switch_pkey) VALUES (?, ?, ?, ?);""", (src_wire_pkey, dest_wire_pkey, pip_pkey, switch_pkey)) for node in progressbar_utils.progressbar(edges_to_channel): write_cur.execute( """ UPDATE node SET classification = ? WHERE pkey = ?;""", ( NodeClassification.EDGES_TO_CHANNEL.value, node, )) for null_node in progressbar_utils.progressbar(null_nodes): write_cur.execute( """ UPDATE node SET classification = ? WHERE pkey = ?;""", ( NodeClassification.NULL.value, null_node, )) write_cur.execute("CREATE INDEX node_type_index ON node(classification);") write_cur.connection.commit()
def import_nodes(db, grid, conn): # Some nodes are just 1 wire, so start by enumerating all wires. cur = conn.cursor() write_cur = conn.cursor() write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") tile_wire_map = {} wires = {} for tile in progressbar_utils.progressbar(grid.tiles()): gridinfo = grid.gridinfo_at_tilename(tile) tile_type = db.get_tile_type(gridinfo.tile_type) cur.execute( """SELECT pkey, tile_type_pkey FROM phy_tile WHERE name = ?;""", (tile, )) phy_tile_pkey, tile_type_pkey = cur.fetchone() for wire in tile_type.get_wires(): # pkey node_pkey tile_pkey wire_in_tile_pkey cur.execute( """ SELECT pkey FROM wire_in_tile WHERE name = ? and tile_type_pkey = ?;""", (wire, tile_type_pkey)) (wire_in_tile_pkey, ) = cur.fetchone() write_cur.execute( """ INSERT INTO wire(phy_tile_pkey, wire_in_tile_pkey) VALUES (?, ?);""", (phy_tile_pkey, wire_in_tile_pkey)) assert (tile, wire) not in tile_wire_map wire_pkey = write_cur.lastrowid tile_wire_map[(tile, wire)] = wire_pkey wires[wire_pkey] = None write_cur.execute("""COMMIT TRANSACTION;""") connections = db.connections() for connection in progressbar_utils.progressbar( connections.get_connections()): a_pkey = tile_wire_map[(connection.wire_a.tile, connection.wire_a.wire)] b_pkey = tile_wire_map[(connection.wire_b.tile, connection.wire_b.wire)] a_node = wires[a_pkey] b_node = wires[b_pkey] if a_node is None: a_node = set((a_pkey, )) if b_node is None: b_node = set((b_pkey, )) if a_node is not b_node: a_node |= b_node for wire in a_node: wires[wire] = a_node nodes = {} for wire_pkey, node in wires.items(): if node is None: node = set((wire_pkey, )) assert wire_pkey in node nodes[id(node)] = node wires_assigned = set() for node in progressbar_utils.progressbar(nodes.values()): write_cur.execute("""INSERT INTO node(number_pips) VALUES (0);""") node_pkey = write_cur.lastrowid for wire_pkey in node: wires_assigned.add(wire_pkey) write_cur.execute( """ UPDATE wire SET node_pkey = ? WHERE pkey = ? ;""", (node_pkey, wire_pkey)) assert len(set(wires.keys()) ^ wires_assigned) == 0 del tile_wire_map del nodes del wires write_cur.execute( "CREATE INDEX wire_in_tile_index ON wire(wire_in_tile_pkey);") write_cur.execute( "CREATE INDEX wire_index ON wire(phy_tile_pkey, wire_in_tile_pkey);") write_cur.execute("CREATE INDEX wire_node_index ON wire(node_pkey);") write_cur.connection.commit()
def create_vpr_grid(conn): """ Create VPR grid from prjxray grid. """ cur = conn.cursor() cur2 = conn.cursor() write_cur = conn.cursor() write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") # Insert synthetic tile types for CLB sites. write_cur.execute('INSERT INTO tile_type(name) VALUES ("SLICEL");') slicel_tile_type_pkey = write_cur.lastrowid write_cur.execute('INSERT INTO tile_type(name) VALUES ("SLICEM");') slicem_tile_type_pkey = write_cur.lastrowid slice_types = { 'SLICEL': slicel_tile_type_pkey, 'SLICEM': slicem_tile_type_pkey, } tiles_to_split = { 'CLBLL_L': tile_splitter.grid.WEST, 'CLBLL_R': tile_splitter.grid.EAST, 'CLBLM_L': tile_splitter.grid.WEST, 'CLBLM_R': tile_splitter.grid.EAST, } # Create initial grid using sites and locations from phy_tile's # Also build up tile_to_tile_type_pkeys, which is a map from original # tile_type_pkey, to array of split tile type pkeys, (e.g. SLICEL/SLICEM). tile_to_tile_type_pkeys = {} grid_loc_map = {} for phy_tile_pkey, tile_type_pkey, grid_x, grid_y in progressbar_utils.progressbar( cur.execute(""" SELECT pkey, tile_type_pkey, grid_x, grid_y FROM phy_tile; """)): cur2.execute("SELECT name FROM tile_type WHERE pkey = ?;", (tile_type_pkey, )) tile_type_name = cur2.fetchone()[0] sites = [] site_pkeys = set() for (site_pkey, ) in cur2.execute( """ SELECT site_pkey FROM wire_in_tile WHERE tile_type_pkey = ? AND site_pkey IS NOT NULL;""", (tile_type_pkey, )): site_pkeys.add(site_pkey) for site_pkey in site_pkeys: cur2.execute( """ SELECT x_coord, y_coord, site_type_pkey FROM site WHERE pkey = ?;""", (site_pkey, )) result = cur2.fetchone() assert result is not None, (tile_type_pkey, site_pkey) x, y, site_type_pkey = result cur2.execute("SELECT name FROM site_type WHERE pkey = ?;", ((site_type_pkey, ))) site_type_name = cur2.fetchone()[0] sites.append( tile_splitter.grid.Site(name=site_type_name, phy_tile_pkey=phy_tile_pkey, tile_type_pkey=tile_type_pkey, site_type_pkey=site_type_pkey, site_pkey=site_pkey, x=x, y=y)) sites = sorted(sites, key=lambda s: (s.x, s.y)) if tile_type_name in tiles_to_split: tile_type_pkeys = [] for site in sites: tile_type_pkeys.append(slice_types[site.name]) if tile_type_name in tile_to_tile_type_pkeys: assert tile_to_tile_type_pkeys[tile_type_name] == \ tile_type_pkeys, (tile_type_name,) else: tile_to_tile_type_pkeys[tile_type_name] = tile_type_pkeys grid_loc_map[(grid_x, grid_y)] = tile_splitter.grid.Tile( root_phy_tile_pkeys=[phy_tile_pkey], phy_tile_pkeys=[phy_tile_pkey], tile_type_pkey=tile_type_pkey, sites=sites) cur.execute('SELECT pkey FROM tile_type WHERE name = "NULL";') empty_tile_type_pkey = cur.fetchone()[0] tile_types = {} for tile_type, split_direction in tiles_to_split.items(): cur.execute('SELECT pkey FROM tile_type WHERE name = ?;', (tile_type, )) tile_type_pkey = cur.fetchone()[0] tile_types[tile_type] = tile_type_pkey vpr_grid = tile_splitter.grid.Grid( grid_loc_map=grid_loc_map, empty_tile_type_pkey=empty_tile_type_pkey) for tile_type, split_direction in tiles_to_split.items(): vpr_grid.split_tile_type( tile_type_pkey=tile_types[tile_type], tile_type_pkeys=tile_to_tile_type_pkeys[tile_type], split_direction=split_direction) new_grid = vpr_grid.output_grid() # Create tile rows for each tile in the VPR grid. As provide map entries # to physical grid and alias map from split tile type to original tile # type. for (grid_x, grid_y), tile in new_grid.items(): # TODO: Merging of tiles isn't supported yet, so don't handle multiple # phy_tile_pkeys yet. The phy_tile_pkey to add to the new VPR tile # should be the tile to use on the FASM prefix. assert len(tile.phy_tile_pkeys) == 1 assert len(tile.root_phy_tile_pkeys) in [0, 1], len( tile.root_phy_tile_pkeys) if tile.split_sites: assert len(tile.sites) == 1 write_cur.execute( """ SELECT pkey, parent_tile_type_pkey FROM site_as_tile WHERE tile_type_pkey = ? AND site_pkey = ?""", (tile.sites[0].tile_type_pkey, tile.sites[0].site_pkey)) result = write_cur.fetchone() if result is None: write_cur.execute( """ INSERT INTO site_as_tile(parent_tile_type_pkey, tile_type_pkey, site_pkey) VALUES (?, ?, ?);""", (tile.tile_type_pkey, tile.sites[0].tile_type_pkey, tile.sites[0].site_pkey)) site_as_tile_pkey = write_cur.lastrowid else: site_as_tile_pkey, parent_tile_type_pkey = result assert parent_tile_type_pkey == tile.tile_type_pkey # Mark that this tile is split by setting the site_as_tile_pkey. write_cur.execute( """ INSERT INTO tile(phy_tile_pkey, tile_type_pkey, site_as_tile_pkey, grid_x, grid_y) VALUES ( ?, ?, ?, ?, ?)""", (tile.phy_tile_pkeys[0], tile.tile_type_pkey, site_as_tile_pkey, grid_x, grid_y)) else: write_cur.execute( """ INSERT INTO tile(phy_tile_pkey, tile_type_pkey, grid_x, grid_y) VALUES ( ?, ?, ?, ?)""", (tile.phy_tile_pkeys[0], tile.tile_type_pkey, grid_x, grid_y)) tile_pkey = write_cur.lastrowid # Build the phy_tile <-> tile map. for phy_tile_pkey in tile.phy_tile_pkeys: write_cur.execute( """ INSERT INTO tile_map(tile_pkey, phy_tile_pkey) VALUES (?, ?) """, (tile_pkey, phy_tile_pkey)) # First assign all wires at the root_phy_tile_pkeys to this tile_pkey. # This ensures all wires, (including wires without sites) have a home. for root_phy_tile_pkey in tile.root_phy_tile_pkeys: write_cur.execute( """ UPDATE wire SET tile_pkey = ? WHERE phy_tile_pkey = ? ;""", (tile_pkey, root_phy_tile_pkey)) write_cur.execute( "CREATE INDEX tile_location_index ON tile(grid_x, grid_y);") write_cur.execute("CREATE INDEX tile_to_phy_map ON tile_map(tile_pkey);") write_cur.execute( "CREATE INDEX phy_to_tile_map ON tile_map(phy_tile_pkey);") write_cur.execute("""COMMIT TRANSACTION;""") write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") # At this point all wires have a tile_pkey that corrisponds to the old root # tile. Wires belonging to sites need to be reassigned to their respective # tiles. for (grid_x, grid_y), tile in new_grid.items(): cur2.execute("SELECT pkey FROM tile WHERE grid_x = ? AND grid_y = ?;", (grid_x, grid_y)) tile_pkey = cur2.fetchone()[0] for site in tile.sites: cur2.execute("SELECT tile_type_pkey FROM phy_tile WHERE pkey = ?;", (site.phy_tile_pkey, )) tile_type_pkey = cur2.fetchone()[0] # Find all wires that belong to the new tile location. for wire_pkey, wire_in_tile_pkey in cur2.execute( """ WITH wires(wire_pkey, wire_in_tile_pkey) AS ( SELECT pkey, wire_in_tile_pkey FROM wire WHERE phy_tile_pkey = ? ) SELECT wires.wire_pkey, wires.wire_in_tile_pkey FROM wires INNER JOIN wire_in_tile ON wire_in_tile.pkey = wires.wire_in_tile_pkey WHERE wire_in_tile.site_pkey = ? ;""", (site.phy_tile_pkey, site.site_pkey)): # Move the wire to the new tile_pkey. write_cur.execute( """ UPDATE wire SET tile_pkey = ? WHERE pkey = ?;""", ( tile_pkey, wire_pkey, )) # Wires connected to the site via a pip require traversing the # pip. other_wire_in_tile_pkey = traverse_pip(conn, wire_in_tile_pkey) if other_wire_in_tile_pkey is not None: # A wire was found connected to the site via pip, reassign # tile_pkey. write_cur.execute( """ UPDATE wire SET tile_pkey = ? WHERE phy_tile_pkey = ? AND wire_in_tile_pkey = ? ;""", (tile_pkey, site.phy_tile_pkey, other_wire_in_tile_pkey)) # Now that final wire <-> tile assignments are made, create the index. write_cur.execute( "CREATE INDEX tile_wire_index ON wire(wire_in_tile_pkey, tile_pkey);") write_cur.execute("""COMMIT TRANSACTION;""")