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


def reduce_connection_box(box):
    """ Reduce the number of connection boxes by merging some.

    Examples:

    >>> reduce_connection_box('IMUX0')
    'IMUX'
    >>> reduce_connection_box('IMUX1')
    'IMUX'
    >>> reduce_connection_box('IMUX10')
    'IMUX'
    >>> reduce_connection_box('BRAM_ADDR')
    'IMUX'
    >>> reduce_connection_box('A_L10')
    'A'
    >>> reduce_connection_box('B')
    'B'
    >>> reduce_connection_box('B_L')
    'B'

    """

    box = CONNECTION_BOX_FILTER.match(box).group(1)

    if 'BRAM_ADDR' in box:
        box = 'IMUX'

    if box.endswith('_L'):
        box = box.replace('_L', '')

    return box


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(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])
        )
        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))

    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 set_connection_box(
        graph, node_idx, grid_x, grid_y, box_id, site_pin_delay
):
    """ Assign a connection box to an IPIN node. """
    node_dict = graph.nodes[node_idx]._asdict()
    node_dict['connection_box'] = graph2.ConnectionBox(
        x=grid_x,
        y=grid_y,
        id=box_id,
        site_pin_delay=site_pin_delay,
    )
    graph.nodes[node_idx] = graph2.Node(**node_dict)


def update_connection_box(
        conn, graph, graph_node_pkey, node_idx, connection_box_map
):
    """ Update connection box of IPIN node if needed. """
    cur = conn.cursor()

    cur.execute(
        """
SELECT connection_box_wire_pkey
FROM graph_node WHERE pkey = ?""", (graph_node_pkey, )
    )

    connection_box_wire_pkey = cur.fetchone()[0]
    if connection_box_wire_pkey is not None:
        cur.execute(
            """
SELECT grid_x, grid_y FROM phy_tile WHERE pkey = (
    SELECT phy_tile_pkey FROM wire WHERE pkey = ?
    )""", (connection_box_wire_pkey, )
        )
        grid_x, grid_y = cur.fetchone()

        cur.execute(
            "SELECT wire_in_tile_pkey FROM wire WHERE pkey = ?",
            (connection_box_wire_pkey, )
        )
        wire_in_tile_pkey = cur.fetchone()[0]
        box_id = connection_box_map[wire_in_tile_pkey]

        cur.execute(
            """
SELECT switch.intrinsic_delay
FROM switch
WHERE pkey = (
    SELECT site_pin_switch_pkey
    FROM wire_in_tile
    WHERE pkey = (
        SELECT wire_in_tile_pkey
        FROM wire
        WHERE pkey = (
            SELECT site_wire_pkey
            FROM node
            WHERE pkey = (
                SELECT node_pkey
                FROM graph_node
                WHERE pkey = ?
            )
        )
    )
)""", (graph_node_pkey, )
        )
        site_pin_delay = cur.fetchone()[0]

        set_connection_box(
            graph, node_idx, grid_x, grid_y, box_id, site_pin_delay
        )


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, connection_box_map):
    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-'):
            set_connection_box(
                graph,
                node_idx,
                node.loc.x_low,
                node.loc.y_low,
                box_id=graph.maybe_add_connection_box('IMUX'),
                site_pin_delay=0.,
            )
            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 = node.loc.side
        if side == tracks.Direction.LEFT:
            assert left_graph_node_pkey is not None, (tile_type, pin_name)
            node_mapping[left_graph_node_pkey] = node.id

            update_connection_box(
                conn, graph, left_graph_node_pkey, node_idx, connection_box_map
            )
        elif side == tracks.Direction.RIGHT:
            assert right_graph_node_pkey is not None, (tile_type, pin_name)
            node_mapping[right_graph_node_pkey] = node.id

            update_connection_box(
                conn, graph, right_graph_node_pkey, node_idx,
                connection_box_map
            )
        elif side == tracks.Direction.TOP:
            assert top_graph_node_pkey is not None, (tile_type, pin_name)
            node_mapping[top_graph_node_pkey] = node.id

            update_connection_box(
                conn, graph, top_graph_node_pkey, node_idx, connection_box_map
            )
        elif side == tracks.Direction.BOTTOM:
            assert bottom_graph_node_pkey is not None, (tile_type, pin_name)
            node_mapping[bottom_graph_node_pkey] = node.id

            update_connection_box(
                conn, graph, bottom_graph_node_pkey, node_idx,
                connection_box_map
            )
        else:
            assert False, side


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

        canonical_loc = None
        cur2.execute(
            """
SELECT grid_x, grid_y FROM phy_tile WHERE pkey = (
    SELECT canon_phy_tile_pkey FROM track WHERE pkey = ?
    )""", (track_pkey, )
        )
        result = cur2.fetchone()
        if result:
            canonical_loc = graph2.CanonicalLoc(x=result[0], y=result[1])

        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,
            ),
            canonical_loc=canonical_loc
        )


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):
    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
            )

            if pin['port_type'] == 'input':
                graph.add_edge(
                    src_node=node_mapping[track_node],
                    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=node_mapping[track_node],
                    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;" "")

    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

        num_edges += 1

    return num_edges


def import_graph_edges(conn, graph, 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 = {}

    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

            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
            )

            src_node = node_mapping[src_graph_node]
            sink_node = node_mapping[dest_graph_node]

            if pip_name is not None:
                feature = check_feature(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 create_connection_boxes(conn, graph):
    """ Assign connection box ids for all connection box types. """
    cur = conn.cursor()
    cur.execute(
        """
SELECT pkey, tile_type_pkey, name FROM wire_in_tile WHERE pkey IN (
    SELECT DISTINCT wire_in_tile_pkey FROM wire WHERE pkey IN (
        SELECT connection_box_wire_pkey FROM graph_node
        WHERE connection_box_wire_pkey IS NOT NULL
    )
);"""
    )

    connection_box_map = {}
    for wire_in_tile_pkey, tile_type_pkey, wire_name in cur:
        connection_box_map[wire_in_tile_pkey] = graph.maybe_add_connection_box(
            reduce_connection_box(wire_name)
        )

    return connection_box_map


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(
        '--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.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)

    with sqlite3.connect("file:{}?mode=ro".format(args.connection_database),
                         uri=True) as conn:

        populate_bufg_rebuf_map(conn)

        cur = conn.cursor()
        for name, internal_capacitance, drive_resistance, intrinsic_delay, penalty_cost, \
                switch_type in cur.execute("""
SELECT
    name,
    internal_capacitance,
    drive_resistance,
    intrinsic_delay,
    penalty_cost,
    switch_type
FROM
    switch;"""):
            # Add back missing switchs, which were unused in arch xml, and so
            # were not  emitted in rrgraph XML.
            #
            # TODO: This can be removed once
            # https://github.com/verilog-to-routing/vtr-verilog-to-routing/issues/354
            # is fixed.

            try:
                graph.get_switch_id(name)
                continue
            except KeyError:
                capnp_graph.add_switch(
                    graph2.Switch(
                        id=None,
                        name=name,
                        type=graph2.SwitchType[switch_type.upper()],
                        timing=graph2.SwitchTiming(
                            r=drive_resistance,
                            c_in=0.0,
                            c_out=0.0,
                            c_internal=internal_capacitance,
                            t_del=intrinsic_delay,
                            p_cost=penalty_cost,
                        ),
                        sizing=graph2.SwitchSizing(
                            mux_trans_size=0,
                            buf_size=0,
                        ),
                    )
                )

        # Mapping of graph_node.pkey to rr node id.
        node_mapping = {}

        print('{} Creating connection box list'.format(now()))
        connection_box_map = create_connection_boxes(conn, graph)

        # Match site pins rr nodes with graph_node's in the connection_database.
        print('{} Importing graph nodes'.format(now()))
        import_graph_nodes(conn, graph, node_mapping, connection_box_map)

        # Walk all track graph nodes and add them.
        print('{} Creating tracks'.format(now()))
        segment_id = graph.get_segment_id_from_name('dummy')
        create_track_rr_graph(
            conn, graph, node_mapping, use_roi, roi, synth_tiles, segment_id
        )

        # Set of (src, sink, switch_id) tuples that pip edges have been sent to
        # VPR.  VPR cannot handle duplicate paths with the same switch id.
        print('{} Adding synthetic edges'.format(now()))
        add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles)

        print('{} Creating channels.'.format(now()))
        channels_obj = create_channels(conn)

        node_remap = create_node_remap(capnp_graph.graph.nodes, channels_obj)

        x_dim, y_dim = phy_grid_dims(conn)
        connection_box_obj = graph.create_connection_box_object(
            x_dim=x_dim, y_dim=y_dim
        )

        num_edges = get_number_graph_edges(conn, graph, node_mapping)
        print('{} Serializing to disk.'.format(now()))

        capnp_graph.serialize_to_capnp(
            channels_obj=channels_obj,
            connection_box_obj=connection_box_obj,
            num_nodes=len(capnp_graph.graph.nodes),
            nodes_obj=yield_nodes(capnp_graph.graph.nodes),
            num_edges=num_edges,
            edges_obj=import_graph_edges(conn, graph, node_mapping),
            node_remap=node_remap,
        )

        for k in node_mapping:
            node_mapping[k] = node_remap(node_mapping[k])

        print('{} Writing node map.'.format(now()))
        with open(args.write_rr_node_map, 'wb') as f:
            pickle.dump(node_mapping, f)
        print('{} Done writing node map.'.format(now()))


if __name__ == '__main__':
    main()

[((46, 24, 46, 59), 're.compile', 're.compile', ({(46, 35, 46, 58): '"""HCLK_CK_BUFHCLK[0-9]+"""'}, {}), "('HCLK_CK_BUFHCLK[0-9]+')", False, 'import re\n'), ((47, 24, 47, 72), 're.compile', 're.compile', ({(47, 35, 47, 71): '"""CLK_HROW_CK_MUX_OUT_([LR])([0-9]+)"""'}, {}), "('CLK_HROW_CK_MUX_OUT_([LR])([0-9]+)')", False, 'import re\n'), ((48, 16, 48, 77), 're.compile', 're.compile', ({(48, 27, 48, 76): '"""BRAM_CASCOUT_ADDR((?:BWR)|(?:ARD))ADDRU([0-9]+)"""'}, {}), "('BRAM_CASCOUT_ADDR((?:BWR)|(?:ARD))ADDRU([0-9]+)')", False, 'import re\n'), ((49, 24, 49, 53), 're.compile', 're.compile', ({(49, 35, 49, 52): '"""([^0-9]+)[0-9]*"""'}, {}), "('([^0-9]+)[0-9]*')", False, 'import re\n'), ((50, 20, 50, 59), 're.compile', 're.compile', ({(50, 31, 50, 58): '"""CLK_HROW_CK_IN_[LR][0-9]+"""'}, {}), "('CLK_HROW_CK_IN_[LR][0-9]+')", False, 'import re\n'), ((51, 21, 51, 59), 're.compile', 're.compile', ({(51, 32, 51, 58): '"""CLK_HROW_R_CK_GCLK[0-9]+"""'}, {}), "('CLK_HROW_R_CK_GCLK[0-9]+')", False, 'import re\n'), ((52, 20, 52, 53), 're.compile', 're.compile', ({(52, 31, 52, 52): '"""HCLK_CMT_CCIO[0-9]+"""'}, {}), "('HCLK_CMT_CCIO[0-9]+')", False, 'import re\n'), ((53, 11, 53, 60), 're.compile', 're.compile', ({(53, 22, 53, 59): '"""CLK_HROW_CK_HCLK_OUT_([LR])([0-9]+)"""'}, {}), "('CLK_HROW_CK_HCLK_OUT_([LR])([0-9]+)')", False, 'import re\n'), ((54, 11, 54, 40), 're.compile', 're.compile', ({(54, 22, 54, 39): '"""IOI_OCLK_([01])"""'}, {}), "('IOI_OCLK_([01])')", False, 'import re\n'), ((474, 20, 474, 60), 're.compile', 're.compile', ({(474, 31, 474, 59): '"""^([^\\\\.]+)\\\\.([^\\\\]]+)\\\\[0\\\\]$"""'}, {}), "('^([^\\\\.]+)\\\\.([^\\\\]]+)\\\\[0\\\\]$')", False, 'import re\n'), ((132, 24, 134, 5), 're.compile', 're.compile', ({(133, 8, 133, 52): '"""CLK_BUFG_REBUF_R_CK_GCLK([0-9]+)_(BOT|TOP)"""'}, {}), "('CLK_BUFG_REBUF_R_CK_GCLK([0-9]+)_(BOT|TOP)')", False, 'import re\n'), ((466, 19, 466, 48), 'prjxray_constant_site_pins.feature_when_routed', 'feature_when_routed', ({(466, 39, 466, 47): 'parts[1]'}, {}), '(parts[1])', False, 'from prjxray_constant_site_pins import feature_when_routed\n'), ((482, 34, 487, 5), 'lib.rr_graph.graph2.ConnectionBox', 'graph2.ConnectionBox', (), '', False, 'from lib.rr_graph import graph2\n'), ((488, 28, 488, 52), 'lib.rr_graph.graph2.Node', 'graph2.Node', ({}, {}), '(**node_dict)', False, 'from lib.rr_graph import graph2\n'), ((563, 5, 563, 35), 'functools.lru_cache', 'functools.lru_cache', (), '', False, 'import functools\n'), ((577, 5, 577, 35), 'functools.lru_cache', 'functools.lru_cache', (), '', False, 'import functools\n'), ((951, 5, 951, 38), 'functools.lru_cache', 'functools.lru_cache', (), '', False, 'import functools\n'), ((966, 5, 966, 38), 'functools.lru_cache', 'functools.lru_cache', (), '', False, 'import functools\n'), ((1106, 11, 1114, 5), 'lib.rr_graph.graph2.Channels', 'graph2.Channels', (), '', False, 'from lib.rr_graph import graph2\n'), ((1244, 20, 1244, 38), 'hilbertcurve.hilbertcurve.HilbertCurve', 'HilbertCurve', ({(1244, 33, 1244, 34): 'p', (1244, 36, 1244, 37): 'N'}, {}), '(p, N)', False, 'from hilbertcurve.hilbertcurve import HilbertCurve\n'), ((1264, 13, 1264, 38), 'argparse.ArgumentParser', 'argparse.ArgumentParser', ({}, {}), '()', False, 'import argparse\n'), ((641, 20, 641, 53), 'prjxray_tile_import.remove_vpr_tile_prefix', 'remove_vpr_tile_prefix', ({(641, 43, 641, 52): 'tile_type'}, {}), '(tile_type)', False, 'from prjxray_tile_import import remove_vpr_tile_prefix\n'), ((760, 20, 760, 52), 'lib.rr_graph.graph2.NodeType', 'graph2.NodeType', ({(760, 36, 760, 51): 'graph_node_type'}, {}), '(graph_node_type)', False, 'from lib.rr_graph import graph2\n'), ((782, 16, 788, 9), 'lib.rr_graph.tracks.Track', 'tracks.Track', (), '', False, 'from lib.rr_graph import tracks\n'), ((1034, 9, 1034, 59), 'lib.progressbar_utils.ProgressBar', 'progressbar_utils.ProgressBar', (), '', False, 'from lib import progressbar_utils\n'), ((1310, 14, 1316, 9), 'prjxray.roi.Roi', 'Roi', (), '', False, 'from prjxray.roi import Roi\n'), ((780, 28, 780, 73), 'lib.rr_graph.graph2.CanonicalLoc', 'graph2.CanonicalLoc', (), '', False, 'from lib.rr_graph import graph2\n'), ((871, 40, 871, 73), 'lib.connection_database.get_track_model', 'get_track_model', ({(871, 56, 871, 60): 'conn', (871, 62, 871, 72): 'track_pkey'}, {}), '(conn, track_pkey)', False, 'from lib.connection_database import get_wire_pkey, get_track_model\n'), ((1099, 22, 1099, 51), 'lib.rr_graph.graph2.ChannelList', 'graph2.ChannelList', ({(1099, 41, 1099, 44): 'idx', (1099, 46, 1099, 50): 'info'}, {}), '(idx, info)', False, 'from lib.rr_graph import graph2\n'), ((1104, 22, 1104, 51), 'lib.rr_graph.graph2.ChannelList', 'graph2.ChannelList', ({(1104, 41, 1104, 44): 'idx', (1104, 46, 1104, 50): 'info'}, {}), '(idx, info)', False, 'from lib.rr_graph import graph2\n'), ((1308, 26, 1308, 38), 'simplejson.load', 'json.load', ({(1308, 36, 1308, 37): 'f'}, {}), '(f)', True, 'import simplejson as json\n'), ((1322, 14, 1328, 9), 'prjxray.roi.Roi', 'Roi', (), '', False, 'from prjxray.roi import Roi\n'), ((1446, 12, 1446, 40), 'pickle.dump', 'pickle.dump', ({(1446, 24, 1446, 36): 'node_mapping', (1446, 38, 1446, 39): 'f'}, {}), '(node_mapping, f)', False, 'import pickle\n'), ((794, 19, 797, 13), 'lib.rr_graph.graph2.NodeTiming', 'graph2.NodeTiming', (), '', False, 'from lib.rr_graph import graph2\n'), ((842, 28, 842, 71), 'lib.connection_database.get_wire_pkey', 'get_wire_pkey', ({(842, 42, 842, 46): 'conn', (842, 48, 842, 57): 'tile_name', (842, 59, 842, 70): "pin['wire']"}, {}), "(conn, tile_name, pin['wire'])", False, 'from lib.connection_database import get_wire_pkey, get_track_model\n'), ((1381, 31, 1388, 25), 'lib.rr_graph.graph2.SwitchTiming', 'graph2.SwitchTiming', (), '', False, 'from lib.rr_graph import graph2\n'), ((1389, 31, 1392, 25), 'lib.rr_graph.graph2.SwitchSizing', 'graph2.SwitchSizing', (), '', False, 'from lib.rr_graph import graph2\n')]