def __init__( self, input_file_name, output_file_name=None, progressbar=None, build_pin_edges=True, rebase_nodes=True, filter_nodes=True, ): if progressbar is None: progressbar = lambda x: x # noqa: E731 self.input_file_name = input_file_name self.progressbar = progressbar self.output_file_name = output_file_name graph_input = graph_from_xml(input_file_name, progressbar, filter_nodes=filter_nodes) graph_input['build_pin_edges'] = build_pin_edges self.root_attrib = graph_input["root_attrib"] del graph_input["root_attrib"] if rebase_nodes: rebase_nodes = [] for node in graph_input['nodes']: node_d = node._asdict() node_d['id'] = len(rebase_nodes) rebase_nodes.append(graph2.Node(**node_d)) graph_input['nodes'] = rebase_nodes self.graph = graph2.Graph(**graph_input) self.xf = None self.xf_tag = [] if DEBUG > 0: self._write_xml = self._write_xml_debug else: self._write_xml = self._write_xml_no_debug
def read_node(node, new_node_id=None): node_loc = node.loc node_timing = node.timing return graph2.Node( id=new_node_id if new_node_id is not None else node.id, type=enum_from_string(graph2.NodeType, node.type), direction=enum_from_string(graph2.NodeDirection, node.direction), capacity=node.capacity, loc=graph2.NodeLoc( x_low=node_loc.xlow, y_low=node_loc.ylow, x_high=node_loc.xhigh, y_high=node_loc.yhigh, ptc=node_loc.ptc, side=enum_from_string(tracks.Direction, node_loc.side), ), timing=graph2.NodeTiming(r=node_timing.r, c=node_timing.c), metadata=None, segment=graph2.NodeSegment(segment_id=node.segment.segmentId), )
def main(): # Parse arguments parser = argparse.ArgumentParser() parser.add_argument("--rr-graph-in", required=True, type=str, help="Input RR graph XML") parser.add_argument("--rr-graph-out", required=True, type=str, help="Output RR graph XML") parser.add_argument("--gsb", default=None, type=str, help="Path to GSB data files") parser.add_argument("--openfpga-arch-in", type=str, required=True, help="OpenFPGA arch.xml input") parser.add_argument( "--capnp-schema", default=None, type=str, help="Path to rr graph cap'n'proto schema (for binary rr graph writing)" ) args = parser.parse_args() # Verify the output rr graph extension rr_graph_out_ext = os.path.splitext(args.rr_graph_out)[1].lower() if rr_graph_out_ext == ".xml": pass if rr_graph_out_ext == ".bin": # Check if we have Cap'n'proto schema provided if args.capnp_schema is None: print( "ERROR: Binary rr graph writeout requires Cap'n'proto schema") exit(-1) else: print("ERROR: Unsupported rr graph extension '{}'".format( rr_graph_out_ext)) exit(-1) # Read and parse the OpenFPGA arch XML file xml_tree = ET.parse(args.openfpga_arch_in, ET.XMLParser(remove_blank_text=True)) xml_openfpga_arch = xml_tree.getroot() assert xml_openfpga_arch is not None and \ xml_openfpga_arch.tag == "openfpga_architecture" # Get circuit models circuit_models = load_circuit_models(xml_openfpga_arch) # Load the routing graph print("Loading rr graph...") xml_graph = rr_xml.Graph(input_file_name=args.rr_graph_in, output_file_name=args.rr_graph_out, rebase_nodes=False, filter_nodes=False, load_edges=True, build_pin_edges=False, progressbar=progressbar_utils.progressbar) compute_rr_graph_stats(xml_graph.graph) # Load GSB data if args.gsb is not None: print("Loading GSB data...") gsb_data = load_gsb_data(args.gsb, progressbar_utils.progressbar) assert len(gsb_data), "No GSB data found!" # DEBUG total_mux_count = 0 conf_mux_count = 0 for loc, gsbs in gsb_data.items(): total_mux_count += len(gsbs) for gsb in gsbs: if len(gsb.drivers) > 1: conf_mux_count += 1 print(" {:>6} locs".format(len(gsb_data))) print(" {:>6} all muxes".format(total_mux_count)) print(" {:>6} configurable muxes".format(conf_mux_count)) # Identify routing muxes print("Identifying routing muxes...") routing_muxes = identify_routing_muxes(xml_graph.graph, gsb_data) # Build routing mux models print("Building routing muxes models...") routing_muxes_models = build_routing_muxes_models( circuit_models, routing_muxes) # Annotate edges print("Annotating rr graph edges...") annotate_edges(xml_graph.graph, routing_muxes) # Re-add all channel nodes as tracks and clear their PTCs. This allows # the Graph2 class code to re-assign PTCs which makes the graph usable # for SymbiFlow VPR. for i, node in enumerate(xml_graph.graph.nodes): # Check endpoint locations assert node.loc.x_low <= node.loc.x_high, node assert node.loc.y_low <= node.loc.y_high, node # Handle CHAN node if node.type in [rr.NodeType.CHANX, rr.NodeType.CHANY]: # Verify direction assert node.direction in [ rr.NodeDirection.INC_DIR, rr.NodeDirection.DEC_DIR, rr.NodeDirection.BI_DIR ], node # Named tuples to dicts node = node._asdict() loc = node["loc"]._asdict() # Clear PTC loc["ptc"] = None # Not sure why the segment id has its own NamedTuple object but # the Graph2 code requires it. node["segment"] = rr.NodeSegment(segment_id=node["segment"]) # Dicts to named tuples loc = rr.NodeLoc(**loc) node["loc"] = loc node = rr.Node(**node) # Replace the node xml_graph.graph.nodes[i] = node # Add it as a track xml_graph.graph.tracks.append(node.id) # Handle non-CHAN node else: # Verify direction assert node.direction == rr.NodeDirection.NO_DIR, node # Set direction to None node = node._asdict() node["direction"] = None node = rr.Node(**node) # Replace the node xml_graph.graph.nodes[i] = node # Create channels from tracks channels_obj = xml_graph.graph.create_channels(pad_segment=0) # Remove padding channels print("Removing padding nodes...") xml_graph.graph.nodes = [ n for n in xml_graph.graph.nodes if n.capacity > 0 ] # Write the routing graph nodes_obj = xml_graph.graph.nodes edges_obj = xml_graph.graph.edges node_remap = lambda x: x print("Serializing the rr graph...") if rr_graph_out_ext == ".xml": xml_graph.serialize_to_xml( channels_obj=channels_obj, nodes_obj=nodes_obj, edges_obj=yield_edges(edges_obj), node_remap=node_remap, ) elif rr_graph_out_ext == ".bin": # Build the writer writer = BinaryGraphWriter( graph=xml_graph.graph, root_attrib=xml_graph.root_attrib, capnp_schema_file_name=args.capnp_schema, output_file_name=args.rr_graph_out, progressbar=progressbar_utils.progressbar, ) # Write writer.serialize_to_capnp( channels_obj=channels_obj, num_nodes=len(nodes_obj), nodes_obj=nodes_obj, num_edges=len(edges_obj), edges_obj=yield_edges(edges_obj), ) else: assert False, rr_graph_out_ext
def graph_from_xml(input_file_name, progressbar=None, filter_nodes=True, load_edges=False): """ Loads relevant information about the routing resource graph from an XML file. """ if progressbar is None: progressbar = lambda x: x # noqa: E731 root_attrib = {} switches = [] segments = [] block_types = [] grid = [] nodes = [] edges = [] # Itertate over XML elements switch_timing = None switch_sizing = None segment_timing = None pins = [] pin_classes = [] node_loc = None node_timing = None node_segment = None for path, element in progressbar( iterate_xml(input_file_name, load_edges=load_edges)): # Root tag if path == "" and element.tag == "rr_graph": root_attrib = dict(element.attrib) # Switch timing if path == "rr_graph/switches/switch" and element.tag == "timing": switch_timing = graph2.SwitchTiming( r=float(element.attrib.get('R', 0)), c_in=float(element.attrib.get('Cin', 0)), c_out=float(element.attrib.get('Cout', 0)), c_internal=float(element.attrib.get('Cinternal', 0)), t_del=float(element.attrib.get('Tdel', 0)), ) # Switch sizing if path == "rr_graph/switches/switch" and element.tag == "sizing": switch_sizing = graph2.SwitchSizing( mux_trans_size=float(element.attrib['mux_trans_size']), buf_size=float(element.attrib['buf_size']), ) # Switch if path == "rr_graph/switches" and element.tag == "switch": switches.append( graph2.Switch( id=int(element.attrib['id']), type=enum_from_string(graph2.SwitchType, element.attrib['type']), name=element.attrib['name'], timing=switch_timing, sizing=switch_sizing, )) switch_timing = None switch_sizing = None # Segment timing if path == "rr_graph/segments/segment" and element.tag == "timing": segment_timing = graph2.SegmentTiming( r_per_meter=float(element.attrib.get('R_per_meter', 0)), c_per_meter=float(element.attrib.get('C_per_meter', 0)), ) # Segment if path == "rr_graph/segments" and element.tag == "segment": segments.append( graph2.Segment( id=int(element.attrib['id']), name=element.attrib['name'], timing=segment_timing, )) segment_timing = None # Block type - pin if path == "rr_graph/block_types/block_type/pin_class" and element.tag == "pin": pins.append( graph2.Pin( ptc=int(element.attrib['ptc']), name=element.text, )) # Block type - pin_class if path == "rr_graph/block_types/block_type" and element.tag == "pin_class": pin_classes.append( graph2.PinClass( type=enum_from_string(graph2.PinType, element.attrib['type']), pin=pins, )) pins = [] # Block type if path == "rr_graph/block_types" and element.tag == "block_type": block_types.append( graph2.BlockType( id=int(element.attrib['id']), name=element.attrib['name'], width=int(element.attrib['width']), height=int(element.attrib['height']), pin_class=pin_classes, )) pin_classes = [] # Grid if path == "rr_graph/grid" and element.tag == "grid_loc": grid.append( graph2.GridLoc( x=int(element.attrib['x']), y=int(element.attrib['y']), block_type_id=int(element.attrib['block_type_id']), width_offset=int(element.attrib['width_offset']), height_offset=int(element.attrib['height_offset']), )) # Node - loc if path == "rr_graph/rr_nodes/node" and element.tag == "loc": if 'side' in element.attrib: side = enum_from_string(tracks.Direction, element.attrib['side']) else: side = None node_loc = graph2.NodeLoc(x_low=int(element.attrib['xlow']), y_low=int(element.attrib['ylow']), x_high=int(element.attrib['xhigh']), y_high=int(element.attrib['yhigh']), ptc=int(element.attrib['ptc']), side=side) # Node - timing if path == "rr_graph/rr_nodes/node" and element.tag == "timing": node_timing = graph2.NodeTiming( r=float(element.attrib['R']), c=float(element.attrib['C']), ) # Node - segment if path == "rr_graph/rr_nodes/node" and element.tag == "segment": node_segment = int(element.attrib['segment_id']) # Node if path == "rr_graph/rr_nodes" and element.tag == "node": node_type = enum_from_string(graph2.NodeType, element.attrib['type']) if filter_nodes and node_type not in [ graph2.NodeType.SOURCE, graph2.NodeType.SINK, graph2.NodeType.OPIN, graph2.NodeType.IPIN ]: continue # Dropping metadata for now metadata = None nodes.append( graph2.Node( id=int(element.attrib['id']), type=node_type, direction=graph2.NodeDirection.NO_DIR, capacity=int(element.attrib['capacity']), loc=node_loc, timing=node_timing, metadata=metadata, segment=node_segment, canonical_loc=None, connection_box=None, )) node_loc = None node_timing = None node_segment = None # Edge if path == "rr_graph/rr_edges" and element.tag == "edge": if load_edges: edges.append( graph2.Edge( src_node=int(element.attrib['src_node']), sink_node=int(element.attrib['sink_node']), switch_id=int(element.attrib['switch_id']), metadata=None # FIXME: Add reading edge metadata )) return dict(root_attrib=root_attrib, switches=switches, segments=segments, block_types=block_types, grid=grid, nodes=nodes, edges=edges)