| #!/usr/bin/env python3 | |
| """ Imports 7-series routing fabric to the rr graph. | |
| For ROI configurations, this also connects the synthetic IO tiles to the routing | |
| node specified. | |
| Rough structure: | |
| Add rr_nodes for CHANX and CHANY from the database. IPIN and OPIN rr_nodes | |
| should already be present from the input rr_graph. | |
| Create a mapping between database graph_nodes and IPIN, OPIN, CHANX and CHANY | |
| rr_node ids in the rr_graph. | |
| Add rr_edge for each row in the graph_edge table. | |
| Import channel XML node from connection database and serialize output to | |
| rr_graph XML. | |
| """ | |
| import argparse | |
| import os.path | |
| from hilbertcurve.hilbertcurve import HilbertCurve | |
| import math | |
| import prjxray.db | |
| from prjxray.roi import Roi | |
| from prjxray.overlay import Overlay | |
| import prjxray.grid as grid | |
| from lib.rr_graph import graph2 | |
| from lib.rr_graph import tracks | |
| from lib.connection_database import get_wire_pkey, get_track_model | |
| import lib.rr_graph_capnp.graph2 as capnp_graph2 | |
| from prjxray_constant_site_pins import feature_when_routed | |
| from prjxray_tile_import import remove_vpr_tile_prefix | |
| import simplejson as json | |
| from lib import progressbar_utils | |
| import datetime | |
| import re | |
| import functools | |
| import pickle | |
| import sqlite3 | |
| now = datetime.datetime.now | |
| HCLK_CK_BUFHCLK_REGEX = re.compile('HCLK_CK_BUFHCLK[0-9]+') | |
| CLK_HROW_CK_MUX_REGEX = re.compile('CLK_HROW_CK_MUX_OUT_([LR])([0-9]+)') | |
| CASCOUT_REGEX = re.compile('BRAM_CASCOUT_ADDR((?:BWR)|(?:ARD))ADDRU([0-9]+)') | |
| CONNECTION_BOX_FILTER = re.compile('([^0-9]+)[0-9]*') | |
| BUFG_CLK_IN_REGEX = re.compile('CLK_HROW_CK_IN_[LR][0-9]+') | |
| BUFG_CLK_OUT_REGEX = re.compile('CLK_HROW_R_CK_GCLK[0-9]+') | |
| CCIO_ACTIVE_REGEX = re.compile('HCLK_CMT_CCIO[0-9]+') | |
| HCLK_OUT = re.compile('CLK_HROW_CK_HCLK_OUT_([LR])([0-9]+)') | |
| IOI_OCLK = re.compile('IOI_OCLK_([01])') | |
| # Regex for [LR]IOI_SING tiles | |
| IOI_SITE_PIPS = ['OLOGIC', 'ILOGIC', 'IDELAY', 'OCLK_', 'OCLKM_'] | |
| IOI_SING_REGEX = re.compile( | |
| r'([RL]IOI3_SING_X[0-9]+Y)([0-9]+)(\.IOI_)({})([01])(.*)'.format( | |
| "|".join(IOI_SITE_PIPS) | |
| ) | |
| ) | |
| class ExtraFeatures(): | |
| def __init__(self): | |
| self.wires_to_nodes = {} | |
| self.nodes_to_features = {} | |
| def extra_features(self, feature_path): | |
| if len(feature_path) != 3: | |
| return | |
| key = (feature_path[0], feature_path[1]) | |
| if key in self.wires_to_nodes: | |
| return self.nodes_to_features[self.wires_to_nodes[key]] | |
| def add_feature_to_wire_for_node(self, conn, wire_pkey, feature): | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT node_pkey FROM wire WHERE pkey = ?; | |
| """, (wire_pkey, ) | |
| ) | |
| (node_pkey, ) = cur.fetchone() | |
| if node_pkey not in self.nodes_to_features: | |
| self.nodes_to_features[node_pkey] = set() | |
| self.nodes_to_features[node_pkey].add(feature) | |
| for (tile_name, wire_name) in cur.execute(""" | |
| SELECT phy_tile.name, wire_in_tile.name | |
| FROM wire | |
| INNER JOIN wire_in_tile ON wire.wire_in_tile_pkey = wire_in_tile.pkey | |
| INNER JOIN phy_tile ON wire.phy_tile_pkey = phy_tile.pkey | |
| WHERE wire.node_pkey = ?; | |
| """, (node_pkey, )): | |
| self.wires_to_nodes[tile_name, wire_name] = node_pkey | |
| def populate_freq_bb_features(conn, extra_features): | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT wire.pkey, phy_tile.name, wire_in_tile.name | |
| FROM wire_in_tile | |
| INNER JOIN wire ON wire.wire_in_tile_pkey = wire_in_tile.pkey | |
| INNER JOIN phy_tile ON wire.phy_tile_pkey = phy_tile.pkey | |
| WHERE wire_in_tile.name LIKE "MMCM_CLK_FREQ_BB_NS%";""" | |
| ) | |
| for wire_pkey, tile_name, wire_name in cur: | |
| extra_features.add_feature_to_wire_for_node( | |
| conn, wire_pkey, '{}.{}_ACTIVE'.format(tile_name, wire_name) | |
| ) | |
| cur.execute( | |
| """ | |
| SELECT wire.pkey, phy_tile.name, wire_in_tile.name | |
| FROM wire_in_tile | |
| INNER JOIN wire ON wire.wire_in_tile_pkey = wire_in_tile.pkey | |
| INNER JOIN phy_tile ON wire.phy_tile_pkey = phy_tile.pkey | |
| WHERE wire_in_tile.name LIKE "PLL_CLK_FREQ_BB%_NS";""" | |
| ) | |
| for wire_pkey, tile_name, wire_name in cur: | |
| extra_features.add_feature_to_wire_for_node( | |
| conn, wire_pkey, '{}.{}_ACTIVE'.format(tile_name, wire_name) | |
| ) | |
| REBUF_NODES = {} | |
| REBUF_SOURCES = {} | |
| def get_clk_hrow_and_rebuf_tiles_sorted(cur): | |
| """ | |
| Finds all CLK_HROW_TOP_R, CLK_HROW_BOT_T and REBUF tiles. | |
| returns them in a list sorted according to their Y coordinates. | |
| """ | |
| cur.execute( | |
| """ | |
| SELECT name | |
| FROM phy_tile | |
| WHERE | |
| name LIKE "CLK_HROW_BOT_R_%" | |
| OR | |
| name LIKE "CLK_HROW_TOP_R_%" | |
| OR | |
| name LIKE "CLK_BUFG_REBUF_%" | |
| ORDER BY grid_y DESC; | |
| """ | |
| ) | |
| return [t[0] for t in cur.fetchall()] | |
| def populate_bufg_rebuf_map(conn): | |
| global REBUF_NODES | |
| REBUF_NODES = {} | |
| global REBUF_SOURCES | |
| REBUF_SOURCES = {} | |
| rebuf_wire_regexp = re.compile( | |
| 'CLK_BUFG_REBUF_R_CK_GCLK([0-9]+)_(BOT|TOP)' | |
| ) | |
| cur = conn.cursor() | |
| # Find CLK_HROW_TOP_R, CLK_HROW_TOP_R and REBUF tiles. | |
| rebuf_and_hrow_tiles = get_clk_hrow_and_rebuf_tiles_sorted(cur) | |
| # Append None on both ends of the list to simplify the code below. | |
| rebuf_and_hrow_tiles = [None] + rebuf_and_hrow_tiles + [None] | |
| def maybe_get_clk_hrow(i): | |
| """ | |
| Returns a name of CLK_HROW tile only if its there on the list. | |
| """ | |
| tile = rebuf_and_hrow_tiles[i] | |
| if tile is not None and tile.startswith("CLK_HROW"): | |
| return tile | |
| return None | |
| # Assign each REBUF tile its above and below CLK_HROW tile. Note that in | |
| # VPR coords terms. "above" and "below" mean the opposite... | |
| rebuf_to_hrow_map = {} | |
| for i, tile_name in enumerate(rebuf_and_hrow_tiles): | |
| if tile_name is not None and tile_name.startswith("CLK_BUFG_REBUF"): | |
| rebuf_to_hrow_map[tile_name] = { | |
| "above": maybe_get_clk_hrow(i - 1), | |
| "below": maybe_get_clk_hrow(i + 1), | |
| } | |
| # Find nodes touching rebuf wires. | |
| cur.execute( | |
| """ | |
| WITH | |
| rebuf_wires(wire_in_tile_pkey) AS ( | |
| SELECT pkey | |
| FROM wire_in_tile | |
| WHERE | |
| name LIKE "CLK_BUFG_REBUF_R_CK_GCLK%_BOT" | |
| OR | |
| name LIKE "CLK_BUFG_REBUF_R_CK_GCLK%_TOP" | |
| ), | |
| rebuf_nodes(node_pkey) AS ( | |
| SELECT DISTINCT node_pkey | |
| FROM wire | |
| WHERE wire_in_tile_pkey IN (SELECT wire_in_tile_pkey FROM rebuf_wires) | |
| ) | |
| SELECT rebuf_nodes.node_pkey, phy_tile.name, wire_in_tile.name | |
| FROM rebuf_nodes | |
| INNER JOIN wire ON wire.node_pkey = rebuf_nodes.node_pkey | |
| INNER JOIN wire_in_tile ON wire_in_tile.pkey = wire.wire_in_tile_pkey | |
| INNER JOIN phy_tile ON phy_tile.pkey = wire.phy_tile_pkey | |
| WHERE wire.wire_in_tile_pkey IN (SELECT wire_in_tile_pkey FROM rebuf_wires) | |
| ORDER BY rebuf_nodes.node_pkey;""" | |
| ) | |
| for node_pkey, rebuf_tile, rebuf_wire_name in cur: | |
| if node_pkey not in REBUF_NODES: | |
| REBUF_NODES[node_pkey] = [] | |
| m = rebuf_wire_regexp.fullmatch(rebuf_wire_name) | |
| if m.group(2) == 'TOP': | |
| REBUF_NODES[node_pkey].append( | |
| '{}.GCLK{}_ENABLE_BELOW'.format(rebuf_tile, m.group(1)) | |
| ) | |
| hrow_tile = rebuf_to_hrow_map[rebuf_tile]["below"] | |
| if hrow_tile is not None: | |
| REBUF_NODES[node_pkey].append( | |
| "{}.CLK_HROW_R_CK_GCLK{}_ACTIVE".format( | |
| hrow_tile, m.group(1) | |
| ) | |
| ) | |
| elif m.group(2) == 'BOT': | |
| REBUF_NODES[node_pkey].append( | |
| '{}.GCLK{}_ENABLE_ABOVE'.format(rebuf_tile, m.group(1)) | |
| ) | |
| hrow_tile = rebuf_to_hrow_map[rebuf_tile]["above"] | |
| if hrow_tile is not None: | |
| REBUF_NODES[node_pkey].append( | |
| "{}.CLK_HROW_R_CK_GCLK{}_ACTIVE".format( | |
| hrow_tile, m.group(1) | |
| ) | |
| ) | |
| else: | |
| assert False, (rebuf_tile, rebuf_wire_name) | |
| for node_pkey in REBUF_NODES: | |
| cur.execute( | |
| """ | |
| SELECT phy_tile.name, wire_in_tile.name | |
| FROM wire | |
| INNER JOIN phy_tile ON phy_tile.pkey = wire.phy_tile_pkey | |
| INNER JOIN wire_in_tile ON wire_in_tile.pkey = wire.wire_in_tile_pkey | |
| WHERE wire.node_pkey = ?;""", (node_pkey, ) | |
| ) | |
| for tile, wire_name in cur: | |
| REBUF_SOURCES[(tile, wire_name)] = node_pkey | |
| HCLK_CMT_TILES = {} | |
| def populate_hclk_cmt_tiles(db): | |
| global HCLK_CMT_TILES | |
| HCLK_CMT_TILES = {} | |
| grid = db.grid() | |
| _, x_max, _, _ = grid.dims() | |
| for tile in grid.tiles(): | |
| gridinfo = grid.gridinfo_at_tilename(tile) | |
| if gridinfo.tile_type not in ['CLK_HROW_BOT_R', 'CLK_HROW_TOP_R']: | |
| continue | |
| hclk_x, hclk_y = grid.loc_of_tilename(tile) | |
| hclk_cmt_x = hclk_x | |
| hclk_cmt_y = hclk_y | |
| while hclk_cmt_x > 0: | |
| hclk_cmt_x -= 1 | |
| gridinfo = grid.gridinfo_at_loc((hclk_cmt_x, hclk_cmt_y)) | |
| if gridinfo.tile_type == 'HCLK_CMT': | |
| HCLK_CMT_TILES[tile, 'L'] = grid.tilename_at_loc( | |
| (hclk_cmt_x, hclk_cmt_y) | |
| ) | |
| break | |
| hclk_cmt_x = hclk_x | |
| while hclk_cmt_x < x_max: | |
| hclk_cmt_x += 1 | |
| gridinfo = grid.gridinfo_at_loc((hclk_cmt_x, hclk_cmt_y)) | |
| if gridinfo.tile_type == 'HCLK_CMT_L': | |
| HCLK_CMT_TILES[tile, 'R'] = grid.tilename_at_loc( | |
| (hclk_cmt_x, hclk_cmt_y) | |
| ) | |
| break | |
| def find_hclk_cmt_hclk_feature(hclk_tile, lr, hclk_number): | |
| if (hclk_tile, lr) not in HCLK_CMT_TILES: | |
| return [] | |
| hclk_cmt_tile = HCLK_CMT_TILES[(hclk_tile, lr)] | |
| return ['{}.HCLK_CMT_CK_BUFHCLK{}_USED'.format(hclk_cmt_tile, hclk_number)] | |
| def check_feature(extra_features, feature): | |
| """ Check if enabling this feature requires other features to be enabled. | |
| Some pips imply other features. Example: | |
| .HCLK_LEAF_CLK_B_BOTL0.HCLK_CK_BUFHCLK10 | |
| implies: | |
| .ENABLE_BUFFER.HCLK_CK_BUFHCLK10 | |
| """ | |
| # IOI_SING tiles have bits in common with the IOI tiles. | |
| # | |
| # The difference is that the TOP IOI_SING tile shares bits with | |
| # the bottom half of a normal IOI tile, while the BOTTOM IOI_SING | |
| # shares bits with the top half of a normal IOI TILE. | |
| # | |
| # The following, is to change the edge feature to accomodate this | |
| # need, as the IOI_SING tiles have the same wire, and pip names | |
| # despite they are found on the TOP or BOTTOM of an IOI column | |
| m = IOI_SING_REGEX.fullmatch(feature) | |
| if m: | |
| # Each clock region spans a total of 50 IOBs. | |
| # The IOI_SING are found on top or bottom of the whole | |
| # IOI/IOB column. The Y coordinate identified with the | |
| # second capture group is dived by 50 to get the relative | |
| # position of the IOI_SING within the clock region column | |
| is_bottom_sing = int(m.group(2)) % 50 == 0 | |
| # This is the value to attach to the source pip name that | |
| # changes based on which IOI_SING is selected (top or bottom) | |
| # | |
| # Example: IOI_OLOGIC0_D1.IOI_IMUX34_0 -> IOI_OLOGIC0_D1.IOI_IMUX34_1 | |
| src_value = '1' if is_bottom_sing else '0' | |
| # This is the value to attach to the IOI_SITE_PIPS names | |
| # in the destination wire of the pip | |
| # | |
| # Example: IOI_OLOGIC0 -> IOI_OLOGIC1 | |
| dst_value = '0' if is_bottom_sing else '1' | |
| unchanged_feature = "{}{}{}{}".format( | |
| m.group(1), m.group(2), m.group(3), m.group(4) | |
| ) | |
| src_wire = m.group(6).replace('_SING', '') | |
| for pip in ['IMUX', 'LOGIC_OUTS', 'CTRL', 'FAN', 'BYP']: | |
| if pip in src_wire: | |
| src_wire = src_wire.replace('_0', '_{}'.format(src_value)) | |
| if 'IOI_OCLK' in src_wire: | |
| src_wire = src_wire.replace('_0', '_{}'.format(dst_value)) | |
| changed_feature = "{}{}".format(dst_value, src_wire) | |
| feature = "{}{}".format(unchanged_feature, changed_feature) | |
| feature_path = feature.split('.') | |
| # IOB_DIFFO_OUT0->IOB_DIFFO_IN1 | |
| # | |
| # When this PIP is active the IOB operates in the differential output mode. | |
| # There is no feature assosciated with that PIP in the prjxray db but there | |
| # is a tile-wide feature named "DIFF_OUT". | |
| # | |
| # The "DIFF_OUT" cannot be set in the architecture as it is defined one | |
| # level up in the hierarchy (its tile-wide, not site-wide). So here we | |
| # map the PIP's feature to "DIFF_OUT" | |
| if feature_path[2] == "IOB_DIFFO_OUT0" and \ | |
| feature_path[1] == "IOB_DIFFO_IN1": | |
| return '{}.OUT_DIFF'.format(feature_path[0]) | |
| # IOB_PADOUT0->IOB_DIFFI_IN1 | |
| # IOB_PADOUT1->IOB_DIFFI_IN0 | |
| # | |
| # These connections are hard wires that connect IOB33M and IOB33S sites. | |
| # They are used in differential input mode. | |
| # | |
| # Vivado does not report this connection as a PIP but in the prjxray db it | |
| # is a pip. Instead of making it a pseudo-pip we simply reject fasm | |
| # features here. | |
| if feature_path[2] == "IOB_PADOUT0" and feature_path[1] == "IOB_DIFFI_IN1": | |
| return '' | |
| if feature_path[2] == "IOB_PADOUT1" and feature_path[1] == "IOB_DIFFI_IN0": | |
| return '' | |
| # REBUF stuff | |
| rebuf_key = (feature_path[0], feature_path[1]) | |
| if rebuf_key in REBUF_SOURCES: | |
| return ' '.join([feature] + REBUF_NODES[REBUF_SOURCES[rebuf_key]]) | |
| m = IOI_OCLK.fullmatch(feature_path[1]) | |
| if m: | |
| enable_oclkm_feature = '{}.IOI_OCLKM_{}.{}'.format( | |
| feature_path[0], m.group(1), feature_path[-1] | |
| ) | |
| return ' '.join((feature, enable_oclkm_feature)) | |
| if HCLK_CK_BUFHCLK_REGEX.fullmatch(feature_path[-1]): | |
| enable_buffer_feature = '{}.ENABLE_BUFFER.{}'.format( | |
| feature_path[0], feature_path[-1] | |
| ) | |
| return ' '.join((feature, enable_buffer_feature)) | |
| # BUFHCE sites are now routed through, without the need of placing them, therefore, | |
| # when the relative pip is traversed, the correct fasm feature needs to be added. | |
| # The relevant features are: | |
| # - IN_USE: to enable the BUFHCE site | |
| # - ZINV_CE: to disable the inverter on CE input which is connected to VCC. | |
| # This sets the CE signal to constant 1 | |
| m = CLK_HROW_CK_MUX_REGEX.fullmatch(feature_path[-1]) | |
| if m: | |
| x_loc_str = m.group(1) | |
| if 'L' in x_loc_str: | |
| x_loc = 0 | |
| elif 'R' in x_loc_str: | |
| x_loc = 1 | |
| else: | |
| assert False, "Impossible to determine X location of BUFHCE" | |
| y_loc = m.group(2) | |
| bufhce_loc = 'BUFHCE_X{}Y{}'.format(x_loc, y_loc) | |
| enable_bufhce_in_use = '{}.BUFHCE.{}.IN_USE'.format( | |
| feature_path[0], bufhce_loc | |
| ) | |
| enable_bufhce_zinv_ce = '{}.BUFHCE.{}.ZINV_CE=1\'b1'.format( | |
| feature_path[0], bufhce_loc | |
| ) | |
| return ' '.join((feature, enable_bufhce_in_use, enable_bufhce_zinv_ce)) | |
| if BUFG_CLK_IN_REGEX.fullmatch(feature_path[-1]): | |
| enable_feature = '{}.{}_ACTIVE'.format( | |
| feature_path[0], feature_path[-1] | |
| ) | |
| return ' '.join((feature, enable_feature)) | |
| if BUFG_CLK_OUT_REGEX.fullmatch(feature_path[-1]): | |
| enable_feature = '{}.{}_ACTIVE'.format( | |
| feature_path[0], feature_path[-1] | |
| ) | |
| return ' '.join((feature, enable_feature)) | |
| if CCIO_ACTIVE_REGEX.fullmatch(feature_path[-1]): | |
| features = [feature] | |
| features.append( | |
| '{}.{}_ACTIVE'.format(feature_path[0], feature_path[-1]) | |
| ) | |
| # Whenever a PIP connecting a CCIOn input of a HCLK_CMT tile is used | |
| # the additional feature CCIOn_USED has to be emitted. There is however | |
| # one exception which is for all PIPs connecting CCIOn with | |
| # MUX_OUT_FREQ_REFn wires. For those the feature should no be emitted. | |
| # | |
| # For more details refer to the fuzzer 045-hclk-cmt-pips in prjxray. | |
| if "FREQ_REF" not in feature_path[-2]: | |
| features.append( | |
| '{}.{}_USED'.format(feature_path[0], feature_path[-1]) | |
| ) | |
| return ' '.join(features) | |
| m = HCLK_OUT.fullmatch(feature_path[-1]) | |
| if m: | |
| return ' '.join( | |
| [feature] + find_hclk_cmt_hclk_feature( | |
| feature_path[0], m.group(1), m.group(2) | |
| ) | |
| ) | |
| m = CASCOUT_REGEX.fullmatch(feature_path[-2]) | |
| if m: | |
| enable_cascout = '{}.CASCOUT_{}_ACTIVE'.format( | |
| feature_path[0], m.group(1) | |
| ) | |
| return ' '.join((feature, enable_cascout)) | |
| extras = extra_features.extra_features(feature_path) | |
| if extras is not None: | |
| return ' '.join((feature, ) + tuple(extras)) | |
| parts = feature.split('.') | |
| wire_feature = feature_when_routed(parts[1]) | |
| if wire_feature is not None: | |
| return '{} {}.{}'.format(feature, parts[0], wire_feature) | |
| return feature | |
| # CLBLL_L.CLBLL_LL_A1[0] -> (CLBLL_L, CLBLL_LL_A1) | |
| PIN_NAME_TO_PARTS = re.compile(r'^([^\.]+)\.([^\]]+)\[0\]$') | |
| def create_get_tile_and_site_as_tile_pkey(cur): | |
| tiles = {} | |
| for tile_pkey, site_as_tile_pkey, grid_x, grid_y in cur.execute(""" | |
| SELECT pkey, site_as_tile_pkey, grid_x, grid_y FROM tile;"""): | |
| tiles[(grid_x, grid_y)] = (tile_pkey, site_as_tile_pkey) | |
| def get_tile_and_site_as_tile_pkey(x, y): | |
| return tiles[(x, y)] | |
| return get_tile_and_site_as_tile_pkey | |
| def create_get_site_as_tile_wire(cur): | |
| @functools.lru_cache(maxsize=0) | |
| def get_site_from_site_as_tile(site_as_tile_pkey): | |
| cur.execute( | |
| """ | |
| SELECT site.site_type_pkey, site_as_tile.site_pkey | |
| FROM site_as_tile | |
| INNER JOIN site ON site.pkey = site_as_tile.site_pkey | |
| WHERE site_as_tile.pkey = ?""", (site_as_tile_pkey, ) | |
| ) | |
| results = cur.fetchall() | |
| assert len(results) == 1, site_as_tile_pkey | |
| return results[0] | |
| @functools.lru_cache(maxsize=0) | |
| def get_site_as_tile_wire(site_as_tile_pkey, pin): | |
| site_type_pkey, site_pkey = get_site_from_site_as_tile( | |
| site_as_tile_pkey | |
| ) | |
| cur.execute( | |
| """ | |
| SELECT | |
| pkey | |
| FROM | |
| wire_in_tile | |
| WHERE | |
| site_pin_pkey = ( | |
| SELECT | |
| pkey | |
| FROM | |
| site_pin | |
| WHERE | |
| site_type_pkey = ? | |
| AND name = ? | |
| ) | |
| AND | |
| site_pkey = ? | |
| ;""", (site_type_pkey, pin, site_pkey) | |
| ) | |
| results = cur.fetchall() | |
| assert len(results) == 1 | |
| wire_in_tile_pkey = results[0][0] | |
| return wire_in_tile_pkey | |
| return get_site_as_tile_wire | |
| def import_graph_nodes(conn, graph, node_mapping): | |
| cur = conn.cursor() | |
| get_tile_and_site_as_tile_pkey = create_get_tile_and_site_as_tile_pkey(cur) | |
| get_site_as_tile_wire = create_get_site_as_tile_wire(cur) | |
| for node_idx, node in enumerate(graph.nodes): | |
| if node.type not in (graph2.NodeType.IPIN, graph2.NodeType.OPIN): | |
| continue | |
| gridloc = graph.loc_map[(node.loc.x_low, node.loc.y_low)] | |
| pin_name = graph.pin_ptc_to_name_map[ | |
| (gridloc.block_type_id, node.loc.ptc)] | |
| # Synthetic blocks are handled below. | |
| if pin_name.startswith('SYN-'): | |
| continue | |
| m = PIN_NAME_TO_PARTS.match(pin_name) | |
| assert m is not None, pin_name | |
| tile_type = m.group(1) | |
| tile_type = remove_vpr_tile_prefix(tile_type) | |
| pin = m.group(2) | |
| tile_pkey, site_as_tile_pkey = get_tile_and_site_as_tile_pkey( | |
| node.loc.x_low, node.loc.y_low | |
| ) | |
| if site_as_tile_pkey is not None: | |
| wire_in_tile_pkey = get_site_as_tile_wire(site_as_tile_pkey, pin) | |
| else: | |
| cur.execute( | |
| """ | |
| SELECT | |
| pkey | |
| FROM | |
| wire_in_tile | |
| WHERE | |
| name = ? | |
| AND | |
| phy_tile_type_pkey IN ( | |
| SELECT tile_type_pkey FROM phy_tile WHERE pkey IN ( | |
| SELECT phy_tile_pkey FROM tile_map WHERE tile_pkey = ? | |
| ) | |
| );""", (pin, tile_pkey) | |
| ) | |
| results = cur.fetchall() | |
| assert len(results) == 1 | |
| wire_in_tile_pkey = results[0][0] | |
| tile_pkey, _ = get_tile_and_site_as_tile_pkey(gridloc[0], gridloc[1]) | |
| cur.execute( | |
| """ | |
| SELECT | |
| top_graph_node_pkey, bottom_graph_node_pkey, | |
| left_graph_node_pkey, right_graph_node_pkey FROM wire | |
| WHERE | |
| wire_in_tile_pkey = ? AND tile_pkey = ?;""", | |
| (wire_in_tile_pkey, tile_pkey) | |
| ) | |
| result = cur.fetchone() | |
| assert result is not None, (wire_in_tile_pkey, tile_pkey) | |
| ( | |
| top_graph_node_pkey, bottom_graph_node_pkey, left_graph_node_pkey, | |
| right_graph_node_pkey | |
| ) = result | |
| side_graph_node_map = { | |
| "TOP": top_graph_node_pkey, | |
| "BOTTOM": bottom_graph_node_pkey, | |
| "LEFT": left_graph_node_pkey, | |
| "RIGHT": right_graph_node_pkey, | |
| } | |
| # VPR emits only one node for each site pin, instead of one node for each | |
| # side location of a pin. | |
| # | |
| # If the directional graph nodes are present for a specific tile wire, | |
| # the same node ID is assigned to the directional graph node to prevent | |
| # VPR failing to find a route. | |
| sides = node.loc.side._name_ | |
| sides_list = sides.split("_") | |
| for side in sides_list: | |
| graph_node_pkey = side_graph_node_map[side] | |
| assert graph_node_pkey is not None, (tile_type, pin_name) | |
| node_mapping[graph_node_pkey] = (node.id, node.type) | |
| def import_tracks(conn, alive_tracks, node_mapping, graph, default_segment_id): | |
| cur = conn.cursor() | |
| cur2 = conn.cursor() | |
| for (graph_node_pkey, track_pkey, graph_node_type, x_low, x_high, y_low, | |
| y_high, ptc, capacitance, | |
| resistance) in progressbar_utils.progressbar(cur.execute(""" | |
| SELECT | |
| pkey, | |
| track_pkey, | |
| graph_node_type, | |
| x_low, | |
| x_high, | |
| y_low, | |
| y_high, | |
| ptc, | |
| capacitance, | |
| resistance | |
| FROM | |
| graph_node WHERE track_pkey IS NOT NULL;""")): | |
| if track_pkey not in alive_tracks: | |
| continue | |
| cur2.execute( | |
| """ | |
| SELECT name FROM segment WHERE pkey = ( | |
| SELECT segment_pkey FROM track WHERE pkey = ? | |
| )""", (track_pkey, ) | |
| ) | |
| result = cur2.fetchone() | |
| if result is not None: | |
| segment_name = result[0] | |
| segment_id = graph.get_segment_id_from_name(segment_name) | |
| else: | |
| segment_id = default_segment_id | |
| node_type = graph2.NodeType(graph_node_type) | |
| if node_type == graph2.NodeType.CHANX: | |
| direction = 'X' | |
| x_low = max(x_low, 1) | |
| elif node_type == graph2.NodeType.CHANY: | |
| direction = 'Y' | |
| y_low = max(y_low, 1) | |
| else: | |
| assert False, node_type | |
| track = tracks.Track( | |
| direction=direction, | |
| x_low=x_low, | |
| x_high=x_high, | |
| y_low=y_low, | |
| y_high=y_high, | |
| ) | |
| assert graph_node_pkey not in node_mapping | |
| node_mapping[graph_node_pkey] = ( | |
| graph.add_track( | |
| track=track, | |
| segment_id=segment_id, | |
| ptc=ptc, | |
| timing=graph2.NodeTiming( | |
| r=resistance, | |
| c=capacitance, | |
| ), | |
| ), node_type | |
| ) | |
| def create_track_rr_graph( | |
| conn, graph, node_mapping, use_roi, roi, synth_tiles, segment_id | |
| ): | |
| cur = conn.cursor() | |
| cur.execute("""SELECT count(*) FROM track;""") | |
| (num_channels, ) = cur.fetchone() | |
| print('{} Import alive tracks'.format(now())) | |
| alive_tracks = set() | |
| for (track_pkey, | |
| ) in cur.execute("SELECT pkey FROM track WHERE alive = 1;"): | |
| alive_tracks.add(track_pkey) | |
| print('{} Importing alive tracks'.format(now())) | |
| import_tracks(conn, alive_tracks, node_mapping, graph, segment_id) | |
| print('original {} final {}'.format(num_channels, len(alive_tracks))) | |
| def add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles, overlay): | |
| cur = conn.cursor() | |
| delayless_switch = graph.get_switch_id('__vpr_delayless_switch__') | |
| for tile_name, synth_tile in synth_tiles['tiles'].items(): | |
| num_inpad = len( | |
| list( | |
| filter( | |
| lambda t: t['port_type'] == 'output', synth_tile['pins'] | |
| ) | |
| ) | |
| ) | |
| num_outpad = len( | |
| list( | |
| filter( | |
| lambda t: t['port_type'] == 'input', synth_tile['pins'] | |
| ) | |
| ) | |
| ) | |
| for pin in synth_tile['pins']: | |
| if pin['port_type'] in ['input', 'output']: | |
| wire_pkey = get_wire_pkey(conn, tile_name, pin['wire']) | |
| cur.execute( | |
| """ | |
| SELECT | |
| track_pkey | |
| FROM | |
| node | |
| WHERE | |
| pkey = ( | |
| SELECT | |
| node_pkey | |
| FROM | |
| wire | |
| WHERE | |
| pkey = ? | |
| );""", (wire_pkey, ) | |
| ) | |
| (track_pkey, ) = cur.fetchone() | |
| assert track_pkey is not None, ( | |
| tile_name, pin['wire'], wire_pkey | |
| ) | |
| elif pin['port_type'] == 'VCC': | |
| cur.execute('SELECT vcc_track_pkey FROM constant_sources') | |
| (track_pkey, ) = cur.fetchone() | |
| elif pin['port_type'] == 'GND': | |
| cur.execute('SELECT gnd_track_pkey FROM constant_sources') | |
| (track_pkey, ) = cur.fetchone() | |
| else: | |
| assert False, pin['port_type'] | |
| tracks_model, track_nodes = get_track_model(conn, track_pkey) | |
| option = list( | |
| tracks_model.get_tracks_for_wire_at_coord( | |
| tuple(synth_tile['loc']) | |
| ).values() | |
| ) | |
| assert len(option) > 0, (pin, len(option)) | |
| if pin['port_type'] == 'input': | |
| tile_type = synth_tile['tile_name'] | |
| wire = 'outpad' | |
| elif pin['port_type'] == 'output': | |
| tile_type = synth_tile['tile_name'] | |
| wire = 'inpad' | |
| elif pin['port_type'] == 'VCC': | |
| tile_type = 'SYN-VCC' | |
| wire = 'VCC' | |
| elif pin['port_type'] == 'GND': | |
| tile_type = 'SYN-GND' | |
| wire = 'GND' | |
| else: | |
| assert False, pin | |
| track_node = track_nodes[option[0]] | |
| assert track_node in node_mapping, (track_node, track_pkey) | |
| if wire == 'inpad' and num_inpad > 1: | |
| pin_name = graph.create_pin_name_from_tile_type_sub_tile_num_and_pin( | |
| tile_type, pin['z_loc'], wire | |
| ) | |
| elif wire == 'outpad' and num_outpad > 1: | |
| pin_name = graph.create_pin_name_from_tile_type_sub_tile_num_and_pin( | |
| tile_type, (pin['z_loc'] - num_inpad), wire | |
| ) | |
| else: | |
| pin_name = graph.create_pin_name_from_tile_type_and_pin( | |
| tile_type, wire | |
| ) | |
| pin_node = graph.get_nodes_for_pin( | |
| tuple(synth_tile['loc']), pin_name | |
| ) | |
| track_node_id, _ = node_mapping[track_node] | |
| if pin['port_type'] == 'input': | |
| graph.add_edge( | |
| src_node=track_node_id, | |
| sink_node=pin_node[0][0], | |
| switch_id=delayless_switch, | |
| name='synth_{}_{}'.format(tile_name, pin['wire']), | |
| ) | |
| elif pin['port_type'] in ['VCC', 'GND', 'output']: | |
| graph.add_edge( | |
| src_node=pin_node[0][0], | |
| sink_node=track_node_id, | |
| switch_id=delayless_switch, | |
| name='synth_{}_{}'.format(tile_name, pin['wire']), | |
| ) | |
| else: | |
| assert False, pin | |
| def get_switch_name(conn, graph, switch_name_map, switch_pkey): | |
| assert switch_pkey is not None | |
| if switch_pkey not in switch_name_map: | |
| cur = conn.cursor() | |
| cur.execute( | |
| """SELECT name FROM switch WHERE pkey = ?;""", (switch_pkey, ) | |
| ) | |
| (switch_name, ) = cur.fetchone() | |
| switch_id = graph.get_switch_id(switch_name) | |
| switch_name_map[switch_pkey] = switch_id | |
| else: | |
| switch_id = switch_name_map[switch_pkey] | |
| return switch_id | |
| def create_get_tile_name(conn): | |
| cur = conn.cursor() | |
| @functools.lru_cache(maxsize=None) | |
| def get_tile_name(tile_pkey): | |
| cur.execute( | |
| """ | |
| SELECT name FROM phy_tile WHERE pkey = ?; | |
| """, (tile_pkey, ) | |
| ) | |
| return cur.fetchone()[0] | |
| return get_tile_name | |
| def create_get_pip_wire_names(conn): | |
| cur = conn.cursor() | |
| @functools.lru_cache(maxsize=None) | |
| def get_pip_wire_names(pip_pkey): | |
| cur.execute( | |
| """SELECT src_wire_in_tile_pkey, dest_wire_in_tile_pkey | |
| FROM pip_in_tile WHERE pkey = ?;""", (pip_pkey, ) | |
| ) | |
| src_wire_in_tile_pkey, dest_wire_in_tile_pkey = cur.fetchone() | |
| cur.execute( | |
| """SELECT name FROM wire_in_tile WHERE pkey = ?;""", | |
| (src_wire_in_tile_pkey, ) | |
| ) | |
| (src_net, ) = cur.fetchone() | |
| cur.execute( | |
| """SELECT name FROM wire_in_tile WHERE pkey = ?;""", | |
| (dest_wire_in_tile_pkey, ) | |
| ) | |
| (dest_net, ) = cur.fetchone() | |
| return (src_net, dest_net) | |
| return get_pip_wire_names | |
| def get_number_graph_edges(conn, graph, node_mapping): | |
| num_edges = len(graph.edges) | |
| print('{} Counting edges.'.format(now())) | |
| cur = conn.cursor() | |
| cur.execute("SELECT count() FROM graph_edge;" "") | |
| nodes_set = set() | |
| for src_graph_node, dest_graph_node in cur.execute(""" | |
| SELECT | |
| src_graph_node_pkey, | |
| dest_graph_node_pkey | |
| FROM | |
| graph_edge; | |
| """): | |
| if src_graph_node not in node_mapping: | |
| continue | |
| if dest_graph_node not in node_mapping: | |
| continue | |
| src_node, src_node_type = node_mapping[src_graph_node] | |
| sink_node, sink_node_type = node_mapping[dest_graph_node] | |
| pin_node_types = [graph2.NodeType.IPIN, graph2.NodeType.OPIN] | |
| src_node_is_site_pin = src_node_type in pin_node_types | |
| sink_node_is_site_pin = sink_node_type in pin_node_types | |
| if src_node_is_site_pin ^ sink_node_is_site_pin: | |
| if (src_node, sink_node) in nodes_set: | |
| continue | |
| else: | |
| nodes_set.add((src_node, sink_node)) | |
| num_edges += 1 | |
| return num_edges | |
| def import_graph_edges(conn, graph, extra_features, node_mapping): | |
| # First yield existing edges | |
| print('{} Importing existing edges.'.format(now())) | |
| for edge in graph.edges: | |
| yield (edge.src_node, edge.sink_node, edge.switch_id, None) | |
| # Then yield edges from database. | |
| cur = conn.cursor() | |
| cur.execute("SELECT count() FROM graph_edge;" "") | |
| (num_edges, ) = cur.fetchone() | |
| get_tile_name = create_get_tile_name(conn) | |
| get_pip_wire_names = create_get_pip_wire_names(conn) | |
| switch_name_map = {} | |
| nodes_set = set() | |
| print('{} Importing edges from database.'.format(now())) | |
| with progressbar_utils.ProgressBar(max_value=num_edges) as bar: | |
| for idx, (src_graph_node, dest_graph_node, switch_pkey, phy_tile_pkey, | |
| pip_pkey, backward) in enumerate(cur.execute(""" | |
| SELECT | |
| src_graph_node_pkey, | |
| dest_graph_node_pkey, | |
| switch_pkey, | |
| phy_tile_pkey, | |
| pip_in_tile_pkey, | |
| backward | |
| FROM | |
| graph_edge; | |
| """)): | |
| if src_graph_node not in node_mapping: | |
| continue | |
| if dest_graph_node not in node_mapping: | |
| continue | |
| src_node, src_node_type = node_mapping[src_graph_node] | |
| sink_node, sink_node_type = node_mapping[dest_graph_node] | |
| pin_node_types = [graph2.NodeType.IPIN, graph2.NodeType.OPIN] | |
| src_node_is_site_pin = src_node_type in pin_node_types | |
| sink_node_is_site_pin = sink_node_type in pin_node_types | |
| # It may happen that a same CHAN <-> PIN edge is generated and this is unaccepted | |
| # by VPR, as it allows only multiple edges between CHAN nodes. | |
| # If a src_node, sink_node CHAN <-> PIN pair has already an edge, no new edge gets | |
| # added | |
| if src_node_is_site_pin ^ sink_node_is_site_pin: | |
| if (src_node, sink_node) in nodes_set: | |
| continue | |
| else: | |
| nodes_set.add((src_node, sink_node)) | |
| if pip_pkey is not None: | |
| tile_name = get_tile_name(phy_tile_pkey) | |
| src_net, dest_net = get_pip_wire_names(pip_pkey) | |
| if not backward: | |
| pip_name = '{}.{}.{}'.format(tile_name, dest_net, src_net) | |
| else: | |
| pip_name = '{}.{}.{}'.format(tile_name, src_net, dest_net) | |
| else: | |
| pip_name = None | |
| switch_id = get_switch_name( | |
| conn, graph, switch_name_map, switch_pkey | |
| ) | |
| if pip_name is not None: | |
| feature = check_feature(extra_features, pip_name) | |
| if feature: | |
| yield ( | |
| src_node, sink_node, switch_id, | |
| (('fasm_features', feature), ) | |
| ) | |
| else: | |
| yield (src_node, sink_node, switch_id, ()) | |
| else: | |
| yield (src_node, sink_node, switch_id, ()) | |
| if idx % 1024 == 0: | |
| bar.update(idx) | |
| def create_channels(conn): | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT chan_width_max, x_min, x_max, y_min, y_max FROM channel;""" | |
| ) | |
| chan_width_max, x_min, x_max, y_min, y_max = cur.fetchone() | |
| cur.execute('SELECT idx, info FROM x_list;') | |
| x_list = [] | |
| for idx, info in cur: | |
| x_list.append(graph2.ChannelList(idx, info)) | |
| cur.execute('SELECT idx, info FROM y_list;') | |
| y_list = [] | |
| for idx, info in cur: | |
| y_list.append(graph2.ChannelList(idx, info)) | |
| return graph2.Channels( | |
| chan_width_max=chan_width_max, | |
| x_min=x_min, | |
| y_min=y_min, | |
| x_max=x_max, | |
| y_max=y_max, | |
| x_list=x_list, | |
| y_list=y_list, | |
| ) | |
| def yield_nodes(nodes): | |
| with progressbar_utils.ProgressBar(max_value=len(nodes)) as bar: | |
| for idx, node in enumerate(nodes): | |
| yield node | |
| if idx % 1024 == 0: | |
| bar.update(idx) | |
| def phy_grid_dims(conn): | |
| """ Returns physical grid dimensions. """ | |
| cur = conn.cursor() | |
| cur.execute("SELECT grid_x FROM phy_tile ORDER BY grid_x DESC LIMIT 1;") | |
| x_max = cur.fetchone()[0] | |
| cur.execute("SELECT grid_y FROM phy_tile ORDER BY grid_y DESC LIMIT 1;") | |
| y_max = cur.fetchone()[0] | |
| return x_max + 1, y_max + 1 | |
| def find_constant_network(graph): | |
| """ Find VCC and GND tiles and create synth_tiles input. | |
| All arches should have these synthetic tiles, search the input rr graph | |
| for the SYN-GND and SYN-VCC tiles. | |
| """ | |
| block_types = {} | |
| for block_type in graph.block_types: | |
| block_types[block_type.name] = block_type.id | |
| assert 'SYN-GND' in block_types | |
| assert 'SYN-VCC' in block_types | |
| gnd_block_id = block_types['SYN-GND'] | |
| vcc_block_id = block_types['SYN-VCC'] | |
| gnd_loc = None | |
| vcc_loc = None | |
| for grid_loc in graph.grid: | |
| if gnd_block_id == grid_loc.block_type_id: | |
| assert gnd_loc is None | |
| gnd_loc = (grid_loc.x, grid_loc.y) | |
| if vcc_block_id == grid_loc.block_type_id: | |
| assert vcc_loc is None | |
| vcc_loc = (grid_loc.x, grid_loc.y) | |
| assert gnd_loc is not None | |
| assert vcc_loc is not None | |
| synth_tiles = { | |
| 'tiles': | |
| { | |
| "VCC": | |
| { | |
| 'loc': | |
| vcc_loc, | |
| 'pins': | |
| [ | |
| { | |
| 'wire': 'VCC', | |
| 'pad': 'VCC', | |
| 'port_type': 'VCC', | |
| 'is_clock': False, | |
| }, | |
| ], | |
| }, | |
| "GND": | |
| { | |
| 'loc': | |
| gnd_loc, | |
| 'pins': | |
| [ | |
| { | |
| 'wire': 'GND', | |
| 'pad': 'GND', | |
| 'port_type': 'GND', | |
| 'is_clock': False, | |
| }, | |
| ], | |
| }, | |
| } | |
| } | |
| return synth_tiles | |
| def create_node_remap(nodes, channels_obj): | |
| N = 2 | |
| p = math.ceil(math.log2(max(channels_obj.x_max, channels_obj.y_max))) | |
| point_map = {} | |
| for node in nodes: | |
| x = node.loc.x_low | |
| y = node.loc.y_low | |
| if (x, y) not in point_map: | |
| point_map[(x, y)] = [] | |
| point_map[(x, y)].append(node.id) | |
| hilbert_curve = HilbertCurve(p, N) | |
| idx = 0 | |
| id_map = {} | |
| for h in range(hilbert_curve.max_h + 1): | |
| coord = tuple(hilbert_curve.coordinates_from_distance(h)) | |
| if coord not in point_map: | |
| continue | |
| for old_id in point_map[coord]: | |
| id_map[old_id] = idx | |
| idx += 1 | |
| del point_map[coord] | |
| return lambda x: id_map[x] | |
| 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: | |
| extra_features = ExtraFeatures() | |
| populate_freq_bb_features(conn, extra_features) | |
| populate_bufg_rebuf_map(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: | |
| 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, | |
| ), | |
| sizing=graph2.SwitchSizing( | |
| mux_trans_size=0, | |
| buf_size=0, | |
| ), | |
| ) | |
| ) | |
| # 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. | |
| 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) | |
| 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, | |
| 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, extra_features, node_mapping | |
| ), | |
| node_remap=node_remap, | |
| ) | |
| for k in node_mapping: | |
| node_id, node_type = node_mapping[k] | |
| node_mapping[k] = (node_remap(node_id), node_type) | |
| 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())) | |
| if __name__ == '__main__': | |
| main() |