| import argparse | |
| import prjxray.db | |
| from prjxray.roi import Roi | |
| import simplejson as json | |
| from prjxray_db_cache import DatabaseCache | |
| def map_tile_to_vpr_coord(conn, tile): | |
| """ Converts prjxray tile name into VPR tile coordinates. | |
| It is assumed that this tile should only have one mapped tile. | |
| """ | |
| c = conn.cursor() | |
| c.execute("SELECT pkey FROM phy_tile WHERE name = ?;", (tile, )) | |
| phy_tile_pkey = c.fetchone()[0] | |
| # Filters NULL tiles to prevent selecting two tiles that point | |
| # to the same phy_tile | |
| c.execute( | |
| """ | |
| SELECT tile_map.tile_pkey FROM tile_map INNER JOIN tile | |
| ON tile_map.tile_pkey = tile.pkey INNER JOIN tile_type | |
| ON tile.tile_type_pkey = tile_type.pkey | |
| WHERE tile_map.phy_tile_pkey = ? AND tile_type.name != 'NULL' | |
| """, (phy_tile_pkey, ) | |
| ) | |
| mapped_tiles = c.fetchall() | |
| assert len(mapped_tiles) == 1, tile | |
| tile_pkey = mapped_tiles[0][0] | |
| c.execute("SELECT grid_x, grid_y FROM tile WHERE pkey = ?", (tile_pkey, )) | |
| grid_x, grid_y = c.fetchone() | |
| return grid_x, grid_y | |
| def tile_in_roi(conn, g, roi, tile_pkey): | |
| """ | |
| Checks if the given tile_pkey is at a location within the specified roi | |
| """ | |
| c = conn.cursor() | |
| c.execute( | |
| """ | |
| SELECT name FROM phy_tile WHERE pkey = | |
| (SELECT phy_tile_pkey FROM tile WHERE pkey = ?) | |
| """, (tile_pkey, ) | |
| ) | |
| tile, = c.fetchone() | |
| loc = g.loc_of_tilename(tile) | |
| return roi.tile_in_roi(loc) | |
| def wire_in_roi(conn, g, roi, wire_pkey): | |
| """ | |
| Checks if the given wire_pkey is at a location within the specified roi | |
| """ | |
| c = conn.cursor() | |
| c.execute(""" | |
| SELECT tile_pkey FROM wire WHERE pkey = ? | |
| """, (wire_pkey, )) | |
| tile_pkey, = c.fetchone() | |
| return tile_in_roi(conn, g, roi, tile_pkey) | |
| def wire_manhattan_distance(conn, wire_pkey1, wire_pkey2): | |
| """ | |
| Determines the manhattan distance between two tiles containing | |
| the given wire_pkeys | |
| """ | |
| c = conn.cursor() | |
| c.execute( | |
| """ | |
| SELECT grid_x, grid_y FROM tile WHERE pkey = (SELECT tile_pkey FROM wire WHERE pkey = ?) | |
| """, (wire_pkey1, ) | |
| ) | |
| x1, y1 = c.fetchone() | |
| c.execute( | |
| """ | |
| SELECT grid_x, grid_y FROM tile WHERE pkey = (SELECT tile_pkey FROM wire WHERE pkey = ?) | |
| """, (wire_pkey2, ) | |
| ) | |
| x2, y2 = c.fetchone() | |
| return abs(x1 - x2) + abs(y1 - y2) | |
| def find_wire_from_node(conn, g, roi, node_name, overlay=False): | |
| """ | |
| Finds a pair on wires in the given node such that: | |
| 1. One wire is inside the roi and the other is outside | |
| 2. The manhattan distance between these the wires is the minimum of any such pair | |
| Returns the wire from this pair that is outside the roi and the tile the returned | |
| wire is contained in. | |
| """ | |
| tile, node = node_name.split('/') | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT pkey, node_pkey FROM wire WHERE | |
| wire_in_tile_pkey IN (SELECT pkey FROM wire_in_tile WHERE name = ?) | |
| AND | |
| phy_tile_pkey = (SELECT pkey FROM phy_tile WHERE name = ?) | |
| """, (node, tile) | |
| ) | |
| results = cur.fetchall() | |
| assert len(results) == 1 | |
| wire_pkey, node_pkey = results[0] | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM wire WHERE node_pkey = ? | |
| """, (node_pkey, ) | |
| ) | |
| wire_pkeys = cur.fetchall() | |
| in_outs = { | |
| w: (overlay ^ wire_in_roi(conn, g, roi, w)) | |
| for w, in wire_pkeys | |
| } | |
| ins = {i for i, v in in_outs.items() if v} | |
| outs = {i for i, v in in_outs.items() if not v} | |
| min_manhattan_dist = 1000000 | |
| correct_wire = None | |
| for i in ins: | |
| for j in outs: | |
| d = wire_manhattan_distance(conn, i, j) | |
| if d < min_manhattan_dist: | |
| min_manhattan_dist = d | |
| correct_wire = j | |
| assert correct_wire is not None, node_name | |
| cur.execute( | |
| """ | |
| SELECT node_pkey, phy_tile_pkey, wire_in_tile_pkey FROM wire WHERE pkey = ? | |
| """, (correct_wire, ) | |
| ) | |
| node_pkey_correct_wire, phy_tile_pkey, wire_in_tile_pkey = cur.fetchone() | |
| cur.execute( | |
| """ | |
| SELECT name, tile_type_pkey FROM wire_in_tile WHERE pkey = ? | |
| """, (wire_in_tile_pkey, ) | |
| ) | |
| wire, tile_type_pkey = cur.fetchone() | |
| cur.execute( | |
| """ | |
| SELECT name FROM tile_type WHERE pkey = ? | |
| """, (tile_type_pkey, ) | |
| ) | |
| tile_type, = cur.fetchone() | |
| cur.execute( | |
| """ | |
| SELECT name FROM phy_tile WHERE pkey = ? | |
| """, (phy_tile_pkey, ) | |
| ) | |
| tile, = cur.fetchone() | |
| return tile, wire | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Generate synth_tiles.json") | |
| parser.add_argument('--db_root', required=True) | |
| parser.add_argument('--part', required=True) | |
| parser.add_argument('--roi', required=False) | |
| parser.add_argument('--overlay', required=False) | |
| parser.add_argument( | |
| '--connection_database', help='Connection database', required=True | |
| ) | |
| parser.add_argument('--synth_tiles', required=True) | |
| args = parser.parse_args() | |
| db = prjxray.db.Database(args.db_root, args.part) | |
| g = db.grid() | |
| synth_tiles = {} | |
| synth_tiles['tiles'] = {} | |
| rois = dict() | |
| if args.roi: | |
| with open(args.roi) as f: | |
| j = json.load(f) | |
| synth_tiles['info'] = j['info'] | |
| 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'], | |
| ) | |
| rois[roi] = j | |
| elif args.overlay: | |
| with open(args.overlay) as f: | |
| j = json.load(f) | |
| synth_tiles['info'] = list() | |
| for r in j: | |
| roi = Roi( | |
| db=db, | |
| x1=r['info']['GRID_X_MIN'], | |
| y1=r['info']['GRID_Y_MIN'], | |
| x2=r['info']['GRID_X_MAX'], | |
| y2=r['info']['GRID_Y_MAX'], | |
| ) | |
| rois[roi] = r | |
| else: | |
| assert False, 'Synth tiles must be for roi or overlay' | |
| with DatabaseCache(args.connection_database, read_only=True) as conn: | |
| tile_in_use = set() | |
| num_synth_tiles = 0 | |
| for roi, j in rois.items(): | |
| if args.overlay: | |
| synth_tiles['info'].append(j['info']) | |
| tile_pin_count = dict() | |
| for port in sorted(sorted(j['ports'], key=lambda i: i['name']), | |
| key=lambda i: i['type'], reverse=bool( | |
| args.overlay)): | |
| if port['type'] == 'out': | |
| port_type = 'input' if not args.overlay else 'output' | |
| is_clock = False | |
| elif port['type'] == 'in': | |
| is_clock = False | |
| port_type = 'output' if not args.overlay else 'input' | |
| elif port['type'] == 'clk': | |
| port_type = 'output' if not args.overlay else 'input' | |
| is_clock = True | |
| else: | |
| assert False, port | |
| if 'wire' not in port: | |
| tile, wire = find_wire_from_node( | |
| conn, g, roi, port['node'], overlay=bool(args.overlay) | |
| ) | |
| else: | |
| tile, wire = port['wire'].split('/') | |
| tile_in_use.add(tile) | |
| # Make sure connecting wire is not in ROI! | |
| loc = g.loc_of_tilename(tile) | |
| if bool(args.overlay) ^ roi.tile_in_roi(loc): | |
| # Or if in the ROI, make sure it has no sites. | |
| gridinfo = g.gridinfo_at_tilename(tile) | |
| assert len( | |
| db.get_tile_type(gridinfo.tile_type).get_sites() | |
| ) == 0, "{}/{}".format(tile, wire) | |
| vpr_loc = map_tile_to_vpr_coord(conn, tile) | |
| if tile not in synth_tiles['tiles']: | |
| tile_name = 'SYN-IOPAD-{}'.format(num_synth_tiles) | |
| synth_tiles['tiles'][tile] = { | |
| 'pins': [], | |
| 'loc': vpr_loc, | |
| 'tile_name': tile_name, | |
| 'wires_outside_roi': {}, | |
| } | |
| num_synth_tiles += 1 | |
| tile_pin_count[tile] = 0 | |
| synth_tiles['tiles'][tile]['pins'].append( | |
| { | |
| 'roi_name': | |
| port['name'].replace('[', '_').replace(']', '_'), | |
| 'wire': | |
| wire, | |
| 'pad': | |
| port['pin'], | |
| 'port_type': | |
| port_type, | |
| 'is_clock': | |
| is_clock, | |
| 'z_loc': | |
| tile_pin_count[tile], | |
| } | |
| ) | |
| if 'wires_outside_roi' in port: | |
| outside_roi = synth_tiles['tiles'][tile | |
| ]['wires_outside_roi'] | |
| for tile_wire in port['wires_outside_roi']: | |
| tile_name, wire_name = tile_wire.split('/') | |
| if tile_name in outside_roi.keys(): | |
| outside_roi[tile_name].append(wire_name) | |
| else: | |
| outside_roi[tile_name] = [wire_name] | |
| tile_pin_count[tile] += 1 | |
| if not args.overlay: | |
| # Find two VBRK's in the corner of the fabric to use as the synthetic VCC/ | |
| # GND source. | |
| vbrk_loc = None | |
| vbrk_tile = None | |
| vbrk2_loc = None | |
| vbrk2_tile = None | |
| for tile in g.tiles(): | |
| if tile in tile_in_use: | |
| continue | |
| loc = g.loc_of_tilename(tile) | |
| if not roi.tile_in_roi(loc): | |
| continue | |
| gridinfo = g.gridinfo_at_tilename(tile) | |
| if 'VBRK' not in gridinfo.tile_type: | |
| continue | |
| assert len( | |
| db.get_tile_type(gridinfo.tile_type).get_sites() | |
| ) == 0, tile | |
| if vbrk_loc is None: | |
| vbrk2_loc = vbrk_loc | |
| vbrk2_tile = vbrk_tile | |
| vbrk_loc = loc | |
| vbrk_tile = tile | |
| else: | |
| if (loc.grid_x < vbrk_loc.grid_x and | |
| loc.grid_y < vbrk_loc.grid_y) or vbrk2_loc is None: | |
| vbrk2_loc = vbrk_loc | |
| vbrk2_tile = vbrk_tile | |
| vbrk_loc = loc | |
| vbrk_tile = tile | |
| assert vbrk_loc is not None | |
| assert vbrk_tile is not None | |
| assert vbrk_tile not in synth_tiles['tiles'] | |
| vbrk_vpr_loc = map_tile_to_vpr_coord(conn, vbrk_tile) | |
| synth_tiles['tiles'][vbrk_tile] = { | |
| 'loc': | |
| vbrk_vpr_loc, | |
| 'pins': | |
| [ | |
| { | |
| 'wire': 'VCC', | |
| 'pad': 'VCC', | |
| 'port_type': 'VCC', | |
| 'is_clock': False, | |
| 'z_loc': '0', | |
| }, | |
| ], | |
| } | |
| assert vbrk2_loc is not None | |
| assert vbrk2_tile is not None | |
| assert vbrk2_tile not in synth_tiles['tiles'] | |
| vbrk2_vpr_loc = map_tile_to_vpr_coord(conn, vbrk2_tile) | |
| synth_tiles['tiles'][vbrk2_tile] = { | |
| 'loc': | |
| vbrk2_vpr_loc, | |
| 'pins': | |
| [ | |
| { | |
| 'wire': 'GND', | |
| 'pad': 'GND', | |
| 'port_type': 'GND', | |
| 'is_clock': False, | |
| 'z_loc': '0', | |
| }, | |
| ], | |
| } | |
| with open(args.synth_tiles, 'w') as f: | |
| json.dump(synth_tiles, f, indent=2) | |
| if __name__ == "__main__": | |
| main() |