| #!/usr/bin/env python3 | |
| """ Classify 7-series nodes and generate channels for required nodes. | |
| Rough structure: | |
| Create initial database import by importing tile types, tile wires, tile pips, | |
| site types and site pins. After importing tile types, imports the grid of | |
| tiles. This uses tilegrid.json, tile_type_*.json and site_type_*.json. | |
| Once all tiles are imported, all wires in the grid are added and nodes are | |
| formed from the sea of wires based on the tile connections description | |
| (tileconn.json). | |
| In order to determine what each node is used for, site pins and pips are counted | |
| on each node (count_sites_and_pips_on_nodes). Depending on the site pin and | |
| pip count, the nodes are classified into one of 4 buckets: | |
| NULL - An unconnected node | |
| CHANNEL - A routing node | |
| EDGE_WITH_MUX - An edge between an IPIN and OPIN. | |
| EDGES_TO_CHANNEL - An edge between an IPIN/OPIN and a CHANNEL. | |
| Then all CHANNEL are grouped into tracks (form_tracks) and graph nodes are | |
| created for the CHANNELs. Graph edges are added to connect graph nodes that are | |
| part of the same track. | |
| Note that IPIN and OPIN graph nodes are not added yet, as pins have not been | |
| assigned sides of the VPR tiles yet. This occurs in | |
| prjxray_assign_tile_pin_direction. | |
| """ | |
| import argparse | |
| import csv | |
| import prjxray.db | |
| import prjxray.tile | |
| from prjxray.timing import PvtCorner | |
| from lib import progressbar_utils | |
| import tile_splitter.grid | |
| from lib.rr_graph import points | |
| from lib.rr_graph import tracks | |
| from lib.rr_graph import graph2 | |
| import datetime | |
| import os | |
| import os.path | |
| from lib.connection_database import NodeClassification, create_tables, node_to_site_pins | |
| from prjxray_db_cache import DatabaseCache | |
| from prjxray_define_segments import SegmentWireMap | |
| SINGLE_PRECISION_FLOAT_MIN = 2**-126 | |
| VCC_NET = 'VCC_NET' | |
| GND_NET = 'GND_NET' | |
| def import_site_type(db, write_cur, site_types, site_type_name): | |
| assert site_type_name not in site_types | |
| site_type = db.get_site_type(site_type_name) | |
| if site_type_name in site_types: | |
| return | |
| write_cur.execute( | |
| "INSERT INTO site_type(name) VALUES (?)", (site_type_name, ) | |
| ) | |
| site_types[site_type_name] = write_cur.lastrowid | |
| for site_pin in site_type.get_site_pins(): | |
| pin_info = site_type.get_site_pin(site_pin) | |
| write_cur.execute( | |
| """ | |
| INSERT INTO site_pin(name, site_type_pkey, direction) | |
| VALUES | |
| (?, ?, ?)""", ( | |
| pin_info.name, site_types[site_type_name], | |
| pin_info.direction.value | |
| ) | |
| ) | |
| def create_get_switch(conn): | |
| """ Returns functions to get or create switches with various timing. | |
| Every switch that requires different timing is given it's own switch | |
| in VPR. get_switch returns a switch a pip. get_switch_timing returns | |
| a switch given a particular timing. | |
| """ | |
| write_cur = conn.cursor() | |
| pip_cache = {} | |
| write_cur.execute( | |
| "SELECT pkey FROM switch WHERE name = ?", | |
| ("__vpr_delayless_switch__", ) | |
| ) | |
| pip_cache[(False, 0.0, 0.0, 0.0)] = write_cur.fetchone()[0] | |
| def get_switch_timing( | |
| is_pass_transistor, delay, internal_capacitance, drive_resistance | |
| ): | |
| """ Return a switch that matches provided timing. | |
| Arguments | |
| --------- | |
| is_pass_transistor : bool-like | |
| If true, this switch should be represented as a "pass_gate". | |
| delay : float or convertable to float | |
| Intrinsic delay through switch (seconds) | |
| internal_capacitance : float or convertable to float | |
| Internal capacitance to switch (Farads). | |
| drive_resistance : float or convertable to float | |
| Drive resistance from switch (Ohms). | |
| Returns | |
| ------- | |
| switch_pkey : int | |
| Switch primary key that represents provided arguments. | |
| """ | |
| key = ( | |
| bool(is_pass_transistor), float(delay), float(drive_resistance), | |
| float(internal_capacitance) | |
| ) | |
| if key not in pip_cache: | |
| name = 'routing' | |
| switch_type = 'mux' | |
| if is_pass_transistor: | |
| name = 'pass_transistor' | |
| switch_type = 'pass_gate' | |
| name = '{}_R{}_C{}_Tdel{}'.format( | |
| name, drive_resistance, internal_capacitance, delay | |
| ) | |
| write_cur.execute( | |
| """ | |
| INSERT INTO | |
| switch( | |
| name, internal_capacitance, drive_resistance, intrinsic_delay, switch_type | |
| ) | |
| VALUES | |
| (?, ?, ?, ?, ?)""", ( | |
| name, internal_capacitance, drive_resistance, delay, | |
| switch_type | |
| ) | |
| ) | |
| pip_cache[key] = write_cur.lastrowid | |
| write_cur.connection.commit() | |
| return pip_cache[key] | |
| def get_switch(pip, pip_timing): | |
| """ Return switch_pkey for given pip timing. | |
| Arguments | |
| --------- | |
| pip : tile.Pip object | |
| Pip being modelled | |
| pip_timing : tile.PipTiming object | |
| Pip timing being modelled | |
| Returns | |
| ------- | |
| switch_pkey : int | |
| Switch primary key that represents provided arguments. | |
| """ | |
| delay = 0.0 | |
| drive_resistance = 0.0 | |
| internal_capacitance = 0.0 | |
| if pip_timing is not None: | |
| if pip_timing.delays is not None: | |
| # Use the largest intristic delay for now. | |
| # This conservative on slack timing, but not on hold timing. | |
| # | |
| # nanosecond -> seconds | |
| delay = pip_timing.delays[PvtCorner.SLOW].max / 1e9 | |
| if pip_timing.internal_capacitance is not None: | |
| # microFarads -> Farads | |
| internal_capacitance = pip_timing.internal_capacitance / 1e6 | |
| if pip_timing.drive_resistance is not None: | |
| # milliOhms -> Ohms | |
| drive_resistance = pip_timing.drive_resistance / 1e3 | |
| return get_switch_timing( | |
| pip.is_pass_transistor, delay, internal_capacitance, | |
| drive_resistance | |
| ) | |
| return get_switch, get_switch_timing | |
| def build_pss_object_mask(db, tile_type_name): | |
| """ | |
| Looks for objects present in PSS* tiles of Zynq7 and masks out those | |
| that are purely PS related and not configued by the PL. | |
| """ | |
| tile_type = db.get_tile_type(tile_type_name) | |
| sites = tile_type.get_sites() | |
| masked_wires = [] | |
| masked_pips = [] | |
| # Get all IOPADS for MIO and DDR signals | |
| iopad_sites = [s for s in sites if s.type == "IOPAD"] | |
| for site in iopad_sites: | |
| # Get pins/wires | |
| site_pins = [p for p in site.site_pins if p.name == "IO"] | |
| for site_pin in site_pins: | |
| # Mask the wire | |
| masked_wires.append(site_pin.wire) | |
| # Find a PIP(s) for this wire, mask them as well as wires on | |
| # their other sides. | |
| for p in tile_type.get_pips(): | |
| if p.net_from == site_pin.wire: | |
| masked_pips.append(p.name) | |
| masked_wires.append(p.net_to) | |
| if p.net_to == site_pin.wire: | |
| masked_pips.append(p.name) | |
| masked_wires.append(p.net_from) | |
| # Masked sites names | |
| masked_sites = [(s.prefix, s.name) for s in iopad_sites] | |
| return masked_sites, masked_wires, masked_pips | |
| def import_tile_type( | |
| db, write_cur, tile_types, site_types, tile_type_name, get_switch | |
| ): | |
| assert tile_type_name not in tile_types | |
| tile_type = db.get_tile_type(tile_type_name) | |
| # For Zynq7 PSS* tiles build a list of sites, wires and PIPs to ignore | |
| if tile_type_name.startswith("PSS"): | |
| masked_sites, masked_wires, masked_pips = build_pss_object_mask( | |
| db, tile_type_name | |
| ) | |
| else: | |
| masked_sites = [] | |
| masked_wires = [] | |
| masked_pips = [] | |
| write_cur.execute( | |
| "INSERT INTO tile_type(name) VALUES (?)", (tile_type_name, ) | |
| ) | |
| tile_types[tile_type_name] = write_cur.lastrowid | |
| wires = {} | |
| for wire, wire_rc_element in tile_type.get_wires().items(): | |
| if wire in masked_wires: | |
| continue | |
| capacitance = 0.0 | |
| resistance = 0.0 | |
| if wire_rc_element is not None: | |
| # microFarads -> Farads | |
| capacitance = wire_rc_element.capacitance / 1e6 | |
| # milliOhms -> Ohms | |
| resistance = wire_rc_element.resistance / 1e3 | |
| write_cur.execute( | |
| """ | |
| INSERT INTO wire_in_tile(name, phy_tile_type_pkey, tile_type_pkey, capacitance, resistance) | |
| VALUES | |
| (?, ?, ?, ?, ?)""", ( | |
| wire, tile_types[tile_type_name], tile_types[tile_type_name], | |
| capacitance, resistance | |
| ) | |
| ) | |
| wires[wire] = write_cur.lastrowid | |
| for pip in tile_type.get_pips(): | |
| if pip.name in masked_pips: | |
| continue | |
| switch_pkey = get_switch(pip, pip.timing) | |
| backward_switch_pkey = get_switch(pip, pip.backward_timing) | |
| write_cur.execute( | |
| """ | |
| INSERT INTO pip_in_tile( | |
| name, tile_type_pkey, src_wire_in_tile_pkey, | |
| dest_wire_in_tile_pkey, can_invert, is_directional, is_pseudo, | |
| is_pass_transistor, switch_pkey, backward_switch_pkey | |
| ) | |
| VALUES | |
| (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", ( | |
| pip.name, tile_types[tile_type_name], wires[pip.net_from], | |
| wires[pip.net_to | |
| ], pip.can_invert, pip.is_directional, pip.is_pseudo, | |
| pip.is_pass_transistor, switch_pkey, backward_switch_pkey | |
| ) | |
| ) | |
| pip_pkey = write_cur.lastrowid | |
| write_cur.execute( | |
| """ | |
| INSERT INTO undirected_pips( | |
| wire_in_tile_pkey, pip_in_tile_pkey, other_wire_in_tile_pkey) | |
| VALUES | |
| (?, ?, ?), (?, ?, ?);""", ( | |
| wires[pip.net_from], | |
| pip_pkey, | |
| wires[pip.net_to], | |
| wires[pip.net_to], | |
| pip_pkey, | |
| wires[pip.net_from], | |
| ) | |
| ) | |
| for site in tile_type.get_sites(): | |
| if (site.prefix, site.name) in masked_sites: | |
| continue | |
| if site.type not in site_types: | |
| import_site_type(db, write_cur, site_types, site.type) | |
| def add_wire_to_site_relation( | |
| db, write_cur, tile_types, site_types, tile_type_name, | |
| get_switch_timing | |
| ): | |
| tile_type = db.get_tile_type(tile_type_name) | |
| for site in tile_type.get_sites(): | |
| if site.type not in site_types: | |
| continue | |
| write_cur.execute( | |
| """ | |
| INSERT INTO site(name, x_coord, y_coord, site_type_pkey, tile_type_pkey) | |
| VALUES | |
| (?, ?, ?, ?, ?)""", ( | |
| site.name, site.x, site.y, site_types[site.type], | |
| tile_types[tile_type_name] | |
| ) | |
| ) | |
| site_pkey = write_cur.lastrowid | |
| for site_pin in site.site_pins: | |
| write_cur.execute( | |
| """ | |
| SELECT | |
| pkey | |
| FROM | |
| site_pin | |
| WHERE | |
| name = ? | |
| AND site_type_pkey = ?""", (site_pin.name, site_types[site.type]) | |
| ) | |
| result = write_cur.fetchone() | |
| site_pin_pkey = result[0] | |
| intrinsic_delay = 0 | |
| drive_resistance = 0 | |
| capacitance = 0 | |
| if site_pin.timing is not None: | |
| # Use the largest intristic delay for now. | |
| # This conservative on slack timing, but not on hold timing. | |
| # | |
| # nanosecond -> seconds | |
| intrinsic_delay = site_pin.timing.delays[PvtCorner.SLOW | |
| ].max / 1e9 | |
| if isinstance(site_pin.timing, prjxray.tile.OutPinTiming): | |
| # milliOhms -> Ohms | |
| drive_resistance = site_pin.timing.drive_resistance / 1e3 | |
| elif isinstance(site_pin.timing, prjxray.tile.InPinTiming): | |
| # microFarads -> Farads | |
| capacitance = site_pin.timing.capacitance / 1e6 | |
| else: | |
| assert False, site_pin | |
| else: | |
| # Use min value instead of 0 to prevent | |
| # VPR from freaking out over a zero net delay. | |
| # | |
| # Note this is the single precision float minimum, because VPR | |
| # uses single precision, not double precision. | |
| intrinsic_delay = SINGLE_PRECISION_FLOAT_MIN | |
| site_pin_switch_pkey = get_switch_timing( | |
| is_pass_transistor=False, | |
| delay=intrinsic_delay, | |
| internal_capacitance=capacitance, | |
| drive_resistance=drive_resistance, | |
| ) | |
| write_cur.execute( | |
| """ | |
| UPDATE | |
| wire_in_tile | |
| SET | |
| site_pkey = ?, | |
| site_pin_pkey = ?, | |
| site_pin_switch_pkey = ? | |
| WHERE | |
| name = ? | |
| and tile_type_pkey = ?;""", ( | |
| site_pkey, site_pin_pkey, site_pin_switch_pkey, | |
| site_pin.wire, tile_types[tile_type_name] | |
| ) | |
| ) | |
| def build_tile_type_indicies(write_cur): | |
| write_cur.execute( | |
| "CREATE INDEX site_pin_index ON site_pin(name, site_type_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX wire_name_index ON wire_in_tile(name, tile_type_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX wire_tile_site_index ON wire_in_tile(tile_type_pkey, site_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX wire_site_pin_index ON wire_in_tile(site_pin_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX tile_type_index ON phy_tile(tile_type_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX pip_tile_type_index ON pip_in_tile(tile_type_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX src_pip_index ON pip_in_tile(src_wire_in_tile_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX dest_pip_index ON pip_in_tile(dest_wire_in_tile_pkey);" | |
| ) | |
| write_cur.execute( | |
| """CREATE INDEX undirected_pip_index ON | |
| undirected_pips(wire_in_tile_pkey, other_wire_in_tile_pkey);""" | |
| ) | |
| def build_other_indicies(write_cur): | |
| write_cur.execute("CREATE INDEX phy_tile_name_index ON phy_tile(name);") | |
| write_cur.execute( | |
| "CREATE INDEX phy_tile_location_index ON phy_tile(grid_x, grid_y);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX site_instance_index on site_instance(name);" | |
| ) | |
| def import_phy_grid(db, grid, conn, get_switch, get_switch_timing): | |
| write_cur = conn.cursor() | |
| tile_types = {} | |
| site_types = {} | |
| for tile in grid.tiles(): | |
| gridinfo = grid.gridinfo_at_tilename(tile) | |
| if gridinfo.tile_type not in tile_types: | |
| if gridinfo.tile_type in tile_types: | |
| continue | |
| import_tile_type( | |
| db, write_cur, tile_types, site_types, gridinfo.tile_type, | |
| get_switch | |
| ) | |
| write_cur.connection.commit() | |
| build_tile_type_indicies(write_cur) | |
| write_cur.connection.commit() | |
| for tile_type in tile_types: | |
| add_wire_to_site_relation( | |
| db, write_cur, tile_types, site_types, tile_type, get_switch_timing | |
| ) | |
| clock_regions = {} | |
| for tile in grid.tiles(): | |
| gridinfo = grid.gridinfo_at_tilename(tile) | |
| clock_region_pkey = None | |
| if gridinfo.clock_region is not None: | |
| if gridinfo.clock_region.name not in clock_regions: | |
| write_cur.execute( | |
| """ | |
| INSERT INTO clock_region(name, x_coord, y_coord) | |
| VALUES (?, ?, ?)""", ( | |
| gridinfo.clock_region.name, | |
| gridinfo.clock_region.x, | |
| gridinfo.clock_region.y, | |
| ) | |
| ) | |
| clock_regions[gridinfo.clock_region.name] = write_cur.lastrowid | |
| clock_region_pkey = clock_regions[gridinfo.clock_region.name] | |
| loc = grid.loc_of_tilename(tile) | |
| # tile: pkey name tile_type_pkey grid_x grid_y | |
| write_cur.execute( | |
| """ | |
| INSERT INTO phy_tile(name, tile_type_pkey, grid_x, grid_y, clock_region_pkey) | |
| VALUES | |
| (?, ?, ?, ?, ?)""", ( | |
| tile, | |
| tile_types[gridinfo.tile_type], | |
| loc.grid_x, | |
| loc.grid_y, | |
| clock_region_pkey, | |
| ) | |
| ) | |
| phy_tile_pkey = write_cur.lastrowid | |
| tile_type = db.get_tile_type(gridinfo.tile_type) | |
| for site, instance_site in zip(tile_type.sites, | |
| tile_type.get_instance_sites(gridinfo)): | |
| write_cur.execute( | |
| """ | |
| INSERT INTO site_instance(name, x_coord, y_coord, site_pkey, phy_tile_pkey) | |
| SELECT ?, ?, ?, site.pkey, ? | |
| FROM site | |
| WHERE | |
| site.name = ? | |
| AND | |
| site.x_coord = ? | |
| AND | |
| site.y_coord = ? | |
| AND | |
| site.site_type_pkey = (SELECT pkey FROM site_type WHERE name = ?) | |
| AND | |
| tile_type_pkey = ?; | |
| """, ( | |
| instance_site.name, | |
| instance_site.x, | |
| instance_site.y, | |
| phy_tile_pkey, | |
| site.name, | |
| site.x, | |
| site.y, | |
| site.type, | |
| tile_types[gridinfo.tile_type], | |
| ) | |
| ) | |
| site_instance_pkey = write_cur.lastrowid | |
| write_cur.execute( | |
| "UPDATE site_instance SET prohibited = ? WHERE pkey = ?", ( | |
| instance_site.name in gridinfo.prohibited_sites, | |
| site_instance_pkey, | |
| ) | |
| ) | |
| build_other_indicies(write_cur) | |
| write_cur.connection.commit() | |
| def import_nodes(db, grid, conn): | |
| # Some nodes are just 1 wire, so start by enumerating all wires. | |
| cur = conn.cursor() | |
| write_cur = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| tile_wire_map = {} | |
| wires = {} | |
| for tile in progressbar_utils.progressbar(grid.tiles()): | |
| gridinfo = grid.gridinfo_at_tilename(tile) | |
| tile_type = db.get_tile_type(gridinfo.tile_type) | |
| cur.execute( | |
| """SELECT pkey, tile_type_pkey FROM phy_tile WHERE name = ?;""", | |
| (tile, ) | |
| ) | |
| phy_tile_pkey, tile_type_pkey = cur.fetchone() | |
| for wire in tile_type.get_wires(): | |
| # pkey node_pkey tile_pkey wire_in_tile_pkey | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM wire_in_tile WHERE name = ? and tile_type_pkey = ?;""", | |
| (wire, tile_type_pkey) | |
| ) | |
| wire_in_tile_pkey = cur.fetchone() | |
| if wire_in_tile_pkey is None: | |
| continue | |
| wire_in_tile_pkey = wire_in_tile_pkey[0] | |
| write_cur.execute( | |
| """ | |
| INSERT INTO wire(phy_tile_pkey, wire_in_tile_pkey) | |
| VALUES | |
| (?, ?);""", (phy_tile_pkey, wire_in_tile_pkey) | |
| ) | |
| assert (tile, wire) not in tile_wire_map | |
| wire_pkey = write_cur.lastrowid | |
| tile_wire_map[(tile, wire)] = wire_pkey | |
| wires[wire_pkey] = None | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| connections = db.connections() | |
| for connection in progressbar_utils.progressbar( | |
| connections.get_connections()): | |
| a_pkey = tile_wire_map[ | |
| (connection.wire_a.tile, connection.wire_a.wire)] | |
| b_pkey = tile_wire_map[ | |
| (connection.wire_b.tile, connection.wire_b.wire)] | |
| a_node = wires[a_pkey] | |
| b_node = wires[b_pkey] | |
| if a_node is None: | |
| a_node = set((a_pkey, )) | |
| if b_node is None: | |
| b_node = set((b_pkey, )) | |
| if a_node is not b_node: | |
| a_node |= b_node | |
| for wire in a_node: | |
| wires[wire] = a_node | |
| nodes = {} | |
| for wire_pkey, node in wires.items(): | |
| if node is None: | |
| node = set((wire_pkey, )) | |
| assert wire_pkey in node | |
| nodes[id(node)] = node | |
| wires_assigned = set() | |
| for node in progressbar_utils.progressbar(nodes.values()): | |
| write_cur.execute("""INSERT INTO node(number_pips) VALUES (0);""") | |
| node_pkey = write_cur.lastrowid | |
| for wire_pkey in node: | |
| wires_assigned.add(wire_pkey) | |
| write_cur.execute( | |
| """ | |
| UPDATE wire | |
| SET node_pkey = ? | |
| WHERE pkey = ? | |
| ;""", (node_pkey, wire_pkey) | |
| ) | |
| assert len(set(wires.keys()) ^ wires_assigned) == 0 | |
| del tile_wire_map | |
| del nodes | |
| del wires | |
| write_cur.execute( | |
| "CREATE INDEX wire_in_tile_index ON wire(wire_in_tile_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX wire_index ON wire(phy_tile_pkey, wire_in_tile_pkey);" | |
| ) | |
| write_cur.execute("CREATE INDEX wire_node_index ON wire(node_pkey);") | |
| write_cur.connection.commit() | |
| def count_sites_and_pips_on_nodes(conn): | |
| cur = conn.cursor() | |
| print("{}: Counting sites on nodes".format(datetime.datetime.now())) | |
| cur.execute( | |
| """ | |
| WITH node_sites(node_pkey, number_site_pins) AS ( | |
| SELECT | |
| wire.node_pkey, | |
| count(wire_in_tile.site_pin_pkey) | |
| FROM | |
| wire_in_tile | |
| INNER JOIN wire ON wire.wire_in_tile_pkey = wire_in_tile.pkey | |
| WHERE | |
| wire_in_tile.site_pin_pkey IS NOT NULL | |
| GROUP BY | |
| wire.node_pkey | |
| ) | |
| SELECT | |
| max(node_sites.number_site_pins) | |
| FROM | |
| node_sites; | |
| """ | |
| ) | |
| # Nodes are only expected to have 1 site | |
| assert cur.fetchone()[0] == 1 | |
| print("{}: Assigning site wires for nodes".format(datetime.datetime.now())) | |
| cur.execute( | |
| """ | |
| WITH site_wires(wire_pkey, node_pkey) AS ( | |
| SELECT | |
| wire.pkey, | |
| wire.node_pkey | |
| FROM | |
| wire_in_tile | |
| INNER JOIN wire ON wire.wire_in_tile_pkey = wire_in_tile.pkey | |
| WHERE | |
| wire_in_tile.site_pin_pkey IS NOT NULL | |
| ) | |
| UPDATE | |
| node | |
| SET | |
| site_wire_pkey = ( | |
| SELECT | |
| site_wires.wire_pkey | |
| FROM | |
| site_wires | |
| WHERE | |
| site_wires.node_pkey = node.pkey | |
| ); | |
| """ | |
| ) | |
| print("{}: Counting pips on nodes".format(datetime.datetime.now())) | |
| cur.execute( | |
| """ | |
| CREATE TABLE node_pip_count( | |
| node_pkey INT, | |
| number_pips INT, | |
| FOREIGN KEY(node_pkey) REFERENCES node(pkey) | |
| );""" | |
| ) | |
| cur.execute( | |
| """ | |
| INSERT INTO node_pip_count(node_pkey, number_pips) | |
| SELECT | |
| wire.node_pkey, | |
| count(pip_in_tile.pkey) | |
| FROM | |
| pip_in_tile | |
| INNER JOIN wire | |
| WHERE | |
| pip_in_tile.is_pseudo = 0 AND ( | |
| pip_in_tile.src_wire_in_tile_pkey = wire.wire_in_tile_pkey | |
| OR pip_in_tile.dest_wire_in_tile_pkey = wire.wire_in_tile_pkey) | |
| GROUP BY | |
| wire.node_pkey;""" | |
| ) | |
| cur.execute("CREATE INDEX pip_count_index ON node_pip_count(node_pkey);") | |
| print("{}: Inserting pip counts".format(datetime.datetime.now())) | |
| cur.execute( | |
| """ | |
| UPDATE | |
| node | |
| SET | |
| number_pips = ( | |
| SELECT | |
| node_pip_count.number_pips | |
| FROM | |
| node_pip_count | |
| WHERE | |
| node_pip_count.node_pkey = node.pkey | |
| ) | |
| WHERE | |
| pkey IN ( | |
| SELECT | |
| node_pkey | |
| FROM | |
| node_pip_count | |
| );""" | |
| ) | |
| cur.execute("""DROP TABLE node_pip_count;""") | |
| cur.connection.commit() | |
| def check_edge_with_mux_timing( | |
| conn, get_switch_timing, src_wire_pkey, dest_wire_pkey, pip_pkey | |
| ): | |
| """ Check if edge with mux timing can be "lumped" into the switch. | |
| Returns | |
| ------- | |
| switch_pkey : int | |
| Switch primary key to model EDGE_WITH_MUX connection. | |
| """ | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT site_pin_switch_pkey, resistance, capacitance FROM wire_in_tile WHERE pkey = ( | |
| SELECT wire_in_tile_pkey FROM wire WHERE pkey = ? | |
| )""", (src_wire_pkey, ) | |
| ) | |
| src_site_pin_switch_pkey, src_wire_resistance, src_wire_capacitance = cur.fetchone( | |
| ) | |
| assert src_wire_resistance == 0, src_wire_pkey | |
| cur.execute( | |
| """ | |
| SELECT intrinsic_delay, drive_resistance FROM switch WHERE pkey = ? | |
| """, (src_site_pin_switch_pkey, ) | |
| ) | |
| src_site_pin_intrinsic_delay, src_site_pin_drive_resistance = cur.fetchone( | |
| ) | |
| cur.execute( | |
| """ | |
| SELECT site_pin_switch_pkey, resistance, capacitance FROM wire_in_tile WHERE pkey = ( | |
| SELECT wire_in_tile_pkey FROM wire WHERE pkey = ? | |
| )""", (dest_wire_pkey, ) | |
| ) | |
| dest_site_pin_switch_pkey, dest_wire_resistance, dest_wire_capacitance = cur.fetchone( | |
| ) | |
| assert dest_wire_resistance == 0, dest_wire_pkey | |
| cur.execute( | |
| """ | |
| SELECT intrinsic_delay, internal_capacitance FROM switch WHERE pkey = ? | |
| """, (dest_site_pin_switch_pkey, ) | |
| ) | |
| dest_site_pin_intrinsic_delay, dest_site_pin_capacitance = cur.fetchone() | |
| cur.execute( | |
| """ | |
| SELECT name, switch_pkey FROM pip_in_tile WHERE pkey = ?""", (pip_pkey, ) | |
| ) | |
| pip_name, switch_pkey = cur.fetchone() | |
| cur.execute( | |
| """ | |
| SELECT | |
| name, intrinsic_delay, internal_capacitance, drive_resistance, switch_type | |
| FROM switch WHERE pkey = ? | |
| """, (switch_pkey, ) | |
| ) | |
| ( | |
| switch_name, switch_intrinsic_delay, switch_internal_capacitance, | |
| switch_drive_resistance, switch_type | |
| ) = cur.fetchone() | |
| assert switch_type in ["mux", "pass_gate"], (switch_pkey, switch_type) | |
| zero_delay_to_switch = src_site_pin_drive_resistance == 0 or ( | |
| switch_internal_capacitance == 0 and src_wire_capacitance == 0 | |
| ) | |
| zero_delay_from_switch = switch_drive_resistance == 0 or ( | |
| dest_wire_capacitance == 0 and dest_site_pin_capacitance == 0 | |
| ) | |
| if zero_delay_to_switch and zero_delay_from_switch and \ | |
| src_site_pin_intrinsic_delay == 0 and \ | |
| dest_site_pin_intrinsic_delay == 0: | |
| return get_switch_timing( | |
| is_pass_transistor=False, | |
| delay=switch_intrinsic_delay, | |
| internal_capacitance=0, | |
| drive_resistance=0, | |
| ) | |
| if switch_type == "mux": | |
| switch_delay = src_site_pin_intrinsic_delay | |
| switch_delay += src_site_pin_drive_resistance * ( | |
| switch_internal_capacitance + src_wire_capacitance | |
| ) | |
| switch_delay += switch_intrinsic_delay | |
| switch_delay += switch_drive_resistance * ( | |
| dest_wire_capacitance + dest_site_pin_capacitance | |
| ) | |
| switch_delay += dest_site_pin_intrinsic_delay | |
| else: | |
| assert switch_type == "pass_gate" | |
| assert switch_intrinsic_delay == 0 | |
| assert switch_drive_resistance == 0 | |
| switch_delay = src_site_pin_intrinsic_delay | |
| switch_delay += src_site_pin_drive_resistance * ( | |
| src_wire_capacitance + switch_internal_capacitance + | |
| dest_wire_capacitance + dest_site_pin_capacitance | |
| ) | |
| switch_delay += dest_site_pin_intrinsic_delay | |
| return get_switch_timing( | |
| is_pass_transistor=False, | |
| delay=switch_delay, | |
| internal_capacitance=0, | |
| drive_resistance=0 | |
| ) | |
| def classify_nodes(conn, get_switch_timing): | |
| write_cur = conn.cursor() | |
| # Nodes are NULL if they they only have either a site pin or 1 pip, but | |
| # nothing else. | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET classification = ? | |
| WHERE (node.site_wire_pkey IS NULL AND node.number_pips <= 1) OR | |
| (node.site_wire_pkey IS NOT NULL AND node.number_pips == 0) | |
| ;""", (NodeClassification.NULL.value, ) | |
| ) | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET classification = ? | |
| WHERE node.number_pips > 1 and node.site_wire_pkey IS NULL;""", | |
| (NodeClassification.CHANNEL.value, ) | |
| ) | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET classification = ? | |
| WHERE node.number_pips > 1 and node.site_wire_pkey IS NOT NULL;""", | |
| (NodeClassification.EDGES_TO_CHANNEL.value, ) | |
| ) | |
| null_nodes = [] | |
| edges_to_channel = [] | |
| edge_with_mux = [] | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT | |
| count(pkey) | |
| FROM | |
| node | |
| WHERE | |
| number_pips == 1 | |
| AND site_wire_pkey IS NOT NULL;""" | |
| ) | |
| num_nodes = cur.fetchone()[0] | |
| with progressbar_utils.ProgressBar(max_value=num_nodes) as bar: | |
| bar.update(0) | |
| for idx, (node, site_wire_pkey) in enumerate(cur.execute(""" | |
| SELECT | |
| pkey, | |
| site_wire_pkey | |
| FROM | |
| node | |
| WHERE | |
| number_pips == 1 | |
| AND site_wire_pkey IS NOT NULL;""")): | |
| bar.update(idx) | |
| write_cur.execute( | |
| """ | |
| WITH wire_in_node( | |
| wire_pkey, phy_tile_pkey, wire_in_tile_pkey | |
| ) AS ( | |
| SELECT | |
| wire.pkey, | |
| wire.phy_tile_pkey, | |
| wire.wire_in_tile_pkey | |
| FROM | |
| wire | |
| WHERE | |
| wire.node_pkey = ? | |
| ) | |
| SELECT | |
| pip_in_tile.pkey, | |
| pip_in_tile.src_wire_in_tile_pkey, | |
| pip_in_tile.dest_wire_in_tile_pkey, | |
| wire_in_node.wire_pkey, | |
| wire_in_node.wire_in_tile_pkey, | |
| wire_in_node.phy_tile_pkey | |
| FROM | |
| wire_in_node | |
| INNER JOIN pip_in_tile | |
| WHERE | |
| pip_in_tile.is_pseudo = 0 AND ( | |
| pip_in_tile.src_wire_in_tile_pkey = wire_in_node.wire_in_tile_pkey | |
| OR pip_in_tile.dest_wire_in_tile_pkey = wire_in_node.wire_in_tile_pkey) | |
| LIMIT | |
| 1; | |
| """, (node, ) | |
| ) | |
| ( | |
| pip_pkey, src_wire_in_tile_pkey, dest_wire_in_tile_pkey, | |
| wire_in_node_pkey, wire_in_tile_pkey, phy_tile_pkey | |
| ) = write_cur.fetchone() | |
| assert write_cur.fetchone() is None, node | |
| assert ( | |
| wire_in_tile_pkey == src_wire_in_tile_pkey | |
| or wire_in_tile_pkey == dest_wire_in_tile_pkey | |
| ), (wire_in_tile_pkey, pip_pkey) | |
| if src_wire_in_tile_pkey == wire_in_tile_pkey: | |
| other_wire = dest_wire_in_tile_pkey | |
| else: | |
| other_wire = src_wire_in_tile_pkey | |
| write_cur.execute( | |
| """ | |
| SELECT node_pkey FROM wire WHERE | |
| wire_in_tile_pkey = ? AND | |
| phy_tile_pkey = ?; | |
| """, (other_wire, phy_tile_pkey) | |
| ) | |
| (other_node_pkey, ) = write_cur.fetchone() | |
| assert write_cur.fetchone() is None | |
| assert other_node_pkey is not None, (other_wire, phy_tile_pkey) | |
| write_cur.execute( | |
| """ | |
| SELECT site_wire_pkey, number_pips | |
| FROM node WHERE pkey = ?; | |
| """, (other_node_pkey, ) | |
| ) | |
| result = write_cur.fetchone() | |
| assert result is not None, other_node_pkey | |
| other_site_wire_pkey, other_number_pips = result | |
| assert write_cur.fetchone() is None | |
| force_direct = False | |
| write_cur.execute( | |
| """ | |
| SELECT name | |
| FROM pip_in_tile WHERE pkey = ?; | |
| """, (pip_pkey, ) | |
| ) | |
| pip_name = write_cur.fetchone()[0] | |
| # A solution for: | |
| # https://github.com/SymbiFlow/symbiflow-arch-defs/issues/1033 | |
| if "PADOUT0" in pip_name and "DIFFI_IN1" in pip_name: | |
| force_direct = True | |
| if "PADOUT1" in pip_name and "DIFFI_IN0" in pip_name: | |
| force_direct = True | |
| if other_site_wire_pkey is not None and (other_number_pips == 1 | |
| or force_direct): | |
| if src_wire_in_tile_pkey == wire_in_tile_pkey: | |
| src_wire_pkey = site_wire_pkey | |
| dest_wire_pkey = other_site_wire_pkey | |
| else: | |
| src_wire_pkey = other_site_wire_pkey | |
| dest_wire_pkey = site_wire_pkey | |
| edge_with_mux.append( | |
| ( | |
| (node, other_node_pkey), src_wire_pkey, dest_wire_pkey, | |
| pip_pkey | |
| ) | |
| ) | |
| elif other_site_wire_pkey is None and other_number_pips == 1: | |
| null_nodes.append(node) | |
| null_nodes.append(other_node_pkey) | |
| pass | |
| else: | |
| edges_to_channel.append(node) | |
| for nodes, src_wire_pkey, dest_wire_pkey, pip_pkey in progressbar_utils.progressbar( | |
| edge_with_mux): | |
| assert len(nodes) == 2 | |
| switch_pkey = check_edge_with_mux_timing( | |
| conn, get_switch_timing, src_wire_pkey, dest_wire_pkey, pip_pkey | |
| ) | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET classification = ? | |
| WHERE pkey IN (?, ?);""", | |
| (NodeClassification.EDGE_WITH_MUX.value, nodes[0], nodes[1]) | |
| ) | |
| write_cur.execute( | |
| """ | |
| INSERT INTO edge_with_mux(src_wire_pkey, dest_wire_pkey, pip_in_tile_pkey, switch_pkey) | |
| VALUES | |
| (?, ?, ?, ?);""", (src_wire_pkey, dest_wire_pkey, pip_pkey, switch_pkey) | |
| ) | |
| for node in progressbar_utils.progressbar(edges_to_channel): | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET classification = ? | |
| WHERE pkey = ?;""", ( | |
| NodeClassification.EDGES_TO_CHANNEL.value, | |
| node, | |
| ) | |
| ) | |
| for null_node in progressbar_utils.progressbar(null_nodes): | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET classification = ? | |
| WHERE pkey = ?;""", ( | |
| NodeClassification.NULL.value, | |
| null_node, | |
| ) | |
| ) | |
| write_cur.execute("CREATE INDEX node_type_index ON node(classification);") | |
| write_cur.execute( | |
| """ | |
| CREATE INDEX edge_with_mux_index ON edge_with_mux( | |
| src_wire_pkey, | |
| dest_wire_pkey, | |
| pip_in_tile_pkey, | |
| switch_pkey);""" | |
| ) | |
| write_cur.connection.commit() | |
| def get_node_rc(conn, node_pkey): | |
| """ Returns capacitance and resistance for given node. | |
| Returns | |
| ------- | |
| capacitance : float | |
| Node capacitance (Farads) | |
| resistance : float | |
| Node resistance (Ohms) | |
| """ | |
| capacitance = 0 | |
| resistance = 0 | |
| cur = conn.cursor() | |
| for wire_cap, wire_res in cur.execute(""" | |
| SELECT capacitance, 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 | |
| return capacitance, resistance | |
| def get_segment_for_node(cur, segments, node_pkey): | |
| """ Attempt to determine the segment for the given node. | |
| Parameters | |
| ---------- | |
| conn : sqlite3.Connection | |
| Connection to channels.db | |
| segments : SegmentWireMap | |
| Object for determined segment based on wire names. | |
| node_pkey : int | |
| Node to determine segment of. Is primary key into node table. | |
| Returns | |
| ------- | |
| segment_pkey : int | |
| Primary key to segment table of segment for this node. | |
| """ | |
| cur.execute( | |
| """ | |
| SELECT name FROM wire_in_tile WHERE pkey IN ( | |
| SELECT wire_in_tile_pkey FROM wire WHERE node_pkey = ? | |
| );""", (node_pkey, ) | |
| ) | |
| segment_name = segments.get_segment_for_wires( | |
| wire for (wire, ) in cur.fetchall() | |
| ) | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM segment WHERE name = ? | |
| """, (segment_name, ) | |
| ) | |
| return cur.fetchone()[0] | |
| def insert_tracks(conn, segments, tracks_to_insert): | |
| write_cur = conn.cursor() | |
| write_cur.execute('SELECT pkey FROM switch WHERE name = "short";') | |
| short_pkey = write_cur.fetchone()[0] | |
| track_graph_nodes = {} | |
| track_pkeys = [] | |
| for node, tracks_list, track_conn, tracks_model, seg_pkey in progressbar_utils.progressbar( | |
| tracks_to_insert): | |
| write_cur.execute( | |
| """INSERT INTO track(segment_pkey) VALUES (?)""", (seg_pkey, ) | |
| ) | |
| track_pkey = write_cur.lastrowid | |
| track_pkeys.append(track_pkey) | |
| write_cur.execute( | |
| """UPDATE node SET track_pkey = ? WHERE pkey = ?""", | |
| (track_pkey, node) | |
| ) | |
| track_graph_node_pkey = [] | |
| for idx, track in enumerate(tracks_list): | |
| if track.direction == 'X': | |
| node_type = graph2.NodeType.CHANX | |
| elif track.direction == 'Y': | |
| node_type = graph2.NodeType.CHANY | |
| else: | |
| assert False, track.direction | |
| if idx == 0: | |
| capacitance, resistance = get_node_rc(conn, node) | |
| else: | |
| capacitance = 0 | |
| resistance = 0 | |
| write_cur.execute( | |
| """ | |
| INSERT INTO graph_node( | |
| graph_node_type, track_pkey, node_pkey, | |
| x_low, x_high, y_low, y_high, capacity, capacitance, resistance | |
| ) | |
| VALUES | |
| (?, ?, ?, ?, ?, ?, ?, 1, ?, ?)""", ( | |
| node_type.value, track_pkey, node, track.x_low, | |
| track.x_high, track.y_low, track.y_high, capacitance, | |
| resistance | |
| ) | |
| ) | |
| track_graph_node_pkey.append(write_cur.lastrowid) | |
| track_graph_nodes[node] = track_graph_node_pkey | |
| for connection in track_conn: | |
| write_cur.execute( | |
| """ | |
| INSERT INTO graph_edge( | |
| src_graph_node_pkey, dest_graph_node_pkey, | |
| switch_pkey, track_pkey | |
| ) | |
| VALUES | |
| (?, ?, ?, ?), | |
| (?, ?, ?, ?)""", ( | |
| track_graph_node_pkey[connection[0]], | |
| track_graph_node_pkey[connection[1]], | |
| short_pkey, | |
| track_pkey, | |
| track_graph_node_pkey[connection[1]], | |
| track_graph_node_pkey[connection[0]], | |
| short_pkey, | |
| track_pkey, | |
| ) | |
| ) | |
| conn.commit() | |
| wire_to_graph = {} | |
| for node, tracks_list, track_conn, tracks_model, _ in progressbar_utils.progressbar( | |
| tracks_to_insert): | |
| track_graph_node_pkey = track_graph_nodes[node] | |
| write_cur.execute( | |
| """ | |
| WITH wires_from_node(wire_pkey, tile_pkey) AS ( | |
| SELECT | |
| pkey, | |
| tile_pkey | |
| FROM | |
| wire | |
| WHERE | |
| node_pkey = ? | |
| ) | |
| SELECT | |
| wires_from_node.wire_pkey, | |
| tile.grid_x, | |
| tile.grid_y | |
| FROM | |
| tile | |
| INNER JOIN wires_from_node ON tile.pkey = wires_from_node.tile_pkey; | |
| """, (node, ) | |
| ) | |
| wires = write_cur.fetchall() | |
| for wire_pkey, grid_x, grid_y in wires: | |
| connections = list( | |
| tracks_model.get_tracks_for_wire_at_coord((grid_x, | |
| grid_y)).values() | |
| ) | |
| assert len(connections) > 0, ( | |
| connections, wire_pkey, track_pkey, grid_x, grid_y, node | |
| ) | |
| graph_node_pkey = track_graph_node_pkey[connections[0]] | |
| wire_to_graph[wire_pkey] = graph_node_pkey | |
| for wire_pkey, graph_node_pkey in progressbar_utils.progressbar( | |
| wire_to_graph.items()): | |
| write_cur.execute( | |
| """ | |
| UPDATE wire SET graph_node_pkey = ? | |
| WHERE pkey = ?""", (graph_node_pkey, wire_pkey) | |
| ) | |
| conn.commit() | |
| write_cur.execute( | |
| """CREATE INDEX graph_node_nodes ON graph_node(node_pkey);""" | |
| ) | |
| write_cur.execute( | |
| """CREATE INDEX graph_node_tracks ON graph_node(track_pkey);""" | |
| ) | |
| write_cur.execute( | |
| """CREATE INDEX graph_edge_tracks ON graph_edge(track_pkey);""" | |
| ) | |
| conn.commit() | |
| return track_pkeys | |
| def create_track(node, unique_pos): | |
| xs, ys = points.decompose_points_into_tracks(unique_pos) | |
| tracks_list, track_connections = tracks.make_tracks(xs, ys, unique_pos) | |
| tracks_model = tracks.Tracks(tracks_list, track_connections) | |
| return [node, tracks_list, track_connections, tracks_model] | |
| def form_tracks(conn, segments): | |
| cur = conn.cursor() | |
| cur2 = conn.cursor() | |
| cur.execute( | |
| 'SELECT count(pkey) FROM node WHERE classification == ?;', | |
| (NodeClassification.CHANNEL.value, ) | |
| ) | |
| num_nodes = cur.fetchone()[0] | |
| tracks_to_insert = [] | |
| with progressbar_utils.ProgressBar(max_value=num_nodes) as bar: | |
| bar.update(0) | |
| for idx, (node_pkey, ) in enumerate(cur.execute(""" | |
| SELECT pkey FROM node WHERE classification == ?; | |
| """, (NodeClassification.CHANNEL.value, ))): | |
| bar.update(idx) | |
| unique_pos = set() | |
| # Get coordinates for all wires in this node. | |
| for grid_x, grid_y in cur2.execute(""" | |
| SELECT DISTINCT grid_x, grid_y FROM tile WHERE pkey IN ( | |
| SELECT tile_pkey FROM wire WHERE node_pkey = ? AND tile_pkey IS NOT NULL | |
| )""", (node_pkey, )): | |
| unique_pos.add((grid_x, grid_y)) | |
| # Find the VPR grid locations that this channel connects to. | |
| # | |
| # Algorithm: | |
| # 1. Identify all pips that connect to or from this node | |
| # 2. Traverse each pip, and determine if the connected node is a | |
| # EDGES_TO_CHANNEL. NULL nodes are uninteresting, and CHANNEL | |
| # nodes are already covered in the earlier loop getting | |
| # locations of the | |
| # discarded. | |
| # 3a. For CHANNEL to CHANNEL connections, use the pip location. | |
| # 3b. For CHANNEL to EDGES_TO_CHANNEL (e.g. site pin connections) | |
| # use location of site in VPR grid. | |
| # 3a loop | |
| for grid_x, grid_y in cur2.execute(""" | |
| -- Get wires from this node | |
| WITH wires_from_node(wire_in_tile_pkey, phy_tile_pkey) AS ( | |
| SELECT | |
| wire_in_tile_pkey, | |
| phy_tile_pkey | |
| FROM | |
| wire | |
| WHERE | |
| node_pkey = ? AND tile_pkey IS NOT NULL | |
| ), | |
| other_wires(phy_tile_pkey, wire_in_tile_pkey) AS ( | |
| SELECT | |
| wires_from_node.phy_tile_pkey, | |
| undirected_pips.other_wire_in_tile_pkey | |
| FROM undirected_pips | |
| INNER JOIN wires_from_node ON | |
| undirected_pips.wire_in_tile_pkey = wires_from_node.wire_in_tile_pkey) | |
| SELECT tile.grid_x, tile.grid_y | |
| FROM tile | |
| WHERE pkey IN ( | |
| SELECT wire.tile_pkey FROM wire | |
| INNER JOIN other_wires ON | |
| wire.wire_in_tile_pkey = other_wires.wire_in_tile_pkey | |
| AND | |
| wire.phy_tile_pkey = other_wires.phy_tile_pkey | |
| );""", (node_pkey, )): | |
| unique_pos.add((grid_x, grid_y)) | |
| # 3b loop | |
| for grid_x, grid_y in cur2.execute(""" | |
| -- Get wires from this node | |
| WITH wires_from_node(wire_in_tile_pkey, phy_tile_pkey) AS ( | |
| SELECT | |
| wire_in_tile_pkey, | |
| phy_tile_pkey | |
| FROM | |
| wire | |
| WHERE | |
| node_pkey = ? AND tile_pkey IS NOT NULL | |
| ), | |
| other_wires(phy_tile_pkey, wire_in_tile_pkey) AS ( | |
| SELECT | |
| wires_from_node.phy_tile_pkey, | |
| undirected_pips.other_wire_in_tile_pkey | |
| FROM undirected_pips | |
| INNER JOIN wires_from_node ON | |
| undirected_pips.wire_in_tile_pkey = wires_from_node.wire_in_tile_pkey), | |
| other_nodes(other_node_pkey) AS ( | |
| SELECT wire.node_pkey FROM wire | |
| INNER JOIN other_wires ON | |
| wire.wire_in_tile_pkey = other_wires.wire_in_tile_pkey | |
| AND | |
| wire.phy_tile_pkey = other_wires.phy_tile_pkey), | |
| other_tiles(site_wire_pkey) AS ( | |
| SELECT node.site_wire_pkey FROM node | |
| WHERE | |
| pkey IN (SELECT other_node_pkey FROM other_nodes) | |
| AND | |
| classification = ? | |
| ) | |
| SELECT tile.grid_x, tile.grid_y | |
| FROM tile | |
| WHERE pkey IN ( | |
| SELECT DISTINCT tile_pkey | |
| FROM wire | |
| WHERE pkey IN ( | |
| SELECT other_tiles.site_wire_pkey FROM other_tiles | |
| ) | |
| ); | |
| """, (node_pkey, NodeClassification.EDGES_TO_CHANNEL.value)): | |
| unique_pos.add((grid_x, grid_y)) | |
| # Determine segment for each routing resource. | |
| segment_pkey = get_segment_for_node(cur2, segments, node_pkey) | |
| tracks_to_insert.append( | |
| create_track(node_pkey, unique_pos) + [segment_pkey] | |
| ) | |
| # Create constant tracks | |
| vcc_track_to_insert, gnd_track_to_insert = create_constant_tracks(conn) | |
| # Assign constant networks special segments to given them dedicated | |
| # router lookaheads. | |
| cur.execute("SELECT pkey FROM segment WHERE name = ?", (VCC_NET, )) | |
| vcc_segment_pkey = cur.fetchone()[0] | |
| cur.execute("SELECT pkey FROM segment WHERE name = ?", (GND_NET, )) | |
| gnd_segment_pkey = cur.fetchone()[0] | |
| vcc_idx = len(tracks_to_insert) | |
| tracks_to_insert.append(vcc_track_to_insert + [vcc_segment_pkey]) | |
| gnd_idx = len(tracks_to_insert) | |
| tracks_to_insert.append(gnd_track_to_insert + [gnd_segment_pkey]) | |
| track_pkeys = insert_tracks(conn, segments, tracks_to_insert) | |
| vcc_track_pkey = track_pkeys[vcc_idx] | |
| gnd_track_pkey = track_pkeys[gnd_idx] | |
| write_cur = conn.cursor() | |
| write_cur.execute( | |
| """ | |
| INSERT INTO constant_sources(vcc_track_pkey, gnd_track_pkey) VALUES (?, ?) | |
| """, ( | |
| vcc_track_pkey, | |
| gnd_track_pkey, | |
| ) | |
| ) | |
| # Create synthetic physical tile for constant network to assign a | |
| # canonical location too. | |
| # | |
| # This location is not important, but exists for uniformity with other | |
| # routing nodes. | |
| write_cur.execute( | |
| """ | |
| INSERT INTO phy_tile(name, grid_x, grid_y) VALUES (?, ?, ?) | |
| """, ("CONSTANT_SOURCE", 1, 1) | |
| ) | |
| zero_phy_tile_pkey = write_cur.lastrowid | |
| write_cur.execute( | |
| "UPDATE track SET canon_phy_tile_pkey = ? WHERE pkey = ?", ( | |
| zero_phy_tile_pkey, | |
| vcc_track_pkey, | |
| ) | |
| ) | |
| write_cur.execute( | |
| "UPDATE track SET canon_phy_tile_pkey = ? WHERE pkey = ?", ( | |
| zero_phy_tile_pkey, | |
| gnd_track_pkey, | |
| ) | |
| ) | |
| conn.commit() | |
| connect_hardpins_to_constant_network(conn, vcc_track_pkey, gnd_track_pkey) | |
| def connect_hardpins_to_constant_network(conn, vcc_track_pkey, gnd_track_pkey): | |
| """ Connect TIEOFF HARD1 and HARD0 pins. | |
| Update nodes connected to to HARD1 or HARD0 pins to point to the new | |
| VCC or GND track. This should connect the pips to the constant | |
| network instead of the TIEOFF site. | |
| """ | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM site_type WHERE name = ? | |
| """, ("TIEOFF", ) | |
| ) | |
| results = cur.fetchall() | |
| assert len(results) == 1, results | |
| tieoff_site_type_pkey = results[0][0] | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM site_pin WHERE site_type_pkey = ? and name = ? | |
| """, (tieoff_site_type_pkey, "HARD1") | |
| ) | |
| vcc_site_pin_pkey = cur.fetchone()[0] | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM wire_in_tile WHERE site_pin_pkey = ? | |
| """, (vcc_site_pin_pkey, ) | |
| ) | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM wire_in_tile WHERE site_pin_pkey = ? | |
| """, (vcc_site_pin_pkey, ) | |
| ) | |
| write_cur = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| for (wire_in_tile_pkey, ) in cur: | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET track_pkey = ? WHERE pkey IN ( | |
| SELECT node_pkey FROM wire WHERE wire_in_tile_pkey = ? | |
| ) | |
| """, ( | |
| vcc_track_pkey, | |
| wire_in_tile_pkey, | |
| ) | |
| ) | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM site_pin WHERE site_type_pkey = ? and name = ? | |
| """, (tieoff_site_type_pkey, "HARD0") | |
| ) | |
| gnd_site_pin_pkey = cur.fetchone()[0] | |
| cur.execute( | |
| """ | |
| SELECT pkey FROM wire_in_tile WHERE site_pin_pkey = ? | |
| """, (gnd_site_pin_pkey, ) | |
| ) | |
| for (wire_in_tile_pkey, ) in cur: | |
| write_cur.execute( | |
| """ | |
| UPDATE node SET track_pkey = ? WHERE pkey IN ( | |
| SELECT node_pkey FROM wire WHERE wire_in_tile_pkey = ? | |
| ) | |
| """, ( | |
| gnd_track_pkey, | |
| wire_in_tile_pkey, | |
| ) | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION""") | |
| def traverse_pip(conn, wire_in_tile_pkey): | |
| """ Given a generic wire, find (if any) the wire on the other side of a pip. | |
| Returns None if no wire or pip connects to this wire. | |
| """ | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| SELECT src_wire_in_tile_pkey FROM pip_in_tile WHERE | |
| is_pseudo = 0 AND | |
| dest_wire_in_tile_pkey = ? | |
| ;""", (wire_in_tile_pkey, ) | |
| ) | |
| result = cur.fetchone() | |
| if result is not None: | |
| return result[0] | |
| cur.execute( | |
| """ | |
| SELECT dest_wire_in_tile_pkey FROM pip_in_tile WHERE | |
| is_pseudo = 0 AND | |
| src_wire_in_tile_pkey = ? | |
| ;""", (wire_in_tile_pkey, ) | |
| ) | |
| result = cur.fetchone() | |
| if result is not None: | |
| return result[0] | |
| return None | |
| def update_wire_in_tile_types( | |
| cur, write_cur, empty_tile_type_pkey, wire_in_tile_pkey_to_update | |
| ): | |
| # Check if each wire in tile pkey can point to one logical tile type. | |
| wire_in_tile_pkey_to_tile_type_pkey = {} | |
| for tile_type_pkey, wire_in_tile_pkeys in wire_in_tile_pkey_to_update.items( | |
| ): | |
| for wire_in_tile_pkey in wire_in_tile_pkeys: | |
| if wire_in_tile_pkey not in wire_in_tile_pkey_to_tile_type_pkey: | |
| wire_in_tile_pkey_to_tile_type_pkey[wire_in_tile_pkey] = set() | |
| wire_in_tile_pkey_to_tile_type_pkey[wire_in_tile_pkey].add( | |
| tile_type_pkey | |
| ) | |
| wire_in_tile_pkey_to_updates = list( | |
| wire_in_tile_pkey_to_tile_type_pkey.keys() | |
| ) | |
| wire_in_tile_pkey_updates = [] | |
| for wire_in_tile_pkey in wire_in_tile_pkey_to_updates: | |
| cur.execute( | |
| "SELECT phy_tile_type_pkey FROM wire_in_tile WHERE pkey = ?", | |
| (wire_in_tile_pkey, ) | |
| ) | |
| phy_tile_type_pkey = cur.fetchone()[0] | |
| if phy_tile_type_pkey == empty_tile_type_pkey: | |
| del wire_in_tile_pkey_to_tile_type_pkey[wire_in_tile_pkey] | |
| continue | |
| tile_type_pkeys = wire_in_tile_pkey_to_tile_type_pkey[wire_in_tile_pkey | |
| ] | |
| assert len(tile_type_pkeys) == 1, (wire_in_tile_pkey, tile_type_pkeys) | |
| wire_in_tile_pkey_updates.append( | |
| (list(tile_type_pkeys)[0], wire_in_tile_pkey) | |
| ) | |
| write_cur.executemany( | |
| """ | |
| UPDATE wire_in_tile | |
| SET tile_type_pkey = ? | |
| WHERE pkey = ?;""", wire_in_tile_pkey_updates | |
| ) | |
| def create_vpr_grid(conn, grid_map_output): | |
| """ Create VPR grid from prjxray grid. """ | |
| cur = conn.cursor() | |
| cur2 = conn.cursor() | |
| slice_types = {} | |
| write_cur = conn.cursor() | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| # Insert synthetic tile types for CLB sites and iopad sites. | |
| for name in [ | |
| 'SLICEL', | |
| 'SLICEM', | |
| 'LIOPAD_M', | |
| 'LIOPAD_S', | |
| 'LIOPAD_SING', | |
| 'RIOPAD_M', | |
| 'RIOPAD_S', | |
| 'RIOPAD_SING', | |
| ]: | |
| write_cur.execute('INSERT INTO tile_type(name) VALUES (?);', (name, )) | |
| slice_types[name] = write_cur.lastrowid | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| tiles_to_merge = { | |
| 'LIOB33_SING': tile_splitter.grid.EAST, | |
| 'LIOB33': tile_splitter.grid.EAST, | |
| 'RIOB33_SING': tile_splitter.grid.WEST, | |
| 'RIOB33': tile_splitter.grid.WEST, | |
| } | |
| tiles_to_split = { | |
| 'LIOI3': tile_splitter.grid.NORTH, | |
| 'LIOI3_TBYTESRC': tile_splitter.grid.NORTH, | |
| 'LIOI3_TBYTETERM': tile_splitter.grid.NORTH, | |
| 'LIOI3_SING': tile_splitter.grid.NORTH, | |
| 'RIOI3': tile_splitter.grid.NORTH, | |
| 'RIOI3_TBYTESRC': tile_splitter.grid.NORTH, | |
| 'RIOI3_TBYTETERM': tile_splitter.grid.NORTH, | |
| 'RIOI3_SING': tile_splitter.grid.NORTH, | |
| } | |
| liopad_ms_split = { | |
| 1: slice_types['LIOPAD_M'], | |
| 0: slice_types['LIOPAD_S'], | |
| } | |
| riopad_ms_split = { | |
| 1: slice_types['RIOPAD_M'], | |
| 0: slice_types['RIOPAD_S'], | |
| } | |
| split_styles = { | |
| 'LIOI3': ('y_split', liopad_ms_split), | |
| 'LIOI3_TBYTESRC': ('y_split', liopad_ms_split), | |
| 'LIOI3_TBYTETERM': ('y_split', liopad_ms_split), | |
| 'RIOI3': ('y_split', riopad_ms_split), | |
| 'RIOI3_TBYTESRC': ('y_split', riopad_ms_split), | |
| 'RIOI3_TBYTETERM': ('y_split', riopad_ms_split), | |
| 'LIOI3_SING': ('y_split', { | |
| 0: slice_types['LIOPAD_SING'] | |
| }), | |
| 'RIOI3_SING': ('y_split', { | |
| 0: slice_types['RIOPAD_SING'] | |
| }), | |
| } | |
| # Create initial grid using sites and locations from phy_tile's | |
| # Also build up tile_to_tile_type_pkeys, which is a map from original | |
| # tile_type_pkey, to array of split tile type pkeys, (e.g. SLICEL/SLICEM). | |
| tile_to_tile_type_pkeys = {} | |
| split_map = {} | |
| grid_loc_map = {} | |
| for phy_tile_pkey, tile_type_pkey, grid_x, grid_y in progressbar_utils.progressbar( | |
| cur.execute(""" | |
| SELECT pkey, tile_type_pkey, grid_x, grid_y FROM phy_tile; | |
| """)): | |
| cur2.execute( | |
| "SELECT name FROM tile_type WHERE pkey = ?;", (tile_type_pkey, ) | |
| ) | |
| tile_type_name = cur2.fetchone()[0] | |
| sites = [] | |
| site_pkeys = set() | |
| for (site_pkey, ) in cur2.execute(""" | |
| SELECT site_pkey FROM wire_in_tile WHERE tile_type_pkey = ? AND site_pkey IS NOT NULL;""", | |
| (tile_type_pkey, )): | |
| site_pkeys.add(site_pkey) | |
| for site_pkey in site_pkeys: | |
| cur2.execute( | |
| """ | |
| SELECT x_coord, y_coord, site_type_pkey | |
| FROM site WHERE pkey = ?;""", (site_pkey, ) | |
| ) | |
| result = cur2.fetchone() | |
| assert result is not None, (tile_type_pkey, site_pkey) | |
| x, y, site_type_pkey = result | |
| cur2.execute( | |
| "SELECT name FROM site_type WHERE pkey = ?;", | |
| ((site_type_pkey, )) | |
| ) | |
| site_type_name = cur2.fetchone()[0] | |
| sites.append( | |
| tile_splitter.grid.Site( | |
| name=site_type_name, | |
| phy_tile_pkey=phy_tile_pkey, | |
| tile_type_pkey=tile_type_pkey, | |
| site_type_pkey=site_type_pkey, | |
| site_pkey=site_pkey, | |
| x=x, | |
| y=y | |
| ) | |
| ) | |
| sites = sorted(sites, key=lambda s: (s.x, s.y)) | |
| if tile_type_name in tiles_to_split: | |
| tile_type_pkeys = [] | |
| tile_split_map = {} | |
| if split_styles[tile_type_name][0] == 'site_as_tile': | |
| for idx, site in enumerate(sites): | |
| tile_type_pkeys.append(slice_types[site.name]) | |
| tile_split_map[site.x, site.y] = idx | |
| elif split_styles[tile_type_name][0] == 'y_split': | |
| ys = set() | |
| for site in sites: | |
| ys.add(site.y) | |
| ys = sorted(ys) | |
| tile_type_pkeys = [ | |
| split_styles[tile_type_name][1][y] for y in ys | |
| ] | |
| for site in sites: | |
| tile_split_map[site.x, site.y] = ys.index(site.y) | |
| else: | |
| assert False, split_styles[tile_type_name] | |
| if tile_type_name in tile_to_tile_type_pkeys: | |
| assert tile_to_tile_type_pkeys[ | |
| tile_type_name] == tile_type_pkeys, (tile_type_name, ) | |
| assert split_map[tile_type_name] == tile_split_map, ( | |
| tile_type_name, | |
| ) | |
| else: | |
| tile_to_tile_type_pkeys[tile_type_name] = tile_type_pkeys | |
| split_map[tile_type_name] = tile_split_map | |
| grid_loc_map[(grid_x, grid_y)] = tile_splitter.grid.Tile( | |
| root_phy_tile_pkeys=[phy_tile_pkey], | |
| phy_tile_pkeys=[phy_tile_pkey], | |
| tile_type_pkey=tile_type_pkey, | |
| sites=sites | |
| ) | |
| cur.execute('SELECT pkey FROM tile_type WHERE name = "NULL";') | |
| empty_tile_type_pkey = cur.fetchone()[0] | |
| tile_types = {} | |
| tile_type_names = {} | |
| for tile_type, _ in tiles_to_merge.items(): | |
| cur.execute( | |
| 'SELECT pkey FROM tile_type WHERE name = ?;', (tile_type, ) | |
| ) | |
| tile_type_pkey = cur.fetchone() | |
| if tile_type_pkey is not None: | |
| tile_type_pkey = tile_type_pkey[0] | |
| tile_types[tile_type] = tile_type_pkey | |
| tile_type_names[tile_type_pkey] = tile_type | |
| for tile_type, _ in tiles_to_split.items(): | |
| cur.execute( | |
| 'SELECT pkey FROM tile_type WHERE name = ?;', (tile_type, ) | |
| ) | |
| tile_type_pkey = cur.fetchone() | |
| if tile_type_pkey is not None: | |
| tile_type_pkey = tile_type_pkey[0] | |
| tile_types[tile_type] = tile_type_pkey | |
| tile_type_names[tile_type_pkey] = tile_type | |
| vpr_grid = tile_splitter.grid.Grid( | |
| grid_loc_map=grid_loc_map, empty_tile_type_pkey=empty_tile_type_pkey | |
| ) | |
| # Merge tiles first, so any splits account for sites the show up after a | |
| # merge. | |
| for tile_type, merge_direction in progressbar_utils.progressbar( | |
| tiles_to_merge.items()): | |
| if tile_type in tile_types: | |
| vpr_grid.merge_tile_type( | |
| tile_type_pkey=tile_types[tile_type], | |
| merge_direction=merge_direction, | |
| ) | |
| for tile_type, split_direction in progressbar_utils.progressbar( | |
| tiles_to_split.items()): | |
| if tile_type in tile_types: | |
| vpr_grid.split_tile_type( | |
| tile_type_pkey=tile_types[tile_type], | |
| tile_type_pkeys=tile_to_tile_type_pkeys[tile_type], | |
| split_direction=split_direction, | |
| split_map=split_map[tile_type], | |
| ) | |
| new_grid = vpr_grid.output_grid() | |
| shifted_grid = {} | |
| for (grid_x, grid_y), tile in new_grid.items(): | |
| # Shift grid away from 0 edges, which have limitations in VPR that | |
| # complicate the rrgraph import process. | |
| grid_x += 1 | |
| grid_y += 1 | |
| shifted_grid[(grid_x, grid_y)] = tile | |
| new_grid = shifted_grid | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| # Create tile rows for each tile in the VPR grid. As provide map entries | |
| # to physical grid and alias map from split tile type to original tile | |
| # type. | |
| for (grid_x, grid_y), tile in new_grid.items(): | |
| if tile.split_sites and len(tile.sites) == 1: | |
| write_cur.execute( | |
| """ | |
| SELECT pkey, parent_tile_type_pkey FROM site_as_tile WHERE tile_type_pkey = ? AND site_pkey = ?""", | |
| (tile.sites[0].tile_type_pkey, tile.sites[0].site_pkey) | |
| ) | |
| result = write_cur.fetchone() | |
| if result is None: | |
| write_cur.execute( | |
| """ | |
| INSERT INTO | |
| site_as_tile(parent_tile_type_pkey, tile_type_pkey, site_pkey) | |
| VALUES | |
| (?, ?, ?);""", ( | |
| tile.tile_type_pkey, tile.sites[0].tile_type_pkey, | |
| tile.sites[0].site_pkey | |
| ) | |
| ) | |
| site_as_tile_pkey = write_cur.lastrowid | |
| else: | |
| site_as_tile_pkey, parent_tile_type_pkey = result | |
| assert parent_tile_type_pkey == tile.tile_type_pkey | |
| # Mark that this tile is split by setting the site_as_tile_pkey. | |
| write_cur.execute( | |
| """ | |
| INSERT INTO tile(phy_tile_pkey, tile_type_pkey, site_as_tile_pkey, grid_x, grid_y) VALUES ( | |
| ?, ?, ?, ?, ?)""", ( | |
| tile.phy_tile_pkeys[0], tile.tile_type_pkey, | |
| site_as_tile_pkey, grid_x, grid_y | |
| ) | |
| ) | |
| else: | |
| phy_tile_pkey = None | |
| if len(tile.phy_tile_pkeys) > 0: | |
| phy_tile_pkey = tile.phy_tile_pkeys[0] | |
| write_cur.execute( | |
| """ | |
| INSERT INTO tile(phy_tile_pkey, tile_type_pkey, grid_x, grid_y) VALUES ( | |
| ?, ?, ?, ?)""", (phy_tile_pkey, tile.tile_type_pkey, grid_x, grid_y) | |
| ) | |
| tile_pkey = write_cur.lastrowid | |
| # Build the phy_tile <-> tile map. | |
| for phy_tile_pkey in tile.phy_tile_pkeys: | |
| write_cur.execute( | |
| """ | |
| INSERT INTO tile_map(tile_pkey, phy_tile_pkey) VALUES (?, ?) | |
| """, (tile_pkey, phy_tile_pkey) | |
| ) | |
| # First assign all wires at the root_phy_tile_pkeys to this tile_pkey. | |
| # This ensures all wires, (including wires without sites) have a home. | |
| for root_phy_tile_pkey in tile.root_phy_tile_pkeys: | |
| write_cur.execute( | |
| """ | |
| UPDATE | |
| wire | |
| SET | |
| tile_pkey = ? | |
| WHERE | |
| phy_tile_pkey = ? | |
| ;""", (tile_pkey, root_phy_tile_pkey) | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX tile_location_index ON tile(grid_x, grid_y);" | |
| ) | |
| write_cur.execute("CREATE INDEX tile_to_phy_map ON tile_map(tile_pkey);") | |
| write_cur.execute( | |
| "CREATE INDEX phy_to_tile_map ON tile_map(phy_tile_pkey);" | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| # Track wire_in_tile_pkey's to update to new tile types. | |
| wire_in_tile_pkey_to_update = {} | |
| # At this point all wires have a tile_pkey that corrisponds to the old root | |
| # tile. Wires belonging to sites need to be reassigned to their respective | |
| # tiles. | |
| for (grid_x, grid_y), tile in new_grid.items(): | |
| cur2.execute( | |
| "SELECT pkey, tile_type_pkey FROM tile WHERE grid_x = ? AND grid_y = ?;", | |
| (grid_x, grid_y) | |
| ) | |
| tile_pkey, tile_type_pkey = cur2.fetchone() | |
| for site in tile.sites: | |
| # Find all wires that belong to the new tile location. | |
| for wire_pkey, wire_in_tile_pkey in cur2.execute(""" | |
| WITH wires(wire_pkey, wire_in_tile_pkey) AS ( | |
| SELECT | |
| pkey, wire_in_tile_pkey | |
| FROM | |
| wire | |
| WHERE | |
| phy_tile_pkey = ? | |
| ) | |
| SELECT | |
| wires.wire_pkey, wires.wire_in_tile_pkey | |
| FROM | |
| wires | |
| INNER JOIN | |
| wire_in_tile | |
| ON | |
| wire_in_tile.pkey = wires.wire_in_tile_pkey | |
| WHERE | |
| wire_in_tile.site_pkey = ? | |
| ;""", (site.phy_tile_pkey, site.site_pkey)): | |
| # Move the wire to the new tile_pkey. | |
| write_cur.execute( | |
| """ | |
| UPDATE | |
| wire | |
| SET | |
| tile_pkey = ? | |
| WHERE | |
| pkey = ?;""", ( | |
| tile_pkey, | |
| wire_pkey, | |
| ) | |
| ) | |
| if tile_type_pkey not in wire_in_tile_pkey_to_update: | |
| wire_in_tile_pkey_to_update[tile_type_pkey] = set() | |
| wire_in_tile_pkey_to_update[tile_type_pkey].add( | |
| wire_in_tile_pkey | |
| ) | |
| # Wires connected to the site via a pip require traversing the | |
| # pip. | |
| other_wire_in_tile_pkey = traverse_pip(conn, wire_in_tile_pkey) | |
| if other_wire_in_tile_pkey is not None: | |
| cur.execute( | |
| """ | |
| SELECT classification | |
| FROM node | |
| WHERE pkey = ( | |
| SELECT node_pkey | |
| FROM wire | |
| WHERE | |
| phy_tile_pkey = ? | |
| AND | |
| wire_in_tile_pkey = ? | |
| ) | |
| ;""", (site.phy_tile_pkey, other_wire_in_tile_pkey) | |
| ) | |
| classification = NodeClassification(cur.fetchone()[0]) | |
| if classification != NodeClassification.CHANNEL: | |
| continue | |
| # A wire was found connected to the site via pip, reassign | |
| # tile_pkey. | |
| write_cur.execute( | |
| """ | |
| UPDATE | |
| wire | |
| SET | |
| tile_pkey = ? | |
| WHERE | |
| phy_tile_pkey = ? | |
| AND | |
| wire_in_tile_pkey = ? | |
| ;""", (tile_pkey, site.phy_tile_pkey, other_wire_in_tile_pkey) | |
| ) | |
| # Now that final wire <-> tile assignments are made, create the index. | |
| write_cur.execute( | |
| "CREATE INDEX tile_wire_index ON wire(wire_in_tile_pkey, tile_pkey, node_pkey);" | |
| ) | |
| write_cur.execute( | |
| "CREATE INDEX node_tile_wire_index ON wire(node_pkey, tile_pkey, wire_in_tile_pkey);" | |
| ) | |
| # Update tile_type_pkey in wire_in_tile table. | |
| update_wire_in_tile_types( | |
| cur, write_cur, empty_tile_type_pkey, wire_in_tile_pkey_to_update | |
| ) | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| write_cur.execute("""BEGIN EXCLUSIVE TRANSACTION;""") | |
| write_cur.execute("ANALYZE") | |
| write_cur.execute("""COMMIT TRANSACTION;""") | |
| # Now that the final VPR grid is generated, we need a fast lookup to easily | |
| # access to the canonical to VPR grid mapping. | |
| # | |
| # Furthermore, information on the internal tile connections between sites belonging | |
| # to the same tile, which are treated as heterogeneous tiles. These connections are | |
| # required to correctly instruct VPR on the presence of direct connections between | |
| # sites at the same X, Y coordinate, but with different sub tile coordinates. | |
| fieldnames = [ | |
| "site_name", | |
| "site_type", | |
| "physical_tile", | |
| "vpr_x", | |
| "vpr_y", | |
| "canon_x", | |
| "canon_y", | |
| "clock_region", | |
| "connected_to_site", | |
| ] | |
| csv_writer = csv.DictWriter(grid_map_output, fieldnames=fieldnames) | |
| csv_writer.writeheader() | |
| # Find all sites that have direct connections with other sites in the same tile. | |
| sites_with_direct = dict() | |
| for src_wire_pkey, dest_wire_pkey, pip_in_tile_pkey in \ | |
| progressbar_utils.progressbar( | |
| cur.execute(""" | |
| SELECT | |
| src_wire_pkey, | |
| dest_wire_pkey, | |
| pip_in_tile_pkey | |
| FROM | |
| edge_with_mux""")): | |
| cur2.execute( | |
| """ | |
| SELECT node_pkey FROM wire WHERE pkey = ?""", (src_wire_pkey, ) | |
| ) | |
| (src_node_pkey, ) = cur2.fetchone() | |
| # Find the wire connected to the source. | |
| src_wire = list(node_to_site_pins(conn, src_node_pkey)) | |
| assert len(src_wire) == 1 | |
| _, src_tile_pkey, src_wire_in_tile_pkey = src_wire[0] | |
| cur2.execute( | |
| """ | |
| SELECT tile_type_pkey, grid_x, grid_y FROM tile WHERE pkey = ?""", | |
| (src_tile_pkey, ) | |
| ) | |
| res = cur2.fetchone() | |
| src_tile_type_pkey, src_grid_x, src_grid_y = res | |
| # Get the node that is attached to the sink. | |
| cur2.execute( | |
| """ | |
| SELECT node_pkey FROM wire WHERE pkey = ?""", (dest_wire_pkey, ) | |
| ) | |
| (dest_node_pkey, ) = cur2.fetchone() | |
| # Find the wire connected to the sink. | |
| dest_wire = list(node_to_site_pins(conn, dest_node_pkey)) | |
| assert len(dest_wire) == 1 | |
| _, dest_tile_pkey, dest_wire_in_tile_pkey = dest_wire[0] | |
| cur2.execute( | |
| """ | |
| SELECT tile_type_pkey, grid_x, grid_y FROM tile WHERE pkey = ?;""", | |
| (dest_tile_pkey, ) | |
| ) | |
| res = cur2.fetchone() | |
| dest_tile_type_pkey, dest_grid_x, dest_grid_y = res | |
| if dest_grid_x == src_grid_x and dest_grid_y == src_grid_y: | |
| tile = new_grid[(dest_grid_x, dest_grid_y)] | |
| cur2.execute( | |
| "SELECT site_pkey FROM wire_in_tile WHERE pkey = ?", | |
| (src_wire_in_tile_pkey, ) | |
| ) | |
| src_site_pkey = cur2.fetchone()[0] | |
| cur2.execute( | |
| "SELECT site_pkey FROM wire_in_tile WHERE pkey = ?", | |
| (dest_wire_in_tile_pkey, ) | |
| ) | |
| dest_site_pkey = cur2.fetchone()[0] | |
| src_site_instance = None | |
| dest_site_instance = None | |
| for site in tile.sites: | |
| cur2.execute( | |
| "SELECT name FROM site_instance WHERE site_pkey = ? AND phy_tile_pkey = ?" | |
| "", ( | |
| site.site_pkey, | |
| site.phy_tile_pkey, | |
| ) | |
| ) | |
| res = cur2.fetchone()[0] | |
| if site.site_pkey == src_site_pkey: | |
| src_site_instance = res | |
| elif site.site_pkey == dest_site_pkey: | |
| dest_site_instance = res | |
| assert src_site_instance and dest_site_instance | |
| sites_with_direct[src_site_instance] = dest_site_instance | |
| # Build the Canonical to VPR grid mapping | |
| for (grid_x, grid_y), tile in new_grid.items(): | |
| for site in tile.sites: | |
| cur.execute( | |
| "SELECT name FROM site_instance WHERE site_pkey = ? AND phy_tile_pkey = ?", | |
| ( | |
| site.site_pkey, | |
| site.phy_tile_pkey, | |
| ) | |
| ) | |
| site_name = cur.fetchone()[0] | |
| cur.execute( | |
| "SELECT name, clock_region_pkey, grid_x, grid_y FROM phy_tile WHERE pkey = ?", | |
| (site.phy_tile_pkey, ) | |
| ) | |
| phy_tile_name, clock_region, canon_x, canon_y = cur.fetchone() | |
| cur.execute( | |
| "SELECT name FROM site_type WHERE pkey = ?", | |
| (site.site_type_pkey, ) | |
| ) | |
| site_type = cur.fetchone()[0] | |
| # This is a site which is directly connected to the current site | |
| # and will be implemented as a sub_tile. This information is relevant | |
| # to generate correct placement constraints for sub_tiles directly | |
| # connected within the same physical tile | |
| connected_to_site = sites_with_direct.get(site_name, None) | |
| csv_writer.writerow( | |
| { | |
| "site_name": site_name, | |
| "site_type": site_type, | |
| "physical_tile": phy_tile_name, | |
| "vpr_x": grid_x, | |
| "vpr_y": grid_y, | |
| "canon_x": canon_x, | |
| "canon_y": canon_y, | |
| "clock_region": clock_region, | |
| "connected_to_site": connected_to_site | |
| } | |
| ) | |
| def create_constant_tracks(conn): | |
| """ Create two tracks that go to all TIEOFF sites to route constants. | |
| Returns (vcc_track_to_insert, gnd_track_to_insert), suitable for insert | |
| via insert_tracks function. | |
| """ | |
| # Make constant track available to all tiles. | |
| write_cur = conn.cursor() | |
| unique_pos = set() | |
| write_cur.execute('SELECT grid_x, grid_y FROM tile') | |
| for grid_x, grid_y in write_cur: | |
| if grid_x == 0 or grid_y == 0: | |
| continue | |
| unique_pos.add((grid_x, grid_y)) | |
| write_cur.execute( | |
| """ | |
| INSERT INTO node(classification) VALUES (?) | |
| """, (NodeClassification.CHANNEL.value, ) | |
| ) | |
| vcc_node = write_cur.lastrowid | |
| write_cur.execute( | |
| """ | |
| INSERT INTO node(classification) VALUES (?) | |
| """, (NodeClassification.CHANNEL.value, ) | |
| ) | |
| gnd_node = write_cur.lastrowid | |
| conn.commit() | |
| return create_track(vcc_node, | |
| unique_pos), create_track(gnd_node, unique_pos) | |
| def import_segments(conn, db): | |
| """ Create segment table, use segment definitions from SegmentWireMap. | |
| Parameters | |
| ---------- | |
| conn : sqlite3.Connection | |
| Connection to channels.db | |
| db : prjxray.Database | |
| Project X-Ray database object. | |
| Returns | |
| ------- | |
| SegmentWireMap | |
| SegmentWireMap can be used to detect which segment a wire belongs too. | |
| """ | |
| write_cur = conn.cursor() | |
| default_segment = "unknown" | |
| segments = SegmentWireMap(default_segment=default_segment, db=db) | |
| # Add some additional useful segments that are not part of the wire map | |
| # (e.g. the global constant networks). | |
| for extra_segment in [default_segment, VCC_NET, GND_NET]: | |
| if extra_segment not in segments.get_segments(): | |
| write_cur.execute( | |
| """ | |
| INSERT INTO segment(name) VALUES (?) | |
| """, (extra_segment, ) | |
| ) | |
| for segment in segments.get_segments(): | |
| write_cur.execute( | |
| """ | |
| INSERT INTO segment(name) VALUES (?) | |
| """, (segment, ) | |
| ) | |
| write_cur.execute("CREATE INDEX segment_name_map ON segment(name);") | |
| conn.commit() | |
| return segments | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument( | |
| '--db_root', help='Project X-Ray Database', required=True | |
| ) | |
| parser.add_argument('--part', help='FPGA part', required=True) | |
| parser.add_argument( | |
| '--connection_database', help='Connection database', required=True | |
| ) | |
| parser.add_argument( | |
| '--grid_map_output', | |
| help='Location of the grid map output', | |
| required=True | |
| ) | |
| args = parser.parse_args() | |
| if os.path.exists(args.connection_database): | |
| os.remove(args.connection_database) | |
| with DatabaseCache(args.connection_database) as conn: | |
| create_tables(conn) | |
| print("{}: About to load database".format(datetime.datetime.now())) | |
| db = prjxray.db.Database(args.db_root, args.part) | |
| grid = db.grid() | |
| get_switch, get_switch_timing = create_get_switch(conn) | |
| import_phy_grid(db, grid, conn, get_switch, get_switch_timing) | |
| segments = import_segments(conn, db) | |
| print("{}: Initial database formed".format(datetime.datetime.now())) | |
| import_nodes(db, grid, conn) | |
| print("{}: Connections made".format(datetime.datetime.now())) | |
| count_sites_and_pips_on_nodes(conn) | |
| print("{}: Counted sites and pips".format(datetime.datetime.now())) | |
| classify_nodes(conn, get_switch_timing) | |
| print("{}: Create VPR grid".format(datetime.datetime.now())) | |
| with open(args.grid_map_output, 'w') as f: | |
| create_vpr_grid(conn, f) | |
| print("{}: Nodes classified".format(datetime.datetime.now())) | |
| form_tracks(conn, segments) | |
| print("{}: Tracks formed".format(datetime.datetime.now())) | |
| print( | |
| '{} Flushing database back to file "{}"'.format( | |
| datetime.datetime.now(), args.connection_database | |
| ) | |
| ) | |
| if __name__ == '__main__': | |
| main() |