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 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() for node_pkey, classification in progressbar.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_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 tile_pkey, wire_in_tile_pkey in c2.execute(""" SELECT tile_pkey, wire_in_tile_pkey FROM wire WHERE node_pkey = ?;""", (node_pkey,)): c3 = conn.cursor() c3.execute(""" SELECT name, grid_x, grid_y FROM tile WHERE pkey = ?;""", (tile_pkey,)) (tile, 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() c3.execute(""" SELECT pkey, name, site_pin_pkey FROM wire_in_tile WHERE pkey = ?;""", (wire_in_tile_pkey,)) (wire_in_tile_pkey, wire, site_pin_pkey) = c3.fetchone() # This node has no site pin, don't need to assign pin direction. if site_pin_pkey is None: continue for pip_pkey, pip, src_wire_in_tile_pkey, dest_wire_in_tile_pkey in c3.execute(""" SELECT pkey, name, src_wire_in_tile_pkey, dest_wire_in_tile_pkey FROM pip_in_tile WHERE src_wire_in_tile_pkey = ? OR dest_wire_in_tile_pkey = ?;""", (wire_in_tile_pkey, wire_in_tile_pkey)): assert ( src_wire_in_tile_pkey == wire_in_tile_pkey or dest_wire_in_tile_pkey == wire_in_tile_pkey), pip if src_wire_in_tile_pkey == wire_in_tile_pkey: other_wire_in_tile_pkey = dest_wire_in_tile_pkey else: other_wire_in_tile_pkey = src_wire_in_tile_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 tile_pkey = ? AND wire_in_tile_pkey = ? );""", (tile_pkey, other_wire_in_tile_pkey)) (track_pkey, classification) = c4.fetchone() # 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(pin_dir for _, pin_dir in tracks_model.get_tracks_for_wire_at_coord((grid_x, grid_y))) edge_assignments[(tile_type, wire)].append(available_pins)