def test_in_roi_overlay(self): region_dict = {} region_dict['pr1'] = (10, 58, 0, 51) region_dict['pr2'] = (10, 58, 52, 103) overlay = Overlay(region_dict) self.assertFalse(overlay.tile_in_roi(GridLoc(18, 50))) self.assertFalse(overlay.tile_in_roi(GridLoc(18, 84))) self.assertTrue(overlay.tile_in_roi(GridLoc(8, 50))) self.assertTrue(overlay.tile_in_roi(GridLoc(18, 112))) self.assertTrue(overlay.tile_in_roi(GridLoc(80, 40)))
def main(): parser = argparse.ArgumentParser() parser.add_argument('--db_root', required=True, help='Project X-Ray Database') parser.add_argument('--part', required=True, help='FPGA part') 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('--overlay', action='store_true', required=False, help='Use synth tiles for Overlay instead of ROI') parser.add_argument( '--graph_limit', help='Limit grid to specified dimensions in x_min,y_min,x_max,y_max', ) parser.add_argument( '--vpr_capnp_schema_dir', help='Directory container VPR schema files', ) print('{} Starting routing import'.format(now())) args = parser.parse_args() db = prjxray.db.Database(args.db_root, args.part) populate_hclk_cmt_tiles(db) synth_tiles = None if args.overlay: assert args.synth_tiles use_roi = True with open(args.synth_tiles) as f: synth_tiles = json.load(f) region_dict = dict() for r in synth_tiles['info']: bounds = (r['GRID_X_MIN'], r['GRID_X_MAX'], r['GRID_Y_MIN'], r['GRID_Y_MAX']) region_dict[r['name']] = bounds roi = Overlay(region_dict=region_dict) print('{} generating routing graph for Overlay.'.format(now())) elif 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 capnp_graph = capnp_graph2.Graph( rr_graph_schema_fname=os.path.join(args.vpr_capnp_schema_dir, 'rr_graph_uxsdcxx.capnp'), input_file_name=args.read_rr_graph, progressbar=progressbar_utils.progressbar, output_file_name=args.write_rr_graph, ) graph = capnp_graph.graph if synth_tiles is None: synth_tiles = find_constant_network(graph) if args.overlay: synth_tiles_const = find_constant_network(graph) synth_tiles['tiles'].update(synth_tiles_const['tiles']) with sqlite3.connect("file:{}?mode=ro".format(args.connection_database), uri=True) as conn: populate_bufg_rebuf_map(conn) cur = conn.cursor() for name, internal_capacitance, drive_resistance, intrinsic_delay, penalty_cost, \ switch_type in cur.execute(""" SELECT name, internal_capacitance, drive_resistance, intrinsic_delay, penalty_cost, 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: capnp_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, p_cost=penalty_cost, ), 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. print('{} Adding synthetic edges'.format(now())) add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles, args.overlay) print('{} Creating channels.'.format(now())) channels_obj = create_channels(conn) node_remap = create_node_remap(capnp_graph.graph.nodes, channels_obj) x_dim, y_dim = phy_grid_dims(conn) connection_box_obj = graph.create_connection_box_object(x_dim=x_dim, y_dim=y_dim) num_edges = get_number_graph_edges(conn, graph, node_mapping) print('{} Serializing to disk.'.format(now())) capnp_graph.serialize_to_capnp( channels_obj=channels_obj, connection_box_obj=connection_box_obj, num_nodes=len(capnp_graph.graph.nodes), nodes_obj=yield_nodes(capnp_graph.graph.nodes), num_edges=num_edges, edges_obj=import_graph_edges(conn, graph, node_mapping), node_remap=node_remap, ) for k in node_mapping: node_mapping[k] = node_remap(node_mapping[k]) 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(description="Generate arch.xml") parser.add_argument( '--db_root', required=True, help="Project X-Ray database to use." ) parser.add_argument('--part', required=True, help="FPGA part") parser.add_argument( '--output-arch', nargs='?', type=argparse.FileType('w'), help="""File to output arch.""" ) parser.add_argument( '--tile-types', required=True, help="Semi-colon seperated tile types." ) parser.add_argument( '--pb_types', required=True, help="Semi-colon seperated pb_types types." ) parser.add_argument( '--pin_assignments', required=True, type=argparse.FileType('r') ) parser.add_argument('--use_roi', required=False) parser.add_argument('--use_overlay', required=False) parser.add_argument('--device', required=True) parser.add_argument('--synth_tiles', required=False) parser.add_argument('--connection_database', required=True) parser.add_argument( '--graph_limit', help='Limit grid to specified dimensions in x_min,y_min,x_max,y_max', ) args = parser.parse_args() tile_types = args.tile_types.split(',') pb_types = args.pb_types.split(',') model_xml_spec = "../../tiles/{0}/{0}.model.xml" pbtype_xml_spec = "../../tiles/{0}/{0}.pb_type.xml" tile_xml_spec = "../../tiles/{0}/{0}.tile.xml" xi_url = "http://www.w3.org/2001/XInclude" ET.register_namespace('xi', xi_url) xi_include = "{%s}include" % xi_url arch_xml = ET.Element( 'architecture', {}, nsmap={'xi': xi_url}, ) model_xml = ET.SubElement(arch_xml, 'models') for pb_type in pb_types: ET.SubElement( model_xml, xi_include, { 'href': model_xml_spec.format(pb_type.lower()), 'xpointer': "xpointer(models/child::node())", } ) tiles_xml = ET.SubElement(arch_xml, 'tiles') tile_capacity = {} for tile_type in tile_types: uri = tile_xml_spec.format(tile_type.lower()) ET.SubElement(tiles_xml, xi_include, { 'href': uri, }) with open(uri) as f: tile_xml = ET.parse(f, ET.XMLParser()) tile_root = tile_xml.getroot() assert tile_root.tag == 'tile' tile_capacity[tile_type] = 0 for sub_tile in tile_root.iter('sub_tile'): if 'capacity' in sub_tile.attrib: tile_capacity[tile_type] += int( sub_tile.attrib['capacity'] ) else: tile_capacity[tile_type] += 1 complexblocklist_xml = ET.SubElement(arch_xml, 'complexblocklist') for pb_type in pb_types: ET.SubElement( complexblocklist_xml, xi_include, { 'href': pbtype_xml_spec.format(pb_type.lower()), } ) layout_xml = ET.SubElement(arch_xml, 'layout') db = prjxray.db.Database(args.db_root, args.part) g = db.grid() synth_tiles = {} synth_tiles['tiles'] = {} synth_loc_map = {} synth_tile_map = {} roi = None if args.use_roi: with open(args.use_roi) as f: j = json.load(f) with open(args.synth_tiles) as f: synth_tiles = json.load(f) roi = Roi( db=db, x1=j['info']['GRID_X_MIN'], y1=j['info']['GRID_Y_MIN'], x2=j['info']['GRID_X_MAX'], y2=j['info']['GRID_Y_MAX'], ) for _, tile_info in synth_tiles['tiles'].items(): if tile_info['pins'][0]['port_type'] in ['GND', 'VCC']: continue assert tuple(tile_info['loc']) not in synth_loc_map tile_name = tile_info['tile_name'] num_input = len( list( filter( lambda t: t['port_type'] == 'output', tile_info['pins'] ) ) ) num_output = len( list( filter( lambda t: t['port_type'] == 'input', tile_info['pins'] ) ) ) create_synth_io_tile( complexblocklist_xml, tiles_xml, tile_name, num_input, num_output ) synth_loc_map[tuple(tile_info['loc'])] = tile_name create_synth_pb_types(model_xml, complexblocklist_xml) synth_tile_map = add_constant_synthetic_tiles( model_xml, complexblocklist_xml, tiles_xml ) for _, tile_info in synth_tiles['tiles'].items(): if tile_info['pins'][0]['port_type'] not in ['GND', 'VCC']: continue assert tuple(tile_info['loc']) not in synth_loc_map vpr_tile_type = synth_tile_map[tile_info['pins'][0]['port_type']] synth_loc_map[tuple(tile_info['loc'])] = vpr_tile_type elif args.graph_limit: 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, ) elif args.use_overlay: with open(args.use_overlay) as f: j = json.load(f) with open(args.synth_tiles) as f: synth_tiles = json.load(f) region_dict = dict() for r in synth_tiles['info']: bounds = ( r['GRID_X_MIN'], r['GRID_X_MAX'], r['GRID_Y_MIN'], r['GRID_Y_MAX'] ) region_dict[r['name']] = bounds roi = Overlay(region_dict=region_dict) for _, tile_info in synth_tiles['tiles'].items(): if tile_info['pins'][0]['port_type'] in ['GND', 'VCC']: continue assert tuple(tile_info['loc']) not in synth_loc_map tile_name = tile_info['tile_name'] num_input = len( list( filter( lambda t: t['port_type'] == 'output', tile_info['pins'] ) ) ) num_output = len( list( filter( lambda t: t['port_type'] == 'input', tile_info['pins'] ) ) ) create_synth_io_tile( complexblocklist_xml, tiles_xml, tile_name, num_input, num_output ) synth_loc_map[tuple(tile_info['loc'])] = tile_name create_synth_pb_types(model_xml, complexblocklist_xml, True) with DatabaseCache(args.connection_database, read_only=True) as conn: c = conn.cursor() if 'GND' not in synth_tile_map: synth_tile_map, synth_loc_map_const = insert_constant_tiles( conn, model_xml, complexblocklist_xml, tiles_xml ) synth_loc_map.update(synth_loc_map_const) # Find the grid extent. y_max = 0 x_max = 0 for grid_x, grid_y in c.execute("SELECT grid_x, grid_y FROM tile"): x_max = max(grid_x + 2, x_max) y_max = max(grid_y + 2, y_max) name = '{}-test'.format(args.device) fixed_layout_xml = ET.SubElement( layout_xml, 'fixed_layout', { 'name': name, 'height': str(y_max), 'width': str(x_max), } ) for vpr_tile_type, grid_x, grid_y, metadata_function in get_tiles( conn=conn, g=g, roi=roi, synth_loc_map=synth_loc_map, synth_tile_map=synth_tile_map, tile_types=tile_types, tile_capacity=tile_capacity, ): single_xml = ET.SubElement( fixed_layout_xml, 'single', { 'priority': '1', 'type': vpr_tile_type, 'x': str(grid_x), 'y': str(grid_y), } ) metadata_function(single_xml) switchlist_xml = ET.SubElement(arch_xml, 'switchlist') for name, internal_capacitance, drive_resistance, intrinsic_delay, \ switch_type in c.execute(""" SELECT name, internal_capacitance, drive_resistance, intrinsic_delay, switch_type FROM switch WHERE name != "__vpr_delayless_switch__";"""): attrib = { 'type': switch_type, 'name': name, "R": str(drive_resistance), "Cin": str(0), "Cout": str(0), "Tdel": str(intrinsic_delay), } if internal_capacitance != 0: attrib["Cinternal"] = str(internal_capacitance) if False: attrib["mux_trans_size"] = str(0) attrib["buf_size"] = str(0) ET.SubElement(switchlist_xml, 'switch', attrib) segmentlist_xml = ET.SubElement(arch_xml, 'segmentlist') # VPR requires a segment, so add one. dummy_xml = ET.SubElement( segmentlist_xml, 'segment', { 'name': 'dummy', 'length': '2', 'freq': '1.0', 'type': 'bidir', 'Rmetal': '0', 'Cmetal': '0', } ) ET.SubElement(dummy_xml, 'wire_switch', { 'name': 'buffer', }) ET.SubElement(dummy_xml, 'opin_switch', { 'name': 'buffer', }) ET.SubElement(dummy_xml, 'sb', { 'type': 'pattern', }).text = ' '.join('1' for _ in range(3)) ET.SubElement(dummy_xml, 'cb', { 'type': 'pattern', }).text = ' '.join('1' for _ in range(2)) for (name, length) in c.execute("SELECT name, length FROM segment"): if length is None: length = 1 segment_xml = ET.SubElement( segmentlist_xml, 'segment', { 'name': name, 'length': str(length), 'freq': '1.0', 'type': 'bidir', 'Rmetal': '0', 'Cmetal': '0', } ) ET.SubElement(segment_xml, 'wire_switch', { 'name': 'buffer', }) ET.SubElement(segment_xml, 'opin_switch', { 'name': 'buffer', }) ET.SubElement(segment_xml, 'sb', { 'type': 'pattern', }).text = ' '.join('1' for _ in range(length + 1)) ET.SubElement(segment_xml, 'cb', { 'type': 'pattern', }).text = ' '.join('1' for _ in range(length)) ET.SubElement( switchlist_xml, 'switch', { 'type': 'mux', 'name': 'buffer', "R": "551", "Cin": ".77e-15", "Cout": "4e-15", # TODO: This value should be the "typical" pip switch delay from # This value is the dominate term in the inter-cluster delay # estimate. "Tdel": "0.178e-9", "mux_trans_size": "2.630740", "buf_size": "27.645901" } ) device_xml = ET.SubElement(arch_xml, 'device') ET.SubElement( device_xml, 'sizing', { "R_minW_nmos": "6065.520020", "R_minW_pmos": "18138.500000", } ) ET.SubElement(device_xml, 'area', { "grid_logic_tile_area": "14813.392", }) ET.SubElement( device_xml, 'connection_block', { "input_switch_name": "buffer", } ) ET.SubElement(device_xml, 'switch_block', { "type": "wilton", "fs": "3", }) chan_width_distr_xml = ET.SubElement(device_xml, 'chan_width_distr') ET.SubElement( chan_width_distr_xml, 'x', { 'distr': 'uniform', 'peak': '1.0', } ) ET.SubElement( chan_width_distr_xml, 'y', { 'distr': 'uniform', 'peak': '1.0', } ) directlist_xml = ET.SubElement(arch_xml, 'directlist') pin_assignments = json.load(args.pin_assignments) # Choose smallest distance for block to block connections with multiple # direct_connections. VPR cannot handle multiple block to block connections. directs = {} for direct in pin_assignments['direct_connections']: key = (direct['from_pin'], direct['to_pin']) if key not in directs: directs[key] = [] directs[key].append( (abs(direct['x_offset']) + abs(direct['y_offset']), direct) ) ALLOWED_ZERO_OFFSET_DIRECT = [ "GTP_CHANNEL_0", "GTP_CHANNEL_1", "GTP_CHANNEL_2", "GTP_CHANNEL_3", "GTP_CHANNEL_0_MID_LEFT", "GTP_CHANNEL_1_MID_LEFT", "GTP_CHANNEL_2_MID_LEFT", "GTP_CHANNEL_3_MID_LEFT", "GTP_CHANNEL_0_MID_RIGHT", "GTP_CHANNEL_1_MID_RIGHT", "GTP_CHANNEL_2_MID_RIGHT", "GTP_CHANNEL_3_MID_RIGHT", "GTP_COMMON_MID_LEFT", "GTP_COMMON_MID_RIGHT", ] zero_offset_directs = dict() for direct in directs.values(): _, direct = min(direct, key=lambda v: v[0]) from_tile = direct['from_pin'].split('.')[0] to_tile = direct['to_pin'].split('.')[0] if from_tile not in tile_types: continue if to_tile not in tile_types: continue # In general, the Z offset is 0, except for special cases # such as for the GTP tiles, where there are direct connections # within the same (x, y) cooredinates, but between different sub_tiles direct['z_offset'] = 0 if direct['x_offset'] == 0 and direct['y_offset'] == 0: if from_tile == to_tile and from_tile in ALLOWED_ZERO_OFFSET_DIRECT: if from_tile not in zero_offset_directs: zero_offset_directs[from_tile] = list() zero_offset_directs[from_tile].append(direct) continue add_direct(directlist_xml, direct) for tile, directs in zero_offset_directs.items(): uri = tile_xml_spec.format(tile.lower()) ports = list() with open(uri) as f: tile_xml = ET.parse(f, ET.XMLParser()) tile_root = tile_xml.getroot() for capacity, sub_tile in enumerate(tile_root.iter('sub_tile')): for in_port in sub_tile.iter('input'): ports.append((in_port.attrib["name"], capacity)) for out_port in sub_tile.iter('output'): ports.append((out_port.attrib["name"], capacity)) for clk_port in sub_tile.iter('clock'): ports.append((clk_port.attrib["name"], capacity)) for direct in directs: tile_type, from_port = direct['from_pin'].split('.') _, to_port = direct['to_pin'].split('.') if tile != tile_type: continue from_port_capacity = None to_port_capacity = None for port, capacity in ports: if port == from_port: from_port_capacity = capacity if port == to_port: to_port_capacity = capacity assert from_port_capacity is not None and to_port_capacity is not None, ( tile, from_port, to_port ) direct["z_offset"] = to_port_capacity - from_port_capacity add_direct(directlist_xml, direct) arch_xml_str = ET.tostring(arch_xml, pretty_print=True).decode('utf-8') args.output_arch.write(arch_xml_str) args.output_arch.close()