def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( '--connection_db', required=True, help="xc7 connection database." ) parser.add_argument( '--route_file', required=True, help="VPR route output file." ) parser.add_argument( '--rr_graph', required=True, help="Real or virt xc7 graph" ) args = parser.parse_args() xml_graph = xml_graph2.Graph( read_xml_file(args.rr_graph), need_edges=False ) graph = xml_graph.graph conn = sqlite3.connect(args.connection_db) with open(args.route_file) as f: net_list = create_net_list(conn, graph, f) print(json.dumps([net._asdict() for net in net_list], indent=2))
def rebuild_graph(fn, fn_out, rcw=6, verbose=False): """ Add rcw tracks spanning full channel to both X and Y channels Connect all of those to all the adjacent pins Fully connect tracks at intersections For intersections this means we actually have two edges per intersection since source and sink must be specified """ print('Importing input g') xml_graph = xml_graph2.Graph( fn, output_file_name=fn_out, ) graph = xml_graph.graph grid_width = max(p.x for p in graph.grid) + 1 grid_height = max(p.y for p in graph.grid) + 1 mux = graph.get_switch_id('mux') try: short = graph.get_switch_id('short') except KeyError: short = xml_graph.add_switch( graph2.Switch( id=None, name='short', type=graph2.SwitchType.SHORT, timing=None, sizing=graph2.SwitchSizing( mux_trans_size=0, buf_size=0, ), ) ) create_tracks(graph, grid_width, grid_height, rcw, verbose=verbose) create_global_constant_tracks(graph, mux, short, grid_width, grid_height) connect_blocks_to_tracks(graph, grid_width, grid_height, rcw, switch=mux) connect_tracks_to_tracks(graph, switch=mux, verbose=verbose) print("Completed rebuild") xml_graph.root_attrib["tool_version"] = "dev" xml_graph.root_attrib["tool_comment"] = "Generated from black magic" channels_obj = graph.create_channels(pad_segment=graph.segments[0].id) xml_graph.serialize_to_xml( channels_obj=channels_obj, connection_box_obj=None, nodes_obj=graph.nodes, edges_obj=graph.edges )
def load_net_list(conn, rr_graph_file, route_file): xml_graph = xml_graph2.Graph(read_xml_file(rr_graph_file), build_pin_edges=False) graph = xml_graph.graph net_map = {} with open(route_file) as f: for net in create_net_list(conn, graph, f): net_map[net.wire_pkey] = net.name return net_map
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('--write_rr_node_map', required=True, help='Output map of graph_node_pkey to rr inode file') parser.add_argument('--connection_database', help='Database of fabric connectivity', required=True) parser.add_argument( '--synth_tiles', help= 'If using an ROI, synthetic tile defintion from prjxray-arch-import') parser.add_argument( '--graph_limit', help='Limit grid to specified dimensions in x_min,y_min,x_max,y_max', ) args = parser.parse_args() db = prjxray.db.Database(args.db_root) synth_tiles = None 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())) elif args.graph_limit: use_roi = True x_min, y_min, x_max, y_max = map(int, args.graph_limit.split(',')) roi = Roi( db=db, x1=x_min, y1=y_min, x2=x_max, y2=y_max, ) else: use_roi = False roi = None synth_tiles = None # Convert input rr graph into graph2.Graph object. input_rr_graph = read_xml_file(args.read_rr_graph) if synth_tiles is None: synth_tiles = find_constant_network(input_rr_graph) xml_graph = xml_graph2.Graph( input_rr_graph, progressbar=progressbar_utils.progressbar, output_file_name=args.write_rr_graph, ) graph = xml_graph.graph tool_version = input_rr_graph.getroot().attrib['tool_version'] tool_comment = input_rr_graph.getroot().attrib['tool_comment'] with DatabaseCache(args.connection_database, True) as conn: cur = conn.cursor() for name, internal_capacitance, drive_resistance, intrinsic_delay, \ switch_type in cur.execute(""" SELECT name, internal_capacitance, drive_resistance, intrinsic_delay, switch_type FROM switch;"""): # Add back missing switchs, which were unused in arch xml, and so # were not emitted in rrgraph XML. # # TODO: This can be removed once # https://github.com/verilog-to-routing/vtr-verilog-to-routing/issues/354 # is fixed. try: graph.get_switch_id(name) continue except KeyError: xml_graph.add_switch( graph2.Switch( id=None, name=name, type=graph2.SwitchType[switch_type.upper()], timing=graph2.SwitchTiming( r=drive_resistance, c_in=0.0, c_out=0.0, c_internal=internal_capacitance, t_del=intrinsic_delay, ), sizing=graph2.SwitchSizing( mux_trans_size=0, buf_size=0, ), )) # Mapping of graph_node.pkey to rr node id. node_mapping = {} print('{} Creating connection box list'.format(now())) connection_box_map = create_connection_boxes(conn, graph) # Match site pins rr nodes with graph_node's in the connection_database. print('{} Importing graph nodes'.format(now())) import_graph_nodes(conn, graph, node_mapping, connection_box_map) # Walk all track graph nodes and add them. print('{} Creating tracks'.format(now())) segment_id = graph.get_segment_id_from_name('dummy') create_track_rr_graph(conn, graph, node_mapping, use_roi, roi, synth_tiles, segment_id) # 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. if use_roi: print('{} Adding synthetic edges'.format(now())) add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles) print('{} Creating channels.'.format(now())) channels_obj = create_channels(conn) x_dim, y_dim = phy_grid_dims(conn) connection_box_obj = graph.create_connection_box_object(x_dim=x_dim, y_dim=y_dim) print('{} Serializing to disk.'.format(now())) with xml_graph: xml_graph.start_serialize_to_xml( tool_version=tool_version, tool_comment=tool_comment, channels_obj=channels_obj, connection_box_obj=connection_box_obj, ) xml_graph.serialize_nodes(yield_nodes(xml_graph.graph.nodes)) xml_graph.serialize_edges( import_graph_edges(conn, graph, node_mapping)) print('{} Writing node map.'.format(now())) with open(args.write_rr_node_map, 'wb') as f: pickle.dump(node_mapping, f) print('{} Done writing node map.'.format(now()))
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 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 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('--connection_database', help='Database of fabric connectivity', required=True) 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, output_file_name=args.write_rr_graph, ) graph = xml_graph.graph # Add back short switch, which is unused in arch xml, so is not emitted in # rrgraph XML. # # TODO: This can be removed once # https://github.com/verilog-to-routing/vtr-verilog-to-routing/issues/354 # is fixed. try: short = graph.get_switch_id('short') except KeyError: short = xml_graph.add_switch( graph2.Switch( id=None, name='short', type=graph2.SwitchType.SHORT, timing=None, sizing=graph2.SwitchSizing( mux_trans_size=0, buf_size=0, ), )) tool_version = input_rr_graph.getroot().attrib['tool_version'] tool_comment = input_rr_graph.getroot().attrib['tool_comment'] with DatabaseCache(args.connection_database, True) as conn: # Mapping of graph_node.pkey to rr node id. node_mapping = {} # Match site pins rr nodes with graph_node's in the connection_database. print('{} Importing graph nodes'.format(now())) import_graph_nodes(conn, graph, node_mapping) # Walk all track graph nodes and add them. print('{} Creating tracks'.format(now())) segment_id = graph.get_segment_id_from_name('dummy') create_track_rr_graph(conn, graph, node_mapping, use_roi, roi, synth_tiles, segment_id) # 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. if use_roi: print('{} Adding synthetic edges'.format(now())) add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles) print('{} Creating channels.'.format(now())) channels_obj = create_channels(conn) print('{} Serializing to disk.'.format(now())) with xml_graph: xml_graph.start_serialize_to_xml( tool_version=tool_version, tool_comment=tool_comment, channels_obj=channels_obj, ) xml_graph.serialize_nodes(yield_nodes(xml_graph.graph.nodes)) xml_graph.serialize_edges( import_graph_edges(conn, graph, node_mapping))