| #!/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() |