def create_tracks_from_points(name, graph, unique_pos, short, grid_width, grid_height): xs, ys = points.decompose_points_into_tracks( unique_pos, grid_width, grid_height, right_only=True, ) tracks_list, track_connections = tracks.make_tracks( xs, ys, unique_pos, grid_width, grid_height) tracks_model = tracks.Tracks(tracks_list, track_connections) nodes = [] for idx, track in enumerate(tracks_list): nodes.append( graph.add_track( track=track, segment_id=graph.segments[0].id, capacity=1, name="{}{}".format(name, idx), )) for aidx, bidx in track_connections: graph.add_edge( src_node=nodes[aidx], sink_node=nodes[bidx], switch_id=short, ) graph.add_edge( src_node=nodes[bidx], sink_node=nodes[aidx], switch_id=short, ) return nodes, tracks_model
def form_tracks(conn): c = conn.cursor() c.execute('SELECT count(pkey) FROM node WHERE classification == ?;', (NodeClassification.CHANNEL.value, )) num_nodes = c.fetchone()[0] tracks_to_insert = [] with progressbar.ProgressBar(max_value=num_nodes) as bar: bar.update(0) c2 = conn.cursor() for idx, (node, ) in enumerate( c.execute( """ SELECT pkey FROM node WHERE classification == ?; """, (NodeClassification.CHANNEL.value, ))): bar.update(idx) unique_pos = set() for wire_pkey, grid_x, grid_y in c2.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, )): unique_pos.add((grid_x, grid_y)) xs, ys = points.decompose_points_into_tracks(unique_pos) tracks_list, track_connections = tracks.make_tracks( xs, ys, unique_pos) tracks_model = tracks.Tracks(tracks_list, track_connections) tracks_to_insert.append( [node, tracks_list, track_connections, tracks_model]) insert_tracks(conn, tracks_to_insert)
def get_track_model(conn, track_pkey): assert track_pkey is not None track_list = [] track_nodes = [] c2 = conn.cursor() graph_node_pkey = {} for idx, (pkey, graph_node_type, x_low, x_high, y_low, y_high) in enumerate( c2.execute( """ SELECT pkey, graph_node_type, x_low, x_high, y_low, y_high FROM graph_node WHERE track_pkey = ?""", (track_pkey, ))): node_type = graph2.NodeType(graph_node_type) if node_type == graph2.NodeType.CHANX: direction = 'X' elif node_type == graph2.NodeType.CHANY: direction = 'Y' graph_node_pkey[pkey] = idx track_nodes.append(pkey) track_list.append( tracks.Track(direction=direction, x_low=x_low, x_high=x_high, y_low=y_low, y_high=y_high)) track_connections = set() for src_graph_node_pkey, dest_graph_node_pkey in c2.execute( """ SELECT src_graph_node_pkey, dest_graph_node_pkey FROM graph_edge WHERE track_pkey = ?""", (track_pkey, )): src_idx = graph_node_pkey[src_graph_node_pkey] dest_idx = graph_node_pkey[dest_graph_node_pkey] track_connections.add(tuple(sorted((src_idx, dest_idx)))) tracks_model = tracks.Tracks(track_list, list(track_connections)) return tracks_model, track_nodes
def form_tracks(self, grid): connected_tiles = set() for tile, wire in self.node: connected_tiles.add(grid.loc_of_tilename(tile)) unique_pos = set() for tile, wire in self.node: loc = grid.loc_of_tilename(tile) unique_pos.add((loc.grid_x, loc.grid_y)) xs, ys = points.decompose_points_into_tracks(unique_pos) self.tracks, self.track_connections = tracks.make_tracks( xs, ys, unique_pos) self.wire_connections = {} self.tracks_model = tracks.Tracks(self.tracks, self.track_connections) for tile, wire in self.node: loc = grid.loc_of_tilename(tile) connections = list( self.tracks_model.get_tracks_for_wire_at_coord( (loc.grid_x, loc.grid_y))) assert len(connections) > 0 self.wire_connections[(tile, wire)] = connections[0][0]
def main(): parser = argparse.ArgumentParser() parser.add_argument( '--db_root', required=True, help='Project X-Ray Database') parser.add_argument( '--read_rr_graph', required=True, help='Input rr_graph file') parser.add_argument( '--write_rr_graph', required=True, help='Output rr_graph file') parser.add_argument( '--channels', required=True, help='Channel definitions from prjxray_form_channels') parser.add_argument( '--synth_tiles', help='If using an ROI, synthetic tile defintion from prjxray-arch-import') args = parser.parse_args() db = prjxray.db.Database(args.db_root) grid = db.grid() 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 # Convert input rr graph into graph2.Graph object. input_rr_graph = read_xml_file(args.read_rr_graph) xml_graph = xml_graph2.Graph( input_rr_graph, progressbar=progressbar.progressbar) graph = xml_graph.graph tool_version = input_rr_graph.getroot().attrib['tool_version'] tool_comment = input_rr_graph.getroot().attrib['tool_comment'] delayless_switch = graph.get_delayless_switch_id() print('{} reading channels definitions.'.format(now())) with open(args.channels) as f: channels = json.load(f) segment_id = graph.get_segment_id_from_name('dummy') track_wire_map = {} print('{} add nodes for all channels.'.format(now())) used_channels = 0 for idx, channel in progressbar.progressbar(enumerate(channels['channels'])): # Don't use dead channels if using an ROI. # Consider a channel alive if at least 1 wire in the node is part of a # live tile. if use_roi: alive = False for tile, wire in channel['wires']: loc = grid.loc_of_tilename(tile) if roi.tile_in_roi(loc) or tile in synth_tiles['tiles']: alive = True break if not alive: continue used_channels += 1 nodes = [] track_list = [] for idx2, track_dict in enumerate(channel['tracks']): if track_dict['direction'] == 'X': track_dict['x_low'] = max(track_dict['x_low'], 1) elif track_dict['direction'] == 'Y': track_dict['y_low'] = max(track_dict['y_low'], 1) track = tracks.Track(**track_dict) track_list.append(track) nodes.append(graph.add_track(track=track, segment_id=segment_id, name='track_{}_{}'.format(idx, idx2))) for a_idx, b_idx in channel['track_connections']: graph.add_edge(nodes[a_idx], nodes[b_idx], delayless_switch, 'track_{}_to_{}'.format(a_idx, b_idx)) graph.add_edge(nodes[b_idx], nodes[a_idx], delayless_switch, 'track_{}_to_{}'.format(b_idx, a_idx)) tracks_model = tracks.Tracks(track_list, channel['track_connections']) for tile, wire in channel['wires']: track_wire_map[(tile, wire)] = (tracks_model, nodes) print('original {} final {}'.format(len(channels['channels']), used_channels)) routing_switch = graph.get_switch_id('routing') pip_map = {} edges_with_mux = {} for idx, edge_with_mux in progressbar.progressbar(enumerate(channels['edges_with_mux'])): if edge_with_mux['pip'] not in edges_with_mux: edges_with_mux[edge_with_mux['pip']] = {} assert len(edge_with_mux['source_node']) == 1 edges_with_mux[edge_with_mux['pip']][tuple(edge_with_mux['source_node'][0])] = edge_with_mux['destination_node'] # Set of (src, sink, switch_id) tuples that pip edges have been sent to # VPR. VPR cannot handle duplicate paths with the same switch id. pip_set = set() print('{} Adding edges'.format(now())) for loc in progressbar.progressbar(grid.tile_locations()): gridinfo = grid.gridinfo_at_loc(loc) tile_name = grid.tilename_at_loc(loc) if use_roi: 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']: tracks_model, track_nodes = track_wire_map[(tile_name, pin['wire'])] option = list(tracks_model.get_tracks_for_wire_at_coord(loc)) assert len(option) > 0 if pin['port_type'] == 'input': tile_type = 'BLK_SY-OUTPAD' wire = 'outpad' elif pin['port_type'] == 'output': tile_type = 'BLK_SY-INPAD' wire = 'inpad' else: assert False, pin track_node = track_nodes[option[0][0]] pin_name = graph.create_pin_name_from_tile_type_and_pin( tile_type, wire) pin_node = graph.get_nodes_for_pin(loc, pin_name) if pin['port_type'] == 'input': graph.add_edge( src_node=track_node, sink_node=pin_node[0][0], switch_id=routing_switch, name='synth_{}_{}'.format(tile_name, pin['wire']), ) elif pin['port_type'] == 'output': graph.add_edge( src_node=pin_node[0][0], sink_node=track_node, switch_id=routing_switch, name='synth_{}_{}'.format(tile_name, pin['wire']), ) else: assert False, pin else: # Not a synth node, check if in ROI. if 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 if not pip.is_directional: # TODO: Handle bidirectional pips? continue edge_node = make_connection(graph, track_wire_map, loc, tile_name, gridinfo.tile_type, pip, routing_switch, edges_with_mux, grid, pip_set) if edge_node is not None: pip_map[(tile_name, pip.name)] = edge_node print('{} Writing node mapping.'.format(now())) node_mapping = { 'pips': [], 'tracks': [] } for (tile, pip_name), edge in pip_map.items(): node_mapping['pips'].append({ 'tile': tile, 'pip': pip_name, 'edge': edge }) for (tile, wire), (_, nodes) in track_wire_map.items(): node_mapping['tracks'].append({ 'tile': tile, 'wire': wire, 'nodes': nodes, }) with open('node_mapping.pickle', 'wb') as f: pickle.dump(node_mapping, f) print('{} Create channels and serializing.'.format(now())) pool = multiprocessing.Pool(10) serialized_rr_graph = xml_graph.serialize_to_xml( tool_version=tool_version, tool_comment=tool_comment, pad_segment=segment_id, pool=pool, ) print('{} Writing to disk.'.format(now())) with open(args.write_rr_graph, "wb") as f: f.write(serialized_rr_graph) print('{} Done.'.format(now()))
def create_track(node, unique_pos): xs, ys = points.decompose_points_into_tracks(unique_pos) tracks_list, track_connections = tracks.make_tracks(xs, ys, unique_pos) tracks_model = tracks.Tracks(tracks_list, track_connections) return [node, tracks_list, track_connections, tracks_model]
def main(): parser = argparse.ArgumentParser() parser.add_argument('--db_root', help='Project X-Ray Database', required=True) parser.add_argument('--channels', help='Input JSON defining channel assignments', 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) grid = db.grid() edge_assignments = {} wires_in_tile_types = set() for tile_type in db.get_tile_types(): type_obj = db.get_tile_type(tile_type) for wire in type_obj.get_wires(): wires_in_tile_types.add((tile_type, wire)) for site in type_obj.get_sites(): for site_pin in site.site_pins: if site_pin.wire is None: continue key = (tile_type, site_pin.wire) assert key not in edge_assignments, key edge_assignments[key] = [] print('{} Reading channel data'.format(datetime.datetime.now())) with open(args.channels) as f: channels = json.load(f) print('{} Done reading channel data'.format(datetime.datetime.now())) direct_connections = set() # 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). for edge_with_mux in progressbar.progressbar(channels['edges_with_mux']): source_tile = None source_tile_type = None source_wire = None destination_tile = None destination_tile_type = None destination_wire = None for tile, wire in edge_with_mux['source_node']: tileinfo = grid.gridinfo_at_tilename(tile) tile_type = db.get_tile_type(tileinfo.tile_type) wire_info = tile_type.get_wire_info(wire) if len(wire_info.sites) == 1: assert source_tile is None, (tile, wire, source_tile) source_tile = tile source_tile_type = tileinfo.tile_type source_wire = wire for tile, wire in edge_with_mux['destination_node']: tileinfo = grid.gridinfo_at_tilename(tile) tile_type = db.get_tile_type(tileinfo.tile_type) wire_info = tile_type.get_wire_info(wire) if len(wire_info.sites) == 1: assert destination_tile is None, (tile, wire, destination_tile, wire_info) destination_tile = tile destination_tile_type = tileinfo.tile_type destination_wire = wire assert source_tile is not None assert destination_tile is not None source_loc = grid.loc_of_tilename(source_tile) destination_loc = grid.loc_of_tilename(destination_tile) assert source_loc.grid_x == destination_loc.grid_x or source_loc.grid_y == destination_loc.grid_y, ( source_tile, destination_tile, edge_with_mux['pip']) direct_connections.add( DirectConnection( from_pin='{}.{}'.format(source_tile_type, source_wire), to_pin='{}.{}'.format(destination_tile_type, destination_wire), switch_name='routing', 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, )) wires_not_in_channels = {} for node in progressbar.progressbar(channels['node_not_in_channels']): reason = node['classification'] for tile, wire in node['wires']: tileinfo = grid.gridinfo_at_tilename(tile) key = (tileinfo.tile_type, wire) # Sometimes nodes in particular tile instances are disconnected, # disregard classification changes if this is the case. if reason != '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, 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. for channel in progressbar.progressbar(channels['channels']): track_list = [] for track in channel['tracks']: track_list.append(tracks.Track(**track)) tracks_model = tracks.Tracks(track_list, channel['track_connections']) channel_nodes.append(tracks_model) for tile, wire in channel['wires']: tileinfo = grid.gridinfo_at_tilename(tile) key = (tileinfo.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) channel_wires_to_tracks[(tile, wire)] = tracks_model # Make sure all wires appear to have been assigned. 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. for node in progressbar.progressbar(channels['node_not_in_channels']): reason = node['classification'] assert reason != 'EDGE_WITH_SHORT' if reason == 'NULL': for tile, wire in node['wires']: tileinfo = grid.gridinfo_at_tilename(tile) tile_type = db.get_tile_type(tileinfo.tile_type) null_tile_wires.add((tileinfo.tile_type, wire)) if reason == 'EDGES_TO_CHANNEL': num_sites = 0 for tile, wire in node['wires']: tileinfo = grid.gridinfo_at_tilename(tile) loc = grid.loc_of_tilename(tile) tile_type = db.get_tile_type(tileinfo.tile_type) wire_info = tile_type.get_wire_info(wire) num_sites += len(wire_info.sites) for pip in wire_info.pips: other_wire = prjxray.tile.get_other_wire_from_pip( tile_type.get_pip_by_name(pip), wire) key = (tile, other_wire) if key in channel_wires_to_tracks: tracks_model = channel_wires_to_tracks[key] if len(wire_info.sites) > 0: available_pins = set( pin_dir for _, pin_dir in tracks_model. get_tracks_for_wire_at_coord((loc.grid_x, loc.grid_y))) edge_assignments[(tileinfo.tile_type, wire)].append(available_pins) final_edge_assignments = {} for (tile_type, wire), available_pins in progressbar.progressbar( edge_assignments.items()): 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[(tile_type, wire)] = [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[(tile_type, wire)] = [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[(tile_type, wire)] = pins for (tile_type, wire), available_pins in edge_assignments.items(): pins = set(final_edge_assignments[(tile_type, wire)]) for required_pins in available_pins: assert len(pins & set(required_pins)) > 0, (tile_type, wire, pins, required_pins) pin_directions = {} for (tile_type, wire), pins in final_edge_assignments.items(): 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)