blob: 2454f312bdcb6f058820f369eee617dc2c642dfc [file] [log] [blame]
#!/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/f4pga-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()