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