| import prjxray.db | |
| from prjxray.roi import Roi | |
| from prjxray.overlay import Overlay | |
| from prjxray import grid_types | |
| import simplejson as json | |
| from lib import progressbar_utils | |
| import datetime | |
| import functools | |
| from collections import namedtuple | |
| from lib.rr_graph import tracks | |
| from lib.rr_graph import graph2 | |
| from prjxray.site_type import SitePinDirection | |
| from prjxray_constant_site_pins import yield_ties_to_wire | |
| from lib.connection_database import get_track_model, get_wire_in_tile_from_pin_name | |
| from lib.rr_graph.graph2 import NodeType | |
| import re | |
| import math | |
| import numpy | |
| from prjxray_db_cache import DatabaseCache | |
| now = datetime.datetime.now | |
| def get_node_type(conn, graph_node_pkey): | |
| """ Returns the node type of a given graph node""" | |
| c = conn.cursor() | |
| c.execute( | |
| """ | |
| SELECT graph_node_type FROM graph_node WHERE pkey = ?""", | |
| (graph_node_pkey, ) | |
| ) | |
| return c.fetchone()[0] | |
| def get_pins(conn, site_type, site_pin): | |
| """ Returns a set of the pin graph_nodes related to the input site type and pin names.""" | |
| c = conn.cursor() | |
| c.execute( | |
| """ | |
| WITH pins(wire_in_tile_pkey) AS ( | |
| SELECT wire_in_tile.pkey FROM wire_in_tile | |
| INNER JOIN site_pin ON site_pin.pkey = wire_in_tile.site_pin_pkey | |
| INNER JOIN site_type ON site_pin.site_type_pkey = site_type.pkey | |
| WHERE | |
| site_type.name == ? | |
| AND | |
| site_pin.name == ? | |
| ) | |
| SELECT graph_node.pkey FROM graph_node | |
| INNER JOIN wire ON graph_node.node_pkey = wire.node_pkey | |
| WHERE | |
| wire.wire_in_tile_pkey IN (SELECT wire_in_tile_pkey FROM pins); | |
| """, ( | |
| site_type, | |
| site_pin, | |
| ) | |
| ) | |
| return set(graph_node_pkey for (graph_node_pkey, ) in c.fetchall()) | |
| def add_graph_nodes_for_pins(conn, tile_type, wire, pin_directions): | |
| """ Adds graph_node rows for each pin on a wire in a tile. """ | |
| (wire_in_tile_pkeys, site_pin_pkey) = get_wire_in_tile_from_pin_name( | |
| conn=conn, tile_type_str=tile_type, wire_str=wire | |
| ) | |
| # Determine if this should be an IPIN or OPIN based on the site_pin | |
| # direction. | |
| c = conn.cursor() | |
| c.execute( | |
| """ | |
| SELECT direction FROM site_pin WHERE pkey = ?;""", (site_pin_pkey, ) | |
| ) | |
| (pin_direction, ) = c.fetchone() | |
| pin_direction = SitePinDirection(pin_direction) | |
| if pin_direction == SitePinDirection.IN: | |
| node_type = NodeType.IPIN | |
| elif pin_direction == SitePinDirection.OUT: | |
| node_type = NodeType.OPIN | |
| # FIXME: Support INOUT pins | |
| elif pin_direction == SitePinDirection.INOUT: | |
| node_type = NodeType.OPIN | |
| else: | |
| assert False, pin_direction | |
| write_cur = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| for wire_in_tile_pkey in wire_in_tile_pkeys.values(): | |
| # Find all instances of this specific wire. | |
| c.execute( | |
| """ | |
| SELECT pkey, node_pkey, tile_pkey | |
| FROM wire WHERE wire_in_tile_pkey = ?;""", | |
| (wire_in_tile_pkey, ) | |
| ) | |
| c3 = conn.cursor() | |
| for wire_pkey, node_pkey, tile_pkey in c: | |
| c3.execute( | |
| """ | |
| SELECT grid_x, grid_y FROM tile WHERE pkey = ?;""", | |
| (tile_pkey, ) | |
| ) | |
| grid_x, grid_y = c3.fetchone() | |
| updates = [] | |
| values = [] | |
| # Insert a graph_node per pin_direction. | |
| for pin_direction in pin_directions: | |
| write_cur.execute( | |
| """ | |
| INSERT INTO graph_node( | |
| graph_node_type, node_pkey, x_low, x_high, y_low, y_high) | |
| VALUES (?, ?, ?, ?, ?, ?)""", ( | |
| node_type.value, | |
| node_pkey, | |
| grid_x, | |
| grid_x, | |
| grid_y, | |
| grid_y, | |
| ) | |
| ) | |
| updates.append( | |
| '{}_graph_node_pkey = ?'.format( | |
| pin_direction.name.lower() | |
| ) | |
| ) | |
| values.append(write_cur.lastrowid) | |
| assert len(updates) > 0, (updates, wire_in_tile_pkey, wire_pkey) | |
| # Update the wire with the graph_nodes in each direction, if | |
| # applicable. | |
| write_cur.execute( | |
| """ | |
| UPDATE wire SET {updates} WHERE pkey = ?;""".format( | |
| updates=','.join(updates) | |
| ), values + [wire_pkey] | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| write_cur.connection.commit() | |
| class KnownSwitch(object): | |
| def __init__(self, switch_pkey): | |
| self.switch_pkey = switch_pkey | |
| def get_pip_switch(self, src_wire_pkey, dest_wire_pkey): | |
| assert src_wire_pkey is None | |
| assert dest_wire_pkey is None | |
| return self.switch_pkey | |
| class Pip(object): | |
| def __init__(self, c, tile_type, pip): | |
| self.c = c | |
| c.execute( | |
| """ | |
| SELECT | |
| pkey, | |
| src_wire_in_tile_pkey, | |
| dest_wire_in_tile_pkey, | |
| switch_pkey, | |
| backward_switch_pkey, | |
| is_directional, | |
| is_pseudo, | |
| can_invert | |
| FROM | |
| pip_in_tile | |
| WHERE | |
| name = ? | |
| AND tile_type_pkey = ( | |
| SELECT | |
| pkey | |
| FROM | |
| tile_type | |
| WHERE | |
| name = ? | |
| );""", (pip, tile_type) | |
| ) | |
| result = c.fetchone() | |
| assert result is not None, (tile_type, pip) | |
| ( | |
| self.pip_pkey, self.src_wire_in_tile_pkey, | |
| self.dest_wire_in_tile_pkey, self.switch_pkey, | |
| self.backward_switch_pkey, self.is_directional, self.is_pseudo, | |
| self.can_invert | |
| ) = result | |
| assert self.switch_pkey is not None, (pip, tile_type) | |
| if self.is_directional: | |
| assert self.switch_pkey == self.backward_switch_pkey | |
| def __iter__(self): | |
| yield "pip_pkey", self.pip_pkey | |
| yield "src_wire_in_tile_pkey", self.src_wire_in_tile_pkey | |
| yield "dest_wire_in_tile_pkey", self.dest_wire_in_tile_pkey | |
| yield "switch_pkey", self.switch_pkey | |
| yield "backward_switch_pkey", self.backward_switch_pkey | |
| yield "is_directional", self.is_directional | |
| yield "is_pseudo", self.is_pseudo | |
| yield "can_invert", self.can_invert | |
| def get_pip_switch(self, src_wire_pkey, dest_wire_pkey): | |
| """ Return the switch_pkey for the given connection. | |
| Selects either normal or backward switch from pip, or if switch is | |
| already known, returns known switch. | |
| It is not valid to provide a switch and provide src/dest/pip arguments. | |
| Arguments | |
| --------- | |
| src_wire_pkey : int | |
| Source wire row primary key. | |
| dest_wire_pkey : int | |
| Destination wire row primary key. | |
| Returns | |
| ------- | |
| Switch row primary key to connect through specified pip. | |
| """ | |
| assert src_wire_pkey is not None | |
| assert dest_wire_pkey is not None | |
| if self.switch_pkey == self.backward_switch_pkey: | |
| return self.switch_pkey | |
| self.c.execute( | |
| "SELECT wire_in_tile_pkey FROM wire WHERE pkey = ?", | |
| (src_wire_pkey, ) | |
| ) | |
| src_wire_in_tile_pkey = self.c.fetchone()[0] | |
| self.c.execute( | |
| "SELECT wire_in_tile_pkey FROM wire WHERE pkey = ?", | |
| (dest_wire_pkey, ) | |
| ) | |
| dest_wire_in_tile_pkey = self.c.fetchone()[0] | |
| if src_wire_in_tile_pkey == self.src_wire_in_tile_pkey: | |
| assert dest_wire_in_tile_pkey == self.dest_wire_in_tile_pkey | |
| return self.switch_pkey | |
| else: | |
| assert src_wire_in_tile_pkey == self.dest_wire_in_tile_pkey | |
| assert dest_wire_in_tile_pkey == self.src_wire_in_tile_pkey | |
| return self.backward_switch_pkey | |
| def create_find_pip(conn): | |
| """Returns a function that takes (tile_type, pip) and returns a tuple | |
| containing: pip_in_tile_pkey, is_directional, is_pseudo, can_invert""" | |
| c = conn.cursor() | |
| @functools.lru_cache(maxsize=None) | |
| def find_pip(tile_type, pip): | |
| return Pip(c, tile_type, pip) | |
| return find_pip | |
| def create_find_wire(conn): | |
| """ Returns a function finds a wire based on tile name and wire name. | |
| Args: | |
| conn: Database connection | |
| Returns: | |
| Function. See find_wire below for signature. | |
| """ | |
| c = conn.cursor() | |
| @functools.lru_cache(maxsize=None) | |
| def find_wire_in_tile(tile_type, wire): | |
| c.execute( | |
| """ | |
| SELECT | |
| pkey | |
| FROM | |
| wire_in_tile | |
| WHERE | |
| name = ? | |
| AND phy_tile_type_pkey = ( | |
| SELECT | |
| pkey | |
| FROM | |
| tile_type | |
| WHERE | |
| name = ? | |
| );""", (wire, tile_type) | |
| ) | |
| result = c.fetchone() | |
| assert result is not None, (tile_type, wire) | |
| return result[0] | |
| @functools.lru_cache(maxsize=100000) | |
| def find_wire(phy_tile, tile_type, wire): | |
| """ Finds a wire in the database. | |
| Args: | |
| phy_tile (str): Physical tile name | |
| tile_type (str): Type of tile name | |
| wire (str): Wire name | |
| Returns: | |
| Tuple (wire_pkey, phy_tile_pkey, node_pkey), where: | |
| wire_pkey (int): Primary key of wire table | |
| tile_pkey (int): Primary key of VPR tile row that contains | |
| this wire. | |
| phy_tile_pkey (int): Primary key of physical tile row that | |
| contains this wire. | |
| node_pkey (int): Primary key of node table that is the node | |
| this wire belongs too. | |
| """ | |
| wire_in_tile_pkey = find_wire_in_tile(tile_type, wire) | |
| c.execute( | |
| """ | |
| SELECT | |
| pkey, | |
| tile_pkey, | |
| phy_tile_pkey, | |
| node_pkey | |
| FROM | |
| wire | |
| WHERE | |
| wire_in_tile_pkey = ? | |
| AND phy_tile_pkey = ( | |
| SELECT | |
| pkey | |
| FROM | |
| phy_tile | |
| WHERE | |
| name = ? | |
| );""", (wire_in_tile_pkey, phy_tile) | |
| ) | |
| result = c.fetchone() | |
| assert result is not None, ( | |
| phy_tile, tile_type, wire, wire_in_tile_pkey | |
| ) | |
| return result | |
| return find_wire | |
| Pins = namedtuple('Pins', 'x y edge_map site_pin_direction') | |
| OPPOSITE_DIRECTIONS = { | |
| tracks.Direction.TOP: tracks.Direction.BOTTOM, | |
| tracks.Direction.BOTTOM: tracks.Direction.TOP, | |
| tracks.Direction.LEFT: tracks.Direction.RIGHT, | |
| tracks.Direction.RIGHT: tracks.Direction.LEFT, | |
| } | |
| class Connector(object): | |
| """ Connector is an object for joining two nodes. | |
| Connector represents either a site pin within a specific tile or routing | |
| channel made of one or more channel nodes. | |
| """ | |
| def __init__(self, conn, pins=None, tracks=None): | |
| """ Create a Connector object. | |
| Provide either pins or tracks, not both or neither. | |
| Args: | |
| pins (Pins namedtuple): If this Connector object represents a | |
| site pin, provide the pins named arguments. | |
| tracks (tuple of (tracks.Tracks, list of graph nodes)): If this | |
| Connector object represents a routing channel, provide the | |
| tracks named argument. | |
| The tuple can most easily be constructed via | |
| connection_database.get_track_model, which builds the Tracks | |
| models and the graph node list. | |
| """ | |
| self.conn = conn | |
| self.pins = pins | |
| self.tracks = tracks | |
| self.track_connections = {} | |
| assert (self.pins is not None) ^ (self.tracks is not None) | |
| def find_wire_node( | |
| self, wire_pkey, graph_node_pkey, track_graph_node_pkey | |
| ): | |
| """ Find/create graph node for site pin. | |
| In order to support site pin timing modelling, an additional node | |
| is required to support the timing model. This function returns that | |
| node, along with the switch that should be used to connect the | |
| IPIN/OPIN to that node. See diagram for details. | |
| Arguments | |
| --------- | |
| wire_pkey : int | |
| Wire primary key to a wire attached to a site pin. | |
| graph_node_pkey : int | |
| Graph node primary key that represents which IPIN/OPIN node is | |
| being used to connect the site pin to the routing graph. | |
| track_graph_node_pkey : int | |
| Graph node primary key that represents the first routing node this | |
| site pin connects too. See diagram for details. | |
| Returns | |
| ------- | |
| site_pin_switch_pkey : int | |
| Switch primary key to the switch to connect IPIN/OPIN node to | |
| new site pin wire node. See diagram for details. | |
| site_pin_graph_node_pkey : int | |
| Graph node primary key that represents site pin wire node. | |
| See diagram for details. | |
| Diagram: | |
| --+ | |
| | tile wire #1 +-----+ tile wire #2 | |
| +==>-------------->+ pip +---------------> | |
| | ^-Site pin +-----+ | |
| --+ | |
| +----+ +-----+ +-----+ | |
| |OPIN+--edge #1->+CHAN1+--edge #2-->+CHAN2|-> | |
| +----+ +-----+ +-----+ | |
| The timing information from the site pin is encoded in edge #1. | |
| The timing information from tile wire #1 is encoded in CHAN1. | |
| The timing information from pip is encoded in edge #2. | |
| The remaining timing information is encoded in edges and channels | |
| as expected. | |
| This function returns edge #1 as the site_pin_switch_pkey. | |
| This function returns CHAN1 as site_pin_graph_node_pkey. | |
| The diagram for an IPIN is the same, except reverse all the arrows. | |
| """ | |
| cur = self.conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT site_wire_pkey FROM node WHERE pkey = ( | |
| SELECT node_pkey FROM wire WHERE pkey = ? | |
| ) | |
| """, (wire_pkey, ) | |
| ) | |
| site_wire_pkey = cur.fetchone()[0] | |
| cur.execute( | |
| """ | |
| SELECT | |
| node_pkey, | |
| top_graph_node_pkey, | |
| bottom_graph_node_pkey, | |
| right_graph_node_pkey, | |
| left_graph_node_pkey, | |
| site_pin_graph_node_pkey | |
| FROM wire WHERE pkey = ?""", (site_wire_pkey, ) | |
| ) | |
| values = cur.fetchone() | |
| node_pkey = values[0] | |
| edge_nodes = values[1:5] | |
| site_pin_graph_node_pkey = values[5] | |
| cur.execute( | |
| """ | |
| SELECT | |
| site_pin_switch_pkey | |
| FROM | |
| wire_in_tile | |
| WHERE | |
| site_pin_switch_pkey IS NOT NULL | |
| AND | |
| pkey IN ( | |
| SELECT | |
| wire_in_tile_pkey | |
| FROM | |
| wire | |
| WHERE | |
| node_pkey IN ( | |
| SELECT | |
| node_pkey | |
| FROM | |
| wire | |
| WHERE | |
| pkey = ? | |
| ) | |
| )""", (wire_pkey, ) | |
| ) | |
| results = cur.fetchall() | |
| assert len(results) == 1, (wire_pkey, results) | |
| site_pin_switch_pkey = results[0][0] | |
| assert site_pin_switch_pkey is not None, wire_pkey | |
| assert graph_node_pkey in edge_nodes, ( | |
| wire_pkey, graph_node_pkey, track_graph_node_pkey, edge_nodes | |
| ) | |
| if site_pin_graph_node_pkey is None: | |
| assert track_graph_node_pkey is not None, ( | |
| wire_pkey, graph_node_pkey, track_graph_node_pkey, edge_nodes | |
| ) | |
| is_lv_node = False | |
| for (name, ) in cur.execute(""" | |
| SELECT wire_in_tile.name | |
| FROM wire_in_tile | |
| WHERE pkey IN ( | |
| SELECT wire_in_tile_pkey FROM wire WHERE node_pkey = ? | |
| )""", (node_pkey, )): | |
| if name.startswith('LV'): | |
| is_lv_node = True | |
| break | |
| capacitance = 0 | |
| resistance = 0 | |
| for idx, (wire_cap, wire_res) in enumerate(cur.execute(""" | |
| SELECT wire_in_tile.capacitance, wire_in_tile.resistance | |
| FROM wire_in_tile | |
| WHERE pkey IN ( | |
| SELECT wire_in_tile_pkey FROM wire WHERE node_pkey = ? | |
| )""", (node_pkey, ))): | |
| capacitance += wire_cap | |
| resistance + wire_res | |
| if is_lv_node and idx == 1: | |
| # Only use first 2 wire RC's, ignore the rest. It appears | |
| # that some of the RC constant was lumped into the switch | |
| # timing, so don't double count. | |
| # | |
| # FIXME: Note that this is a hack, and should be fixed if | |
| # possible. | |
| break | |
| # This node does not exist, create it now | |
| write_cur = self.conn.cursor() | |
| write_cur.execute("INSERT INTO track DEFAULT VALUES") | |
| new_track_pkey = write_cur.lastrowid | |
| write_cur.execute( | |
| """ | |
| INSERT INTO | |
| graph_node( | |
| graph_node_type, | |
| node_pkey, | |
| x_low, | |
| x_high, | |
| y_low, | |
| y_high, | |
| capacity, | |
| capacitance, | |
| resistance, | |
| track_pkey) | |
| SELECT | |
| graph_node_type, | |
| ?, | |
| x_low, | |
| x_high, | |
| y_low, | |
| y_high, | |
| capacity, | |
| ?, | |
| ?, | |
| ? | |
| FROM graph_node WHERE pkey = ?""", ( | |
| node_pkey, | |
| capacitance, | |
| resistance, | |
| new_track_pkey, | |
| track_graph_node_pkey, | |
| ) | |
| ) | |
| site_pin_graph_node_pkey = write_cur.lastrowid | |
| write_cur.execute( | |
| """ | |
| UPDATE wire SET site_pin_graph_node_pkey = ? | |
| WHERE pkey = ?""", ( | |
| site_pin_graph_node_pkey, | |
| wire_pkey, | |
| ) | |
| ) | |
| write_cur.connection.commit() | |
| return site_pin_switch_pkey, site_pin_graph_node_pkey | |
| def get_edge_with_mux_switch( | |
| self, src_wire_pkey, pip_pkey, dest_wire_pkey | |
| ): | |
| """ Return switch_pkey for EDGE_WITH_MUX instance. """ | |
| cur = self.conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT site_wire_pkey FROM node WHERE pkey = ( | |
| SELECT node_pkey FROM wire WHERE pkey = ? | |
| );""", (src_wire_pkey, ) | |
| ) | |
| src_site_wire_pkey = cur.fetchone()[0] | |
| cur.execute( | |
| """ | |
| SELECT site_wire_pkey FROM node WHERE pkey = ( | |
| SELECT node_pkey FROM wire WHERE pkey = ? | |
| );""", (dest_wire_pkey, ) | |
| ) | |
| dest_site_wire_pkey = cur.fetchone()[0] | |
| cur.execute( | |
| """ | |
| SELECT switch_pkey FROM edge_with_mux WHERE | |
| src_wire_pkey = ? | |
| AND | |
| dest_wire_pkey = ? | |
| AND | |
| pip_in_tile_pkey = ?""", ( | |
| src_site_wire_pkey, | |
| dest_site_wire_pkey, | |
| pip_pkey, | |
| ) | |
| ) | |
| result = cur.fetchone() | |
| assert result is not None, ( | |
| src_site_wire_pkey, | |
| dest_site_wire_pkey, | |
| pip_pkey, | |
| ) | |
| return result[0] | |
| def find_connection_at_loc(self, loc): | |
| assert self.tracks is not None | |
| tracks_model, graph_nodes = self.tracks | |
| if loc not in self.track_connections: | |
| for idx in tracks_model.get_tracks_for_wire_at_coord(loc).values(): | |
| break | |
| self.track_connections[loc] = idx | |
| else: | |
| idx = self.track_connections[loc] | |
| assert idx is not None | |
| return idx | |
| def connect_at( | |
| self, | |
| loc, | |
| other_connector, | |
| pip, | |
| src_wire_pkey=None, | |
| dest_wire_pkey=None, | |
| ): | |
| """ Connect two Connector objects at a location within the grid. | |
| Arguments | |
| --------- | |
| loc : prjxray.grid_types.GridLoc | |
| Location within grid to make connection. | |
| other_connector : Connector | |
| Destination connection. | |
| src_wire_pkey : int | |
| Source wire pkey of pip being connected. | |
| dest_wire_pkey : int | |
| Destination wire pkey of pip being connected. | |
| pip : Pip | |
| Pip object of pip being connected. | |
| Returns: | |
| Tuple of (src_graph_node_pkey, dest_graph_node_pkey) | |
| """ | |
| if self.tracks and other_connector.tracks: | |
| tracks_model, graph_nodes = self.tracks | |
| idx1 = self.find_connection_at_loc(loc) | |
| other_tracks_model, other_graph_nodes = other_connector.tracks | |
| idx2 = other_connector.find_connection_at_loc(loc) | |
| switch_pkey = pip.get_pip_switch(src_wire_pkey, dest_wire_pkey) | |
| yield graph_nodes[idx1], switch_pkey, other_graph_nodes[ | |
| idx2], pip.pip_pkey | |
| return | |
| elif self.pins and other_connector.tracks: | |
| assert self.pins.site_pin_direction == SitePinDirection.OUT | |
| tracks_model, graph_nodes = other_connector.tracks | |
| for pin_dir, idx in tracks_model.get_tracks_for_wire_at_coord( | |
| grid_types.GridLoc(self.pins.x, self.pins.y)).items(): | |
| if pin_dir in self.pins.edge_map: | |
| # Site pin -> Interconnect is modelled as: | |
| # | |
| # OPIN -> edge (Site pin) -> Wire CHAN -> edge (PIP) -> Interconnect CHAN node | |
| # | |
| src_node = self.pins.edge_map[pin_dir] | |
| dest_track_node = graph_nodes[idx] | |
| site_pin_switch_pkey, src_wire_node = self.find_wire_node( | |
| src_wire_pkey, src_node, dest_track_node | |
| ) | |
| switch_pkey = pip.get_pip_switch( | |
| src_wire_pkey, dest_wire_pkey | |
| ) | |
| yield (src_node, site_pin_switch_pkey, src_wire_node, None) | |
| yield ( | |
| src_wire_node, switch_pkey, dest_track_node, | |
| pip.pip_pkey | |
| ) | |
| return | |
| elif self.tracks and other_connector.pins: | |
| assert other_connector.pins.site_pin_direction == SitePinDirection.IN | |
| tracks_model, graph_nodes = self.tracks | |
| for pin_dir, idx in tracks_model.get_tracks_for_wire_at_coord( | |
| grid_types.GridLoc(other_connector.pins.x, | |
| other_connector.pins.y)).items(): | |
| if pin_dir in other_connector.pins.edge_map: | |
| # Interconnect -> Site pin is modelled as: | |
| # | |
| # Interconnect CHAN node -> edge (PIP) -> Wire CHAN -> edge (Site pin) -> IPIN | |
| # | |
| src_track_node = graph_nodes[idx] | |
| dest_node = other_connector.pins.edge_map[pin_dir] | |
| site_pin_switch_pkey, dest_wire_node = self.find_wire_node( | |
| dest_wire_pkey, dest_node, src_track_node | |
| ) | |
| switch_pkey = pip.get_pip_switch( | |
| src_wire_pkey, dest_wire_pkey | |
| ) | |
| yield ( | |
| src_track_node, switch_pkey, dest_wire_node, | |
| pip.pip_pkey | |
| ) | |
| yield ( | |
| dest_wire_node, site_pin_switch_pkey, dest_node, None | |
| ) | |
| return | |
| elif self.pins and other_connector.pins and not pip.is_pseudo: | |
| assert self.pins.site_pin_direction == SitePinDirection.OUT, dict( | |
| pip | |
| ) | |
| assert other_connector.pins.site_pin_direction == SitePinDirection.IN, dict( | |
| pip | |
| ) | |
| switch_pkey = self.get_edge_with_mux_switch( | |
| src_wire_pkey, pip.pip_pkey, dest_wire_pkey | |
| ) | |
| if len(self.pins.edge_map) == 1 and len( | |
| other_connector.pins.edge_map) == 1: | |
| # If there is only one choice, make it. | |
| src_node = list(self.pins.edge_map.values())[0] | |
| dest_node = list(other_connector.pins.edge_map.values())[0] | |
| yield (src_node, switch_pkey, dest_node, pip.pip_pkey) | |
| return | |
| for pin_dir in self.pins.edge_map: | |
| if OPPOSITE_DIRECTIONS[pin_dir | |
| ] in other_connector.pins.edge_map: | |
| src_node = self.pins.edge_map[pin_dir] | |
| dest_node = other_connector.pins.edge_map[ | |
| OPPOSITE_DIRECTIONS[pin_dir]] | |
| yield (src_node, switch_pkey, dest_node, pip.pip_pkey) | |
| return | |
| # If there is a pseudo pip that needs to be explicitly routed through, | |
| # two CHAN nodes are first created before and after IPIN and OPIN and | |
| # connected with an edge accordingly | |
| elif self.pins and other_connector.pins and pip.is_pseudo: | |
| switch_pkey = pip.get_pip_switch(src_wire_pkey, dest_wire_pkey) | |
| for pin_dir in self.pins.edge_map: | |
| if pin_dir in other_connector.pins.edge_map: | |
| src_node = self.pins.edge_map[pin_dir] | |
| src_node_type = get_node_type(self.conn, src_node) | |
| assert NodeType( | |
| src_node_type | |
| ) == NodeType.IPIN, "src node for ppip is not an IPIN ({}, {})".format( | |
| src_node, src_node_type | |
| ) | |
| dest_node = other_connector.pins.edge_map[pin_dir] | |
| dest_node_type = get_node_type(self.conn, dest_node) | |
| assert NodeType( | |
| dest_node_type | |
| ) == NodeType.OPIN, "dest node for ppip is not an OPIN ({}, {})".format( | |
| src_node, dest_node_type | |
| ) | |
| src_wire_switch_pkey, src_wire_node = self.find_wire_node( | |
| src_wire_pkey, src_node, None | |
| ) | |
| dest_wire_switch_pkey, dest_wire_node = self.find_wire_node( | |
| dest_wire_pkey, dest_node, None | |
| ) | |
| yield ( | |
| src_wire_node, switch_pkey, dest_wire_node, | |
| pip.pip_pkey | |
| ) | |
| return | |
| assert False, ( | |
| self.tracks, self.pins, other_connector.tracks, | |
| other_connector.pins, loc | |
| ) | |
| def create_find_connector(conn): | |
| """ Returns a function returns a Connector object for a given wire and node. | |
| Args: | |
| conn: Database connection | |
| Returns: | |
| Function. See find_connector below for signature. | |
| """ | |
| c = conn.cursor() | |
| @functools.lru_cache(maxsize=100000) | |
| def find_connector(wire_pkey, node_pkey): | |
| """ Finds Connector for a wire and node in the database. | |
| Args: | |
| wire_pkey (int): Primary key into wire table of target wire | |
| node_pkey (int): Primary key into node table of parent node of | |
| specified wire. | |
| Returns: | |
| None if wire is disconnected, otherwise returns Connector objet. | |
| """ | |
| # Find all graph_nodes for this node. | |
| c.execute( | |
| """ | |
| SELECT pkey, track_pkey, graph_node_type, x_low, x_high, y_low, y_high FROM graph_node | |
| WHERE node_pkey = ?;""", (node_pkey, ) | |
| ) | |
| graph_nodes = c.fetchall() | |
| # If there are no graph nodes, this wire is likely disconnected. | |
| if len(graph_nodes) == 0: | |
| return | |
| # If this is a track (e.g. track_pkey is not NULL), then verify | |
| # all graph_nodes for the specified node belong to the same track, | |
| # and then retrieved and return the connector for the track. | |
| track_pkey = graph_nodes[0][1] | |
| if track_pkey is not None: | |
| for node in graph_nodes: | |
| assert node[1] == track_pkey | |
| return Connector( | |
| conn=conn, tracks=get_track_model(conn, track_pkey) | |
| ) | |
| # Check if this node has a special track. This is being used to | |
| # denote the GND and VCC track connections on TIEOFF HARD0 and HARD1. | |
| c.execute( | |
| """ | |
| SELECT | |
| track_pkey, | |
| site_wire_pkey | |
| FROM | |
| node | |
| WHERE | |
| pkey = ?;""", (node_pkey, ) | |
| ) | |
| for track_pkey, site_wire_pkey in c: | |
| if track_pkey is not None and site_wire_pkey is not None: | |
| return Connector( | |
| conn=conn, tracks=get_track_model(conn, track_pkey) | |
| ) | |
| # This is not a track, so it must be a site pin. Make sure the | |
| # graph_nodes share a type and verify that it is in fact a site pin. | |
| node_type = graph2.NodeType(graph_nodes[0][2]) | |
| for node in graph_nodes: | |
| assert node_type == graph2.NodeType( | |
| node[2] | |
| ), (node_pkey, node_type, graph2.NodeType(node[2])) | |
| assert node_type in [graph2.NodeType.IPIN, graph2.NodeType.OPIN] | |
| if node_type == graph2.NodeType.IPIN: | |
| site_pin_direction = SitePinDirection.IN | |
| elif node_type == graph2.NodeType.OPIN: | |
| site_pin_direction = SitePinDirection.OUT | |
| else: | |
| assert False, node_type | |
| # Build the edge_map (map of edge direction to graph node). | |
| c.execute( | |
| """ | |
| SELECT | |
| top_graph_node_pkey, | |
| bottom_graph_node_pkey, | |
| left_graph_node_pkey, | |
| right_graph_node_pkey | |
| FROM | |
| wire | |
| WHERE | |
| node_pkey = ?;""", (node_pkey, ) | |
| ) | |
| all_graph_node_pkeys = c.fetchall() | |
| graph_node_pkeys = None | |
| for keys in all_graph_node_pkeys: | |
| if any(keys): | |
| assert graph_node_pkeys is None | |
| graph_node_pkeys = keys | |
| # This wire may not have an connections, if so return now. | |
| if graph_node_pkeys is None: | |
| return | |
| edge_map = {} | |
| for edge, graph_node in zip( | |
| ( | |
| tracks.Direction.TOP, | |
| tracks.Direction.BOTTOM, | |
| tracks.Direction.LEFT, | |
| tracks.Direction.RIGHT, | |
| ), | |
| graph_node_pkeys, | |
| ): | |
| if graph_node is not None: | |
| edge_map[edge] = graph_node | |
| assert len(edge_map) == len(graph_nodes), ( | |
| edge_map, graph_node_pkeys, graph_nodes | |
| ) | |
| # Make sure that all graph nodes for this wire are in the edge_map | |
| # and at the same grid coordinate. | |
| x = graph_nodes[0][3] | |
| y = graph_nodes[0][5] | |
| for pkey, _, _, x_low, x_high, y_low, y_high in graph_nodes: | |
| assert x == x_low, (wire_pkey, node_pkey, x, x_low, x_high) | |
| assert x == x_high, (wire_pkey, node_pkey, x, x_low, x_high) | |
| assert y == y_low, (wire_pkey, node_pkey, y, y_low, y_high) | |
| assert y == y_high, (wire_pkey, node_pkey, y, y_low, y_high) | |
| assert pkey in edge_map.values(), (pkey, edge_map) | |
| return Connector( | |
| conn=conn, | |
| pins=Pins( | |
| edge_map=edge_map, | |
| x=x, | |
| y=y, | |
| site_pin_direction=site_pin_direction, | |
| ) | |
| ) | |
| return find_connector | |
| def create_const_connectors(conn): | |
| c = conn.cursor() | |
| c.execute( | |
| """ | |
| SELECT vcc_track_pkey, gnd_track_pkey FROM constant_sources; | |
| """ | |
| ) | |
| vcc_track_pkey, gnd_track_pkey = c.fetchone() | |
| const_connectors = {} | |
| const_connectors[0] = Connector( | |
| conn=conn, tracks=get_track_model(conn, gnd_track_pkey) | |
| ) | |
| const_connectors[1] = Connector( | |
| conn=conn, tracks=get_track_model(conn, vcc_track_pkey) | |
| ) | |
| return const_connectors | |
| def create_get_tile_loc(conn): | |
| c = conn.cursor() | |
| @functools.lru_cache(maxsize=None) | |
| def get_tile_loc(tile_pkey): | |
| c.execute( | |
| "SELECT grid_x, grid_y FROM tile WHERE pkey = ?", (tile_pkey, ) | |
| ) | |
| return grid_types.GridLoc(*c.fetchone()) | |
| return get_tile_loc | |
| def yield_edges( | |
| const_connectors, delayless_switch, phy_tile_pkey, src_connector, | |
| sink_connector, pip, pip_obj, src_wire_pkey, sink_wire_pkey, loc, | |
| forward | |
| ): | |
| if forward: | |
| for (src_graph_node_pkey, switch_pkey, dest_graph_node_pkey, | |
| pip_pkey) in src_connector.connect_at( | |
| pip=pip_obj, src_wire_pkey=src_wire_pkey, | |
| dest_wire_pkey=sink_wire_pkey, loc=loc, | |
| other_connector=sink_connector): | |
| assert switch_pkey is not None, ( | |
| pip, src_graph_node_pkey, dest_graph_node_pkey, phy_tile_pkey, | |
| pip_pkey | |
| ) | |
| yield ( | |
| src_graph_node_pkey, dest_graph_node_pkey, switch_pkey, | |
| phy_tile_pkey, pip_pkey, False | |
| ) | |
| if not forward and not pip.is_directional: | |
| for (src_graph_node_pkey, switch_pkey, dest_graph_node_pkey, | |
| pip_pkey) in sink_connector.connect_at( | |
| pip=pip_obj, src_wire_pkey=sink_wire_pkey, | |
| dest_wire_pkey=src_wire_pkey, loc=loc, | |
| other_connector=src_connector): | |
| assert switch_pkey is not None, ( | |
| pip, src_graph_node_pkey, dest_graph_node_pkey, phy_tile_pkey, | |
| pip_pkey | |
| ) | |
| yield ( | |
| src_graph_node_pkey, dest_graph_node_pkey, switch_pkey, | |
| phy_tile_pkey, pip_pkey, True | |
| ) | |
| if forward: | |
| # Make additional connections to constant network if the sink needs it. | |
| for constant_src in yield_ties_to_wire(pip.net_to): | |
| for (src_graph_node_pkey, switch_pkey, dest_graph_node_pkey | |
| ) in const_connectors[constant_src].connect_at( | |
| pip=delayless_switch, loc=loc, | |
| other_connector=sink_connector): | |
| assert switch_pkey is not None, ( | |
| pip, src_graph_node_pkey, dest_graph_node_pkey, | |
| phy_tile_pkey, pip_pkey | |
| ) | |
| yield ( | |
| src_graph_node_pkey, dest_graph_node_pkey, switch_pkey, | |
| phy_tile_pkey, None, False | |
| ) | |
| def make_connection( | |
| conn, input_only_nodes, output_only_nodes, find_wire, find_pip, | |
| find_connector, get_tile_loc, tile_name, tile_type, pip, | |
| delayless_switch, const_connectors, forward | |
| ): | |
| """ Attempt to connect graph nodes on either side of a pip. | |
| Args: | |
| input_only_nodes (set of node_pkey): Nodes that can only be used as | |
| sinks. This is because a synthetic tile will use this node as a | |
| source. | |
| output_only_nodes (set of node_pkey): Nodes that can only be used as | |
| sources. This is because a synthetic tile will use this node as a | |
| sink. | |
| find_wire (function): Return value from create_find_wire. | |
| find_pip (function): Return value from create_find_pip. | |
| find_connector (function): Return value from create_find_connector. | |
| tile_name (str): Name of tile pip belongs too. | |
| pip (prjxray.tile.Pip): Pip being connected. | |
| switch_pkey (int): Primary key to switch table of switch to be used | |
| in this connection. | |
| Returns: | |
| None if connection cannot be made, otherwise returns tuple of: | |
| src_graph_node_pkey (int) - Primary key into graph_node table of | |
| source. | |
| dest_graph_node_pkey (int) - Primary key into graph_node table of | |
| destination. | |
| switch_pkey (int) - Primary key into switch table of switch used | |
| in connection. | |
| phy_tile_pkey (int) - Primary key into table of parent physical | |
| tile of the pip. | |
| pip_pkey (int) - Primary key into pip_in_tile table for this pip. | |
| """ | |
| src_wire_pkey, tile_pkey, phy_tile_pkey, src_node_pkey = find_wire( | |
| tile_name, tile_type, pip.net_from | |
| ) | |
| sink_wire_pkey, tile_pkey2, phy_tile_pkey2, sink_node_pkey = find_wire( | |
| tile_name, tile_type, pip.net_to | |
| ) | |
| assert phy_tile_pkey == phy_tile_pkey2 | |
| # Skip nodes that are reserved because of ROI | |
| if src_node_pkey in input_only_nodes: | |
| return | |
| if sink_node_pkey in output_only_nodes: | |
| return | |
| src_connector = find_connector(src_wire_pkey, src_node_pkey) | |
| if src_connector is None: | |
| return | |
| sink_connector = find_connector(sink_wire_pkey, sink_node_pkey) | |
| if sink_connector is None: | |
| return | |
| pip_obj = find_pip(tile_type, pip.name) | |
| # Generally pseudo-pips are skipped, with the exception for BUFHCE related pips, | |
| # for which we want to create a routing path to have VPR route thorugh these pips. | |
| assert not pip_obj.is_pseudo or "CLK_HROW_CK" in pip.name | |
| loc = get_tile_loc(tile_pkey) | |
| for edge in yield_edges( | |
| const_connectors=const_connectors, | |
| delayless_switch=delayless_switch, phy_tile_pkey=phy_tile_pkey, | |
| src_connector=src_connector, sink_connector=sink_connector, | |
| pip=pip, pip_obj=pip_obj, src_wire_pkey=src_wire_pkey, | |
| sink_wire_pkey=sink_wire_pkey, loc=loc, forward=forward): | |
| yield edge | |
| def mark_track_liveness(conn, input_only_nodes, output_only_nodes): | |
| """ Checks tracks for liveness. | |
| Iterates over all graph nodes that are routing tracks and determines if | |
| at least one graph edge originates from or two the track. | |
| Args: | |
| conn (sqlite3.Connection): Connection database | |
| """ | |
| alive_tracks = set() | |
| c = conn.cursor() | |
| write_cur = conn.cursor() | |
| for graph_node_pkey, node_pkey, track_pkey in c.execute(""" | |
| SELECT | |
| pkey, | |
| node_pkey, | |
| track_pkey | |
| FROM | |
| graph_node | |
| WHERE | |
| track_pkey IS NOT NULL;"""): | |
| if track_pkey in alive_tracks: | |
| continue | |
| if node_pkey in input_only_nodes or node_pkey in output_only_nodes: | |
| alive_tracks.add(track_pkey) | |
| continue | |
| write_cur.execute( | |
| """SELECT count(switch_pkey) FROM graph_edge WHERE | |
| src_graph_node_pkey = ?;""", (graph_node_pkey, ) | |
| ) | |
| src_count = write_cur.fetchone()[0] | |
| write_cur.execute( | |
| """SELECT count(switch_pkey) FROM graph_edge WHERE | |
| dest_graph_node_pkey = ?;""", (graph_node_pkey, ) | |
| ) | |
| sink_count = write_cur.fetchone()[0] | |
| write_cur.execute( | |
| """ | |
| SELECT count() FROM ( | |
| SELECT dest_graph_node_pkey FROM graph_edge WHERE src_graph_node_pkey = ? | |
| UNION | |
| SELECT src_graph_node_pkey FROM graph_edge WHERE dest_graph_node_pkey = ? | |
| );""", (graph_node_pkey, graph_node_pkey) | |
| ) | |
| active_other_nodes = write_cur.fetchone()[0] | |
| if src_count > 0 and sink_count > 0 and active_other_nodes > 1: | |
| alive_tracks.add(track_pkey) | |
| c.execute("SELECT count(pkey) FROM track;") | |
| track_count = c.fetchone()[0] | |
| print( | |
| "{} Alive tracks {} / {}".format( | |
| now(), len(alive_tracks), track_count | |
| ) | |
| ) | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| for (track_pkey, ) in c.execute("""SELECT pkey FROM track;"""): | |
| write_cur.execute( | |
| "UPDATE track SET alive = ? WHERE pkey = ?;", | |
| (track_pkey in alive_tracks, track_pkey) | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| print('{} Track aliveness committed'.format(now())) | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| write_cur.execute("""CREATE INDEX alive_tracks ON track(alive);""") | |
| write_cur.execute( | |
| """CREATE INDEX graph_node_x_index ON graph_node(x_low);""" | |
| ) | |
| write_cur.execute( | |
| """CREATE INDEX graph_node_y_index ON graph_node(y_low);""" | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| def direction_to_enum(pin): | |
| """ Converts string to tracks.Direction. """ | |
| for direction in tracks.Direction: | |
| if direction._name_ == pin: | |
| return direction | |
| assert False | |
| def build_channels(conn): | |
| x_channel_models = {} | |
| y_channel_models = {} | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT MIN(x_low), MAX(x_high), MIN(y_low), MAX(y_high) FROM graph_node | |
| INNER JOIN track | |
| ON track.pkey = graph_node.track_pkey | |
| WHERE track.alive;""" | |
| ) | |
| x_min, x_max, y_min, y_max = cur.fetchone() | |
| for x in progressbar_utils.progressbar(range(x_min, x_max + 1)): | |
| cur.execute( | |
| """ | |
| SELECT | |
| graph_node.y_low, | |
| graph_node.y_high, | |
| graph_node.pkey | |
| FROM graph_node | |
| INNER JOIN track | |
| ON track.pkey = graph_node.track_pkey | |
| WHERE | |
| track_pkey IS NOT NULL | |
| AND | |
| track.alive | |
| AND | |
| graph_node_type = ? | |
| AND | |
| x_low = ?;""", (graph2.NodeType.CHANY.value, x) | |
| ) | |
| data = list(cur) | |
| y_channel_models[x] = graph2.process_track(data) | |
| for y in progressbar_utils.progressbar(range(y_min, y_max + 1)): | |
| cur.execute( | |
| """ | |
| SELECT | |
| graph_node.x_low, | |
| graph_node.x_high, | |
| graph_node.pkey | |
| FROM graph_node | |
| INNER JOIN track | |
| ON track.pkey = graph_node.track_pkey | |
| WHERE | |
| track_pkey IS NOT NULL | |
| AND | |
| track.alive | |
| AND | |
| graph_node_type = ? | |
| AND | |
| y_low = ?;""", (graph2.NodeType.CHANX.value, y) | |
| ) | |
| data = list(cur) | |
| x_channel_models[y] = graph2.process_track(data) | |
| x_list = [] | |
| y_list = [] | |
| write_cur = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| for y in progressbar_utils.progressbar(range(y_max + 1)): | |
| if y in x_channel_models: | |
| x_list.append(len(x_channel_models[y].trees)) | |
| for idx, tree in enumerate(x_channel_models[y].trees): | |
| for i in tree: | |
| write_cur.execute( | |
| 'UPDATE graph_node SET ptc = ? WHERE pkey = ?;', | |
| (idx, i[2]) | |
| ) | |
| else: | |
| x_list.append(0) | |
| for x in progressbar_utils.progressbar(range(x_max + 1)): | |
| if x in y_channel_models: | |
| y_list.append(len(y_channel_models[x].trees)) | |
| for idx, tree in enumerate(y_channel_models[x].trees): | |
| for i in tree: | |
| write_cur.execute( | |
| 'UPDATE graph_node SET ptc = ? WHERE pkey = ?;', | |
| (idx, i[2]) | |
| ) | |
| else: | |
| y_list.append(0) | |
| write_cur.execute( | |
| """ | |
| INSERT INTO channel(chan_width_max, x_min, x_max, y_min, y_max) VALUES | |
| (?, ?, ?, ?, ?);""", | |
| (max(max(x_list), max(y_list)), x_min, x_max, y_min, y_max) | |
| ) | |
| for idx, info in enumerate(x_list): | |
| write_cur.execute( | |
| """ | |
| INSERT INTO x_list(idx, info) VALUES (?, ?);""", (idx, info) | |
| ) | |
| for idx, info in enumerate(y_list): | |
| write_cur.execute( | |
| """ | |
| INSERT INTO y_list(idx, info) VALUES (?, ?);""", (idx, info) | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| def verify_channels(conn): | |
| """ Verify PTC numbers in channels. | |
| No duplicate PTC's. | |
| Violation of this requirement results in a check failure during rr graph | |
| loading. | |
| """ | |
| c = conn.cursor() | |
| chan_ptcs = {} | |
| for (graph_node_pkey, alive, graph_node_type, x_low, x_high, y_low, y_high, | |
| ptc, capacity) in c.execute( | |
| """ | |
| SELECT | |
| graph_node.pkey, | |
| track.alive, | |
| graph_node.graph_node_type, | |
| graph_node.x_low, | |
| graph_node.x_high, | |
| graph_node.y_low, | |
| graph_node.y_high, | |
| graph_node.ptc, | |
| graph_node.capacity | |
| FROM graph_node | |
| INNER JOIN track | |
| ON graph_node.track_pkey = track.pkey | |
| WHERE (graph_node_type = ? or graph_node_type = ?);""", | |
| (graph2.NodeType.CHANX.value, graph2.NodeType.CHANY.value)): | |
| if not alive and capacity != 0: | |
| assert ptc is None, graph_node_pkey | |
| continue | |
| assert ptc is not None, graph_node_pkey | |
| for x in range(x_low, x_high + 1): | |
| for y in range(y_low, y_high + 1): | |
| key = (graph_node_type, x, y) | |
| if key not in chan_ptcs: | |
| chan_ptcs[key] = [] | |
| chan_ptcs[key].append((graph_node_pkey, ptc)) | |
| for key in chan_ptcs: | |
| ptcs = {} | |
| for graph_node_pkey, ptc in chan_ptcs[key]: | |
| assert ptc not in ptcs, (ptcs[ptc], graph_node_pkey) | |
| ptcs[ptc] = graph_node_pkey | |
| def set_pin_connection( | |
| conn, write_cur, pin_graph_node_pkey, forward, graph_node_pkey, tracks | |
| ): | |
| """ Sets pin connection box location canonical location. | |
| Tracks that are a part of the pinfeed also get this location. | |
| """ | |
| cur = conn.cursor() | |
| cur2 = conn.cursor() | |
| cur.execute( | |
| """SELECT node_pkey, graph_node_type FROM graph_node WHERE pkey = ?""", | |
| (pin_graph_node_pkey, ) | |
| ) | |
| pin_node_pkey, graph_node_type = cur.fetchone() | |
| source_wires = [] | |
| sink_wires = [] | |
| cur.execute( | |
| """SELECT pkey FROM wire WHERE node_pkey = ( | |
| SELECT node_pkey FROM graph_node WHERE pkey = ? | |
| )""", (graph_node_pkey, ) | |
| ) | |
| for (wire_pkey, ) in cur: | |
| cur2.execute( | |
| """SELECT count() FROM pip_in_tile WHERE src_wire_in_tile_pkey = ( | |
| SELECT wire_in_tile_pkey FROM wire WHERE pkey = ? | |
| ) AND pip_in_tile.is_pseudo = 0""", (wire_pkey, ) | |
| ) | |
| has_forward_pip = cur2.fetchone()[0] | |
| cur2.execute( | |
| """SELECT count() FROM pip_in_tile WHERE dest_wire_in_tile_pkey = ( | |
| SELECT wire_in_tile_pkey FROM wire WHERE pkey = ? | |
| ) AND pip_in_tile.is_pseudo = 0""", (wire_pkey, ) | |
| ) | |
| has_backward_pip = cur2.fetchone()[0] | |
| if forward: | |
| if has_forward_pip: | |
| source_wires.append(wire_pkey) | |
| if has_backward_pip: | |
| sink_wires.append(wire_pkey) | |
| else: | |
| if has_forward_pip: | |
| sink_wires.append(wire_pkey) | |
| if has_backward_pip: | |
| source_wires.append(wire_pkey) | |
| if len(source_wires) > 1: | |
| if forward: | |
| # Ambiguous output location, just use input pips, which should | |
| # have only 1 phy_tile location. | |
| cur2.execute( | |
| """ | |
| WITH wires_in_graph_node(phy_tile_pkey, phy_tile_type_pkey, wire_in_tile_pkey) AS ( | |
| SELECT wire.phy_tile_pkey, phy_tile.tile_type_pkey, wire.wire_in_tile_pkey | |
| FROM graph_node | |
| INNER JOIN wire ON wire.node_pkey = graph_node.node_pkey | |
| INNER JOIN phy_tile ON wire.phy_tile_pkey = phy_tile.pkey | |
| WHERE graph_node.pkey = ? | |
| ) | |
| SELECT DISTINCT wire.phy_tile_pkey, pip_in_tile.is_directional | |
| FROM wires_in_graph_node | |
| INNER JOIN pip_in_tile | |
| ON | |
| pip_in_tile.dest_wire_in_tile_pkey = wires_in_graph_node.wire_in_tile_pkey | |
| AND | |
| pip_in_tile.tile_type_pkey = wires_in_graph_node.phy_tile_type_pkey | |
| INNER JOIN wire | |
| ON | |
| wire.wire_in_tile_pkey = pip_in_tile.src_wire_in_tile_pkey | |
| AND | |
| wire.phy_tile_pkey = wires_in_graph_node.phy_tile_pkey; | |
| """, (graph_node_pkey, ) | |
| ) | |
| src_phy_tiles = cur2.fetchall() | |
| if len(src_phy_tiles) > 1: | |
| # Try pruning bi-directional pips | |
| src_phy_tiles = [ | |
| (phy_tile_pkey, is_directional) | |
| for (phy_tile_pkey, is_directional) in src_phy_tiles | |
| if is_directional | |
| ] | |
| assert len(src_phy_tiles) == 1, ( | |
| pin_graph_node_pkey, graph_node_pkey, source_wires, tracks, | |
| src_phy_tiles | |
| ) | |
| phy_tile_pkey = src_phy_tiles[0][0] | |
| else: | |
| # Have an ambiguous source, see if there is an unambigous sink. | |
| # | |
| # Remove sinks that are also sources (e.g. bidirectional wires) | |
| sink_wires = list(set(sink_wires) - set(source_wires)) | |
| if len(sink_wires) == 1: | |
| cur.execute( | |
| "SELECT phy_tile_pkey FROM wire WHERE pkey = ?", | |
| (sink_wires[0], ) | |
| ) | |
| source_wires = sink_wires | |
| phy_tile_pkey = cur.fetchone()[0] | |
| else: | |
| assert False, ( | |
| pin_graph_node_pkey, graph_node_pkey, source_wires, | |
| sink_wires, tracks | |
| ) | |
| return | |
| elif len(source_wires) == 1: | |
| cur.execute( | |
| "SELECT phy_tile_pkey FROM wire WHERE pkey = ?", | |
| (source_wires[0], ) | |
| ) | |
| phy_tile_pkey = cur.fetchone()[0] | |
| elif len(sink_wires) == 1: | |
| cur.execute( | |
| "SELECT phy_tile_pkey FROM wire WHERE pkey = ?", (sink_wires[0], ) | |
| ) | |
| source_wires = sink_wires | |
| phy_tile_pkey = cur.fetchone()[0] | |
| else: | |
| return | |
| for track_pkey in tracks: | |
| write_cur.execute( | |
| "UPDATE track SET canon_phy_tile_pkey = ? WHERE pkey = ?", ( | |
| phy_tile_pkey, | |
| track_pkey, | |
| ) | |
| ) | |
| if not forward: | |
| assert NodeType(graph_node_type) == NodeType.IPIN | |
| source_wire_pkey = source_wires[0] | |
| write_cur.execute( | |
| """ | |
| UPDATE graph_node SET connection_box_wire_pkey = ? WHERE pkey = ? | |
| """, ( | |
| source_wire_pkey, | |
| pin_graph_node_pkey, | |
| ) | |
| ) | |
| def walk_and_mark_segment( | |
| conn, write_cur, graph_node_pkey, forward, segment_pkey, unknown_pkey, | |
| pin_graph_node_pkey, tracks, visited_nodes | |
| ): | |
| """ Recursive function to walk along a node and mark segments. | |
| This algorithm is used for marking INPINFEED and OUTPINFEED on nodes | |
| starting from an IPIN or OPIN edge. | |
| In addition, the canonical location of the connection box IPIN/OPIN nodes | |
| is the canonical location of the routing interface. For example, the | |
| CLBLL_R tile is located to the right of the INT_R tile. The routing | |
| lookahead routes to the INT_R (e.g. a x-1 of the CLBLL_R). So the | |
| canonical location of the CLBLL_R IPIN is the INT_R tile, not the CLBLL_R | |
| tile. | |
| """ | |
| cur = conn.cursor() | |
| # Update track segment's to segment_pkey (e.g. INPINFEED or OUTPINFEED). | |
| cur.execute( | |
| """SELECT graph_node_type FROM graph_node WHERE pkey = ?""", | |
| (graph_node_pkey, ) | |
| ) | |
| graph_node_type = NodeType(cur.fetchone()[0]) | |
| if graph_node_type in [NodeType.CHANX, NodeType.CHANY]: | |
| cur.execute( | |
| "SELECT track_pkey FROM graph_node WHERE pkey = ?", | |
| (graph_node_pkey, ) | |
| ) | |
| track_pkey = cur.fetchone()[0] | |
| assert track_pkey is not None | |
| cur.execute( | |
| "SELECT segment_pkey FROM track WHERE pkey = ?", (track_pkey, ) | |
| ) | |
| old_segment_pkey = cur.fetchone()[0] | |
| if old_segment_pkey == unknown_pkey or old_segment_pkey is None: | |
| tracks.append(track_pkey) | |
| write_cur.execute( | |
| "UPDATE track SET segment_pkey = ? WHERE pkey = ?", ( | |
| segment_pkey, | |
| track_pkey, | |
| ) | |
| ) | |
| else: | |
| track_pkey = None | |
| # Traverse to the next graph node. | |
| if forward: | |
| cur.execute( | |
| """ | |
| SELECT | |
| graph_edge.dest_graph_node_pkey, | |
| graph_node.track_pkey | |
| FROM | |
| graph_edge | |
| INNER JOIN graph_node ON graph_node.pkey = graph_edge.dest_graph_node_pkey | |
| WHERE | |
| src_graph_node_pkey = ? | |
| """, (graph_node_pkey, ) | |
| ) | |
| next_nodes = cur.fetchall() | |
| else: | |
| cur.execute( | |
| """ | |
| SELECT | |
| graph_edge.src_graph_node_pkey, | |
| graph_node.track_pkey | |
| FROM | |
| graph_edge | |
| INNER JOIN graph_node ON graph_node.pkey = graph_edge.src_graph_node_pkey | |
| WHERE | |
| dest_graph_node_pkey = ? | |
| """, (graph_node_pkey, ) | |
| ) | |
| next_nodes = cur.fetchall() | |
| if not forward: | |
| # Some nodes simply lead to GND/VCC tieoff pins, these should not | |
| # stop the walk, as they are not relevant to connection box. | |
| next_non_tieoff_nodes = [] | |
| for (next_graph_node_pkey, next_track) in next_nodes: | |
| cur.execute( | |
| """ | |
| SELECT count() FROM constant_sources WHERE | |
| vcc_track_pkey = (SELECT track_pkey FROM graph_node WHERE pkey = ?) | |
| OR | |
| gnd_track_pkey = (SELECT track_pkey FROM graph_node WHERE pkey = ?) | |
| """, ( | |
| next_graph_node_pkey, | |
| next_graph_node_pkey, | |
| ) | |
| ) | |
| if cur.fetchone()[0] == 0: | |
| next_non_tieoff_nodes.append( | |
| (next_graph_node_pkey, next_track) | |
| ) | |
| if len(next_non_tieoff_nodes) == 1: | |
| (next_node, next_track) = next_non_tieoff_nodes[0] | |
| next_nodes = next_non_tieoff_nodes | |
| if len(next_nodes) == 1: | |
| # This is a simple edge, keep walking. | |
| (next_node, next_track) = next_nodes[0] | |
| else: | |
| next_other_nodes = [] | |
| for next_node, next_track in next_nodes: | |
| # Shorted groups will have edges back to previous nodes, but they | |
| # will be in the same track, so ignore these. | |
| if next_node in visited_nodes and track_pkey == next_track: | |
| continue | |
| else: | |
| next_other_nodes.append((next_node, next_track)) | |
| if len(next_other_nodes) == 1: | |
| # This is a simple edge, keep walking. | |
| (next_node, next_track) = next_other_nodes[0] | |
| else: | |
| next_node = None | |
| if next_node is not None and next_node not in visited_nodes: | |
| # If there is a next node, keep walking | |
| visited_nodes.add(next_node) | |
| walk_and_mark_segment( | |
| conn=conn, | |
| write_cur=write_cur, | |
| graph_node_pkey=next_node, | |
| forward=forward, | |
| segment_pkey=segment_pkey, | |
| unknown_pkey=unknown_pkey, | |
| pin_graph_node_pkey=pin_graph_node_pkey, | |
| tracks=tracks, | |
| visited_nodes=visited_nodes | |
| ) | |
| else: | |
| # There is not a next node, update the connection box of the IPIN/OPIN | |
| # the walk was started from. | |
| set_pin_connection( | |
| conn=conn, | |
| write_cur=write_cur, | |
| pin_graph_node_pkey=pin_graph_node_pkey, | |
| forward=forward, | |
| graph_node_pkey=graph_node_pkey, | |
| tracks=tracks | |
| ) | |
| def active_graph_node(conn, graph_node_pkey, forward): | |
| """ Returns true if an edge in the specified direction exists. """ | |
| cur = conn.cursor() | |
| if forward: | |
| cur.execute( | |
| """ | |
| SELECT count(*) FROM graph_edge WHERE src_graph_node_pkey = ? LIMIT 1 | |
| """, (graph_node_pkey, ) | |
| ) | |
| else: | |
| cur.execute( | |
| """ | |
| SELECT count(*) FROM graph_edge WHERE dest_graph_node_pkey = ? LIMIT 1 | |
| """, (graph_node_pkey, ) | |
| ) | |
| return cur.fetchone()[0] > 0 | |
| def annotate_pin_feeds(conn, ccio_sites): | |
| """ Identifies and annotates pin feed channels. | |
| Some channels are simply paths from IPIN's or OPIN's. Set | |
| pin_classification to either IPIN_FEED or OPIN_FEED. During track creation | |
| if these nodes are not given a specific segment, they will be assigned as | |
| INPINFEED or OUTPINFEED. | |
| """ | |
| write_cur = conn.cursor() | |
| cur = conn.cursor() | |
| segments = {} | |
| for segment_pkey, segment_name in cur.execute( | |
| "SELECT pkey, name FROM segment"): | |
| segments[segment_name] = segment_pkey | |
| # Find BUFHCE OPIN's, so that walk_and_mark_segment uses correct segment | |
| # type. | |
| bufhce_opins = get_pins(conn, "BUFHCE", "O") | |
| # Find BUFGCTRL OPIN's, so that walk_and_mark_segment uses correct segment | |
| # type. | |
| bufg_opins = get_pins(conn, "BUFGCTRL", "O") | |
| # Find BUFGCTRL IPIN's, so that walk_and_mark_segment uses correct segment | |
| # type. | |
| bufg_ipins = set() | |
| for nipins in range(2): | |
| bufg_ipins |= get_pins(conn, "BUFGCTRL", "I{}".format(nipins)) | |
| # Find PLL OPIN's, so that walk_and_mark_segment uses correct segment | |
| # type. | |
| pll_opins = set() | |
| for nclk in range(6): | |
| pll_opins |= get_pins(conn, "PLLE2_ADV", "CLKOUT{}".format(nclk)) | |
| # Find PLL IPIN's, so that walk_and_mark_segment uses correct segment | |
| # type. | |
| pll_ipins = set() | |
| for nclk in range(2): | |
| pll_ipins |= get_pins(conn, "PLLE2_ADV", "CLKIN{}".format(nclk + 1)) | |
| ccio_opins = set() | |
| # Find graph nodes for IOI_ILOGIC0_O for IOPAD_M's that are CCIO tiles ( | |
| # e.g. have dedicate clock paths). | |
| for ccio_site in ccio_sites: | |
| ccio_ilogic = ccio_site.replace('IOB', 'ILOGIC') | |
| cur.execute( | |
| """ | |
| WITH ilogic_o_wires(wire_in_tile_pkey) AS ( | |
| SELECT wire_in_tile.pkey FROM wire_in_tile | |
| INNER JOIN site_pin ON site_pin.pkey = wire_in_tile.site_pin_pkey | |
| INNER JOIN site_type ON site_pin.site_type_pkey = site_type.pkey | |
| WHERE | |
| site_type.name == "ILOGICE3" | |
| AND | |
| site_pin.name == "O" | |
| AND | |
| wire_in_tile.site_pkey IN ( | |
| SELECT site_pkey FROM site_instance WHERE name = ? | |
| ) | |
| ) | |
| SELECT graph_node.pkey FROM graph_node | |
| INNER JOIN wire ON graph_node.node_pkey = wire.node_pkey | |
| WHERE | |
| wire.wire_in_tile_pkey IN (SELECT wire_in_tile_pkey FROM ilogic_o_wires) | |
| AND | |
| wire.phy_tile_pkey = (SELECT phy_tile_pkey FROM site_instance WHERE name = ?); | |
| """, (ccio_ilogic, ccio_ilogic) | |
| ) | |
| for (graph_node_pkey, ) in cur: | |
| ccio_opins.add(graph_node_pkey) | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| # Walk from OPIN's first. | |
| for (graph_node_pkey, node_pkey) in cur.execute(""" | |
| SELECT graph_node.pkey, graph_node.node_pkey | |
| FROM graph_node | |
| WHERE graph_node.graph_node_type = ? | |
| """, (NodeType.OPIN.value, )): | |
| if not active_graph_node(conn, graph_node_pkey, forward=True): | |
| continue | |
| if graph_node_pkey in bufhce_opins: | |
| segment_pkey = segments["HCLK_ROWS"] | |
| elif graph_node_pkey in bufg_opins: | |
| segment_pkey = segments["GCLK_OUTPINFEED"] | |
| elif graph_node_pkey in pll_opins: | |
| segment_pkey = segments["PLL_OUTPINFEED"] | |
| elif graph_node_pkey in ccio_opins: | |
| segment_pkey = segments["CCIO_OUTPINFEED"] | |
| else: | |
| segment_pkey = segments["OUTPINFEED"] | |
| walk_and_mark_segment( | |
| conn, | |
| write_cur, | |
| graph_node_pkey, | |
| forward=True, | |
| segment_pkey=segment_pkey, | |
| unknown_pkey=segments["unknown"], | |
| pin_graph_node_pkey=graph_node_pkey, | |
| tracks=list(), | |
| visited_nodes=set() | |
| ) | |
| # Walk from IPIN's next. | |
| for (graph_node_pkey, ) in cur.execute(""" | |
| SELECT graph_node.pkey | |
| FROM graph_node | |
| WHERE graph_node.graph_node_type = ? | |
| """, (NodeType.IPIN.value, )): | |
| if not active_graph_node(conn, graph_node_pkey, forward=False): | |
| continue | |
| if graph_node_pkey in bufg_ipins: | |
| segment_pkey = segments["GCLK_INPINFEED"] | |
| elif graph_node_pkey in pll_ipins: | |
| segment_pkey = segments["PLL_INPINFEED"] | |
| else: | |
| segment_pkey = segments["INPINFEED"] | |
| walk_and_mark_segment( | |
| conn, | |
| write_cur, | |
| graph_node_pkey, | |
| forward=False, | |
| segment_pkey=segment_pkey, | |
| unknown_pkey=segments["unknown"], | |
| pin_graph_node_pkey=graph_node_pkey, | |
| tracks=list(), | |
| visited_nodes=set() | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| def set_track_canonical_loc(conn): | |
| """ For each track, compute a canonical location. | |
| This canonical location should be consisent across instances of the track | |
| type. For example, a 6 length NW should always have a canonical location | |
| at the SE corner of the the track. | |
| For bidirection wires (generally long segments), use a consisent | |
| canonilization. | |
| """ | |
| write_cur = conn.cursor() | |
| cur = conn.cursor() | |
| cur2 = conn.cursor() | |
| cur3 = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| cur.execute("SELECT pkey FROM track WHERE alive") | |
| tracks = cur.fetchall() | |
| for (track_pkey, ) in progressbar_utils.progressbar(tracks): | |
| source_wires = [] | |
| for (wire_pkey, ) in cur2.execute(""" | |
| SELECT pkey FROM wire WHERE node_pkey IN ( | |
| SELECT pkey FROM node WHERE track_pkey = ? | |
| )""", (track_pkey, )): | |
| cur3.execute( | |
| """ | |
| SELECT count(*) | |
| FROM pip_in_tile | |
| WHERE | |
| dest_wire_in_tile_pkey = (SELECT wire_in_tile_pkey FROM wire WHERE pkey = ?) | |
| LIMIT 1 | |
| """, (wire_pkey, ) | |
| ) | |
| pips_to_wire = cur3.fetchone()[0] | |
| if pips_to_wire > 0: | |
| cur3.execute( | |
| """ | |
| SELECT grid_x, grid_y FROM phy_tile WHERE pkey = ( | |
| SELECT phy_tile_pkey FROM wire WHERE pkey = ? | |
| )""", (wire_pkey, ) | |
| ) | |
| grid_x, grid_y = cur3.fetchone() | |
| source_wires.append(((grid_x, grid_y), wire_pkey)) | |
| if len(source_wires) > 0: | |
| source_wire_pkey = min(source_wires, key=lambda x: x[0])[1] | |
| write_cur.execute( | |
| """ | |
| UPDATE track | |
| SET canon_phy_tile_pkey = (SELECT phy_tile_pkey FROM wire WHERE pkey = ?) | |
| WHERE pkey = ? | |
| """, (source_wire_pkey, track_pkey) | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| def get_segment_length(segment_lengths): | |
| if len(segment_lengths) == 0: | |
| return 1 | |
| median_length = int(math.ceil(numpy.median(segment_lengths))) | |
| return max(1, median_length) | |
| def compute_segment_lengths(conn): | |
| """ Determine segment lengths used for cost normalization. """ | |
| cur = conn.cursor() | |
| cur2 = conn.cursor() | |
| cur3 = conn.cursor() | |
| cur4 = conn.cursor() | |
| write_cur = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| for (segment_pkey, ) in cur.execute("SELECT pkey FROM segment"): | |
| segment_lengths = [] | |
| # Get all tracks with this segment | |
| for (track_pkey, src_phy_tile_pkey) in cur2.execute(""" | |
| SELECT pkey, canon_phy_tile_pkey FROM track | |
| WHERE | |
| canon_phy_tile_pkey IS NOT NULL | |
| AND | |
| segment_pkey = ? | |
| """, (segment_pkey, )): | |
| segment_length = 1 | |
| cur4.execute( | |
| "SELECT grid_x, grid_y FROM phy_tile WHERE pkey = ?", | |
| (src_phy_tile_pkey, ) | |
| ) | |
| src_x, src_y = cur4.fetchone() | |
| # Get tiles downstream of this track. | |
| for (dest_phy_tile_pkey, ) in cur3.execute(""" | |
| SELECT DISTINCT canon_phy_tile_pkey FROM track WHERE pkey IN ( | |
| SELECT track_pkey FROM graph_node WHERE pkey IN ( | |
| SELECT dest_graph_node_pkey FROM graph_edge WHERE src_graph_node_pkey IN ( | |
| SELECT pkey FROM graph_node WHERE track_pkey = ? | |
| ) | |
| ) | |
| ) AND canon_phy_tile_pkey IS NOT NULL | |
| """, (track_pkey, )): | |
| if src_phy_tile_pkey == dest_phy_tile_pkey: | |
| continue | |
| cur4.execute( | |
| "SELECT grid_x, grid_y FROM phy_tile WHERE pkey = ?", | |
| (dest_phy_tile_pkey, ) | |
| ) | |
| dest_x, dest_y = cur4.fetchone() | |
| segment_length = max( | |
| segment_length, | |
| abs(dest_x - src_x) + abs(dest_y - src_y) | |
| ) | |
| segment_lengths.append(segment_length) | |
| write_cur.execute( | |
| "UPDATE segment SET length = ? WHERE pkey = ?", ( | |
| get_segment_length(segment_lengths), | |
| segment_pkey, | |
| ) | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| def commit_edges(write_cur, edges): | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| write_cur.executemany( | |
| """ | |
| INSERT INTO graph_edge( | |
| src_graph_node_pkey, dest_graph_node_pkey, switch_pkey, | |
| phy_tile_pkey, pip_in_tile_pkey, backward) VALUES (?, ?, ?, ?, ?, ?)""", | |
| edges | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| REMOVE_TRAILING_NUM = re.compile(r'[0-9]+$') | |
| def pip_sort_key(forward_pip): | |
| """ Sort pips to match canonical order. """ | |
| forward, pip = forward_pip | |
| count = ( | |
| len(REMOVE_TRAILING_NUM.sub('', pip.net_to)) + | |
| len(REMOVE_TRAILING_NUM.sub('', pip.net_from)) | |
| ) | |
| if forward: | |
| return (pip.is_pseudo, count, pip.net_to, pip.net_from) | |
| else: | |
| return (pip.is_pseudo, count, pip.net_from, pip.net_to) | |
| def make_sorted_pips(pips): | |
| out_pips = [] | |
| for pip in pips: | |
| # Add forward copy of pip | |
| out_pips.append((True, pip)) | |
| # Add backward copy of pip if not directional. | |
| if not pip.is_directional: | |
| out_pips.append((False, pip)) | |
| out_pips.sort(key=pip_sort_key) | |
| return out_pips | |
| def create_edge_indices(conn): | |
| write_cur = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| write_cur.execute( | |
| """CREATE INDEX src_node_index ON graph_edge(src_graph_node_pkey);""" | |
| ) | |
| write_cur.execute( | |
| """CREATE INDEX dest_node_index ON graph_edge(dest_graph_node_pkey);""" | |
| ) | |
| write_cur.execute("""CREATE INDEX node_track_index ON node(track_pkey);""") | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| write_cur.connection.commit() | |
| print('{} Indices created, marking track liveness'.format(now())) | |
| def create_and_insert_edges( | |
| db, grid, conn, use_roi, roi, input_only_nodes, output_only_nodes | |
| ): | |
| write_cur = conn.cursor() | |
| write_cur.execute( | |
| 'SELECT pkey FROM switch WHERE name = ?;', | |
| ('__vpr_delayless_switch__', ) | |
| ) | |
| delayless_switch_pkey = write_cur.fetchone()[0] | |
| delayless_switch = KnownSwitch(delayless_switch_pkey) | |
| find_pip = create_find_pip(conn) | |
| find_wire = create_find_wire(conn) | |
| find_connector = create_find_connector(conn) | |
| get_tile_loc = create_get_tile_loc(conn) | |
| const_connectors = create_const_connectors(conn) | |
| sorted_pips = {} | |
| num_edges = 0 | |
| edges = [] | |
| for loc in progressbar_utils.progressbar(grid.tile_locations()): | |
| edge_set = set() | |
| gridinfo = grid.gridinfo_at_loc(loc) | |
| tile_name = grid.tilename_at_loc(loc) | |
| # Not a synth node, check if in ROI. | |
| if use_roi and not roi.tile_in_roi(loc): | |
| continue | |
| tile_type = db.get_tile_type(gridinfo.tile_type) | |
| if tile_type not in sorted_pips: | |
| sorted_pips[tile_type] = make_sorted_pips(tile_type.get_pips()) | |
| for forward, pip in sorted_pips[tile_type]: | |
| # FIXME: The PADOUT0/1 connections do not work. | |
| # | |
| # These connections are used for: | |
| # - XADC | |
| # - Differential signal signal connection between pads. | |
| # | |
| # Issue tracking fix: | |
| # https://github.com/SymbiFlow/f4pga-arch-defs/issues/1033 | |
| if 'PADOUT0' in pip.name and 'DIFFI_IN1' not in pip.name: | |
| continue | |
| if 'PADOUT1' in pip.name and 'DIFFI_IN0' not in pip.name: | |
| continue | |
| # These edges are used for bringing general interconnect to the | |
| # horizontal clock buffers. This should only be used when routing | |
| # clocks. | |
| if 'CLK_HROW_CK_INT_' in pip.name: | |
| continue | |
| # Generally pseudo-pips are skipped, with the exception for BUFHCE related pips, | |
| # for which we want to create a routing path to have VPR route thorugh these pips. | |
| if pip.is_pseudo and "CLK_HROW_CK" not in pip.name: | |
| continue | |
| # Filter out PIPs related to MIO and DDR pins of the Zynq7 PS. | |
| # These PIPs are actually not there, they are just informative. | |
| if "PS72_" in pip.net_to or "PS72_" in pip.net_from: | |
| continue | |
| connections = make_connection( | |
| conn=conn, | |
| input_only_nodes=input_only_nodes, | |
| output_only_nodes=output_only_nodes, | |
| find_pip=find_pip, | |
| find_wire=find_wire, | |
| find_connector=find_connector, | |
| get_tile_loc=get_tile_loc, | |
| tile_name=tile_name, | |
| tile_type=gridinfo.tile_type, | |
| pip=pip, | |
| delayless_switch=delayless_switch, | |
| const_connectors=const_connectors, | |
| forward=forward, | |
| ) | |
| if connections: | |
| for connection in connections: | |
| key = tuple(connection[0:3]) | |
| if key in edge_set: | |
| continue | |
| edge_set.add(key) | |
| edges.append(connection) | |
| if len(edges) > 1000: | |
| commit_edges(write_cur, edges) | |
| num_edges += len(edges) | |
| edges = [] | |
| print('{} Created {} edges, inserted'.format(now(), num_edges)) | |
| def get_ccio_sites(grid): | |
| ccio_sites = set() | |
| for tile in grid.tiles(): | |
| gridinfo = grid.gridinfo_at_tilename(tile) | |
| for site, pin_function in gridinfo.pin_functions.items(): | |
| if 'SRCC' in pin_function or 'MRCC' in pin_function: | |
| if gridinfo.sites[site][-1] == 'M': | |
| ccio_sites.add(site) | |
| return ccio_sites | |
| def create_edges(args): | |
| db = prjxray.db.Database(args.db_root, args.part) | |
| grid = db.grid() | |
| with DatabaseCache(args.connection_database) as conn: | |
| with open(args.pin_assignments) as f: | |
| pin_assignments = json.load(f) | |
| tile_wires = [] | |
| for tile_type, wire_map in pin_assignments['pin_directions'].items(): | |
| for wire in wire_map.keys(): | |
| tile_wires.append((tile_type, wire)) | |
| for tile_type, wire in progressbar_utils.progressbar(tile_wires): | |
| pins = [ | |
| direction_to_enum(pin) | |
| for pin in pin_assignments['pin_directions'][tile_type][wire] | |
| ] | |
| add_graph_nodes_for_pins(conn, tile_type, wire, pins) | |
| 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, | |
| ) | |
| synth_tiles = {'tiles': {}} | |
| else: | |
| use_roi = False | |
| output_only_nodes = set() | |
| input_only_nodes = set() | |
| print('{} Finding nodes belonging to ROI'.format(now())) | |
| if use_roi: | |
| find_wire = create_find_wire(conn) | |
| for loc in progressbar_utils.progressbar(grid.tile_locations()): | |
| gridinfo = grid.gridinfo_at_loc(loc) | |
| tile_name = grid.tilename_at_loc(loc) | |
| if tile_name in synth_tiles['tiles']: | |
| synth_tile = synth_tiles['tiles'][tile_name] | |
| for pin in synth_tile['pins']: | |
| if pin['port_type'] not in ['input', 'output']: | |
| continue | |
| _, _, _, node_pkey = find_wire( | |
| tile_name, gridinfo.tile_type, pin['wire'] | |
| ) | |
| is_input = pin['port_type'] == 'input' | |
| is_output = pin['port_type'] == 'output' | |
| assert is_input or is_output, pin | |
| if is_input: | |
| # This track can be used as a sink. | |
| input_only_nodes.add(node_pkey) | |
| elif is_output: | |
| # This track can be used as a src. | |
| output_only_nodes.add(node_pkey) | |
| # Adding all wires outside of the ROI relative to this synth tile | |
| # to the input/output only nodes, so that the corresponding nodes | |
| # have only outgoing or incoming edges | |
| for tile, wires in synth_tile['wires_outside_roi' | |
| ].items(): | |
| tile_type = grid.gridinfo_at_tilename( | |
| tile | |
| ).tile_type | |
| for wire in wires: | |
| _, _, _, node_pkey = find_wire( | |
| tile, tile_type, wire | |
| ) | |
| if is_input: | |
| # This track can be used as a sink. | |
| input_only_nodes.add(node_pkey) | |
| elif is_output: | |
| # This track can be used as a src. | |
| output_only_nodes.add(node_pkey) | |
| create_and_insert_edges( | |
| db=db, | |
| grid=grid, | |
| conn=conn, | |
| use_roi=use_roi, | |
| roi=roi if use_roi else None, | |
| input_only_nodes=input_only_nodes, | |
| output_only_nodes=output_only_nodes | |
| ) | |
| create_edge_indices(conn) | |
| mark_track_liveness(conn, input_only_nodes, output_only_nodes) | |
| return get_ccio_sites(grid) |