blob: 3d700815bd95e75a4ff5fdf9ebf89f9bb25746fe [file] [log] [blame] [edit]
""" Convert a PCF file into a VPR io.place file. """
from __future__ import print_function
import argparse
import eblif
import sys
import csv
import os
import vpr_place_constraints
import lxml.etree as ET
import constraint
import prjxray.db
from collections import defaultdict
CLOCKS = {
"PLLE2_ADV_VPR":
{
"sinks":
frozenset(("CLKFBIN", "CLKIN1", "CLKIN2", "DCLK")),
"sources":
frozenset(
(
"CLKFBOUT", "CLKOUT0", "CLKOUT1", "CLKOUT2", "CLKOUT3",
"CLKOUT4", "CLKOUT5"
)
),
"type":
"PLLE2_ADV",
},
"MMCME2_ADV_VPR":
{
"sinks":
frozenset(("CLKFBIN", "CLKIN1", "CLKIN2", "DCLK", "PSCLK")),
"sources":
frozenset(
(
"CLKFBOUT",
"CLKFBOUTB",
"CLKOUT0",
"CLKOUT0B",
"CLKOUT1",
"CLKOUT1B",
"CLKOUT2",
"CLKOUT2B",
"CLKOUT3",
"CLKOUT3B",
"CLKOUT4",
"CLKOUT5",
"CLKOUT6",
)
),
"type":
"MMCME2_ADV",
},
"BUFGCTRL_VPR":
{
"sinks": frozenset(("I0", "I1")),
"sources": frozenset(("O", )),
"type": "BUFGCTRL",
},
"PS7_VPR":
{
"sinks":
frozenset(
(
"DMA0ACLK",
"DMA1ACLK",
"DMA2ACLK",
"DMA3ACLK",
"EMIOENET0GMIIRXCLK",
"EMIOENET0GMIITXCLK",
"EMIOENET1GMIIRXCLK",
"EMIOENET1GMIITXCLK",
"EMIOSDIO0CLKFB",
"EMIOSDIO1CLKFB",
"EMIOSPI0SCLKI",
"EMIOSPI1SCLKI",
"EMIOTRACECLK",
"EMIOTTC0CLKI[0]",
"EMIOTTC0CLKI[1]",
"EMIOTTC0CLKI[2]",
"EMIOTTC1CLKI[0]",
"EMIOTTC1CLKI[1]",
"EMIOTTC1CLKI[2]",
"EMIOWDTCLKI",
"FCLKCLKTRIGN[0]",
"FCLKCLKTRIGN[1]",
"FCLKCLKTRIGN[2]",
"FCLKCLKTRIGN[3]",
"MAXIGP0ACLK",
"MAXIGP1ACLK",
"SAXIACPACLK",
"SAXIGP0ACLK",
"SAXIGP1ACLK",
"SAXIHP0ACLK",
"SAXIHP1ACLK",
"SAXIHP2ACLK",
"SAXIHP3ACLK",
)
),
"sources":
frozenset(
(
"FCLKCLK[0]",
"FCLKCLK[1]",
"FCLKCLK[2]",
"FCLKCLK[3]",
"EMIOSDIO0CLK",
"EMIOSDIO1CLK",
# There are also EMIOSPI[01]CLKO and EMIOSPI[01]CLKTN but seem
# to be more of a GPIO outputs than clock sources for the FPGA
# fabric.
)
),
"type":
"PS7",
},
"IBUF_VPR":
{
"sources": frozenset(("O", )),
"sinks": frozenset(("I", )),
"type": "IBUF",
},
"IBUFDS_GTE2_VPR":
{
"sources": frozenset(("O", )),
"sinks": frozenset(),
"type": "IBUFDS_GTE2",
},
"GTPE2_COMMON_VPR":
{
"sources": frozenset(),
"sinks": frozenset(("GTREFCLK0", "GTREFCLK1")),
"type": "GTPE2_COMMON",
},
"GTPE2_CHANNEL_VPR":
{
"sources": frozenset(("TXOUTCLK", "RXOUTCLK")),
"sinks": frozenset(),
"type": "GTPE2_CHANNEL",
},
}
GTP_PRIMITIVES = ["IBUFDS_GTE2", "GTPE2_COMMON", "GTPE2_CHANNEL"]
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def get_cmt(cmt_dict, loc):
"""Returns the clock region of an input location."""
for k, v in cmt_dict.items():
for (x, y) in v['vpr_loc']:
if x == loc[0] and y == loc[1]:
return v['clock_region']
return None
class VprGrid(object):
"""This class contains a set of dictionaries helpful
to have a fast lookup at the various coordinates mapping."""
def __init__(self, vpr_grid_map, graph_limit):
self.site_dict = dict()
self.site_type_dict = dict()
self.cmt_dict = dict()
self.tile_dict = dict()
self.vpr_loc_cmt = dict()
self.canon_loc = dict()
if graph_limit is not None:
limits = graph_limit.split(",")
xmin, ymin, xmax, ymax = [int(x) for x in limits]
with open(vpr_grid_map, 'r') as csv_vpr_grid:
csv_reader = csv.DictReader(csv_vpr_grid)
for row in csv_reader:
site_name = row['site_name']
site_type = row['site_type']
phy_tile = row['physical_tile']
vpr_x = row['vpr_x']
vpr_y = row['vpr_y']
can_x = row['canon_x']
can_y = row['canon_y']
clk_region = row['clock_region']
connected_to_site = row['connected_to_site']
if graph_limit is not None:
if int(can_x) < xmin or int(can_x) > xmax:
continue
if int(can_y) < ymin or int(can_y) > ymax:
continue
clk_region = None if not clk_region else int(clk_region)
# Generating the site dictionary
self.site_dict[site_name] = {
'type': site_type,
'tile': phy_tile,
'vpr_loc': (int(vpr_x), int(vpr_y)),
'canon_loc': (int(can_x), int(can_y)),
'clock_region': clk_region,
'connected_to_site': connected_to_site,
}
# Generating site types dictionary.
if site_type not in self.site_type_dict:
self.site_type_dict[site_type] = []
self.site_type_dict[site_type].append(
(site_name, phy_tile, clk_region)
)
# Generating the cmt dictionary.
# Each entry has:
# - canonical location
# - a list of vpr coordinates
# - clock region
if phy_tile not in self.cmt_dict:
self.cmt_dict[phy_tile] = {
'canon_loc': (int(can_x), int(can_y)),
'vpr_loc': [(int(vpr_x), int(vpr_y))],
'clock_region': clk_region,
}
else:
self.cmt_dict[phy_tile]['vpr_loc'].append(
(int(vpr_x), int(vpr_y))
)
# Generating the tile dictionary.
# Each tile has a list of (site, site_type) pairs
if phy_tile not in self.tile_dict:
self.tile_dict[phy_tile] = []
self.tile_dict[phy_tile].append((site_name, site_type))
self.vpr_loc_cmt[(int(vpr_x), int(vpr_y))] = clk_region
self.canon_loc[(int(vpr_x),
int(vpr_y))] = (int(can_x), int(can_y))
def get_site_dict(self):
return self.site_dict
def get_site_type_dict(self):
return self.site_type_dict
def get_cmt_dict(self):
return self.cmt_dict
def get_tile_dict(self):
return self.tile_dict
def get_vpr_loc_cmt(self):
"""
Returns a dictionary containing the mapping between
VPR physical locations to the belonging clock region.
Dictionary content:
- key : (x, y) coordinate on the VPR grid
- value : clock region corresponding to the (x, y) coordinates
"""
return self.vpr_loc_cmt
def get_canon_loc(self):
return self.canon_loc
class ClockPlacer(object):
def __init__(
self,
vpr_grid,
io_locs,
blif_data,
roi,
graph_limit,
allow_bufg_logic_sources=False
):
self.roi = roi
self.cmt_to_bufg_tile = {}
self.bufg_from_cmt = {
'TOP': [],
'BOT': [],
}
self.pll_cmts = set()
self.mmcm_cmts = set()
self.gtp_cmts = set()
cmt_dict = vpr_grid.get_cmt_dict()
site_type_dict = vpr_grid.get_site_type_dict()
try:
top_cmt_tile = next(
k for k, v in cmt_dict.items() if k.startswith('CLK_BUFG_TOP')
)
_, thresh_top_y = cmt_dict[top_cmt_tile]['canon_loc']
except StopIteration:
thresh_top_y = None
try:
bot_cmt_tile = next(
k for k, v in cmt_dict.items() if k.startswith('CLK_BUFG_BOT')
)
_, thresh_bot_y = cmt_dict[bot_cmt_tile]['canon_loc']
except StopIteration:
thresh_bot_y = None
if graph_limit is None:
assert thresh_top_y is not None, "BUFG sites in the top half of the device not found"
assert thresh_bot_y is not None, "BUFG sites in the bottom half of the device not found"
else:
assert thresh_top_y is not None or thresh_bot_y is not None, (
"The device grid does not contain any BUFG sites"
)
for k, v in cmt_dict.items():
clock_region = v['clock_region']
x, y = v['canon_loc']
if clock_region is None:
continue
elif clock_region in self.cmt_to_bufg_tile:
continue
elif thresh_top_y is not None and y <= thresh_top_y:
self.cmt_to_bufg_tile[clock_region] = "TOP"
elif thresh_bot_y is not None and y >= thresh_bot_y:
self.cmt_to_bufg_tile[clock_region] = "BOT"
for _, _, clk_region in site_type_dict['PLLE2_ADV']:
self.pll_cmts.add(clk_region)
if any(site in GTP_PRIMITIVES for site in site_type_dict):
assert "IBUFDS_GTE2" in site_type_dict
for _, _, clk_region in site_type_dict['IBUFDS_GTE2']:
self.gtp_cmts.add(clk_region)
for _, _, clk_region in site_type_dict['MMCME2_ADV']:
self.mmcm_cmts.add(clk_region)
self.input_pins = {}
if not self.roi:
for input_pin in blif_data['inputs']['args']:
if input_pin not in io_locs.keys():
continue
loc = io_locs[input_pin]
cmt = get_cmt(cmt_dict, loc)
assert cmt is not None, loc
self.input_pins[input_pin] = cmt
self.clock_blocks = {}
self.clock_sources = {}
self.clock_sources_cname = {}
self.clock_cmts = {}
if "subckt" not in blif_data.keys():
return
for subckt in blif_data["subckt"]:
if 'cname' not in subckt:
continue
bel = subckt['args'][0]
assert 'cname' in subckt and len(subckt['cname']) == 1, subckt
if bel not in CLOCKS:
continue
cname = subckt['cname'][0]
clock = {
'name': cname,
'subckt': bel,
'sink_nets': [],
'source_nets': [],
}
sources = CLOCKS[bel]['sources']
ports = dict(
arg.split('=', maxsplit=1) for arg in subckt['args'][1:]
)
for source in sources:
source_net = ports[source]
if source_net == '$true' or source_net == '$false':
continue
self.clock_sources[source_net] = []
self.clock_sources_cname[source_net] = cname
clock['source_nets'].append(source_net)
self.clock_blocks[cname] = clock
# Both PS7 and BUFGCTRL has specialized constraints,
# do not bind based on input pins.
if bel not in ['PS7_VPR', 'BUFGCTRL_VPR']:
for port in ports.values():
if port not in io_locs:
continue
if cname in self.clock_cmts:
assert_out = (
cname, port, self.clock_cmts[cname],
self.input_pins[port]
)
assert self.clock_cmts[cname] == self.input_pins[
port], assert_out
else:
self.clock_cmts[cname] = self.input_pins[port]
for subckt in blif_data["subckt"]:
if 'cname' not in subckt:
continue
bel = subckt['args'][0]
if bel not in CLOCKS:
continue
sinks = CLOCKS[bel]['sinks']
ports = dict(
arg.split('=', maxsplit=1) for arg in subckt['args'][1:]
)
assert 'cname' in subckt and len(subckt['cname']) == 1, subckt
cname = subckt['cname'][0]
clock = self.clock_blocks[cname]
for sink in sinks:
if sink not in ports:
continue
sink_net = ports[sink]
if sink_net == '$true' or sink_net == '$false':
continue
clock['sink_nets'].append(sink_net)
if sink_net not in self.input_pins and sink_net not in self.clock_sources:
# Allow IBUFs. At this point we cannot distinguish between
# IBUFs for clock and logic.
if bel == "IBUF_VPR":
continue
# Allow BUFGs to be driven by generic sources but only
# when enabled.
if bel == "BUFGCTRL_VPR" and allow_bufg_logic_sources:
continue
# The clock source comes from logic, disallow that
eprint(
"The clock net '{}' driving '{}' sources at logic which is not allowed!"
.format(sink_net, bel)
)
exit(-1)
if sink_net in self.input_pins:
if sink_net not in self.clock_sources:
self.clock_sources[sink_net] = []
self.clock_sources[sink_net].append(cname)
def assign_cmts(self, vpr_grid, blocks, block_locs):
""" Assign CMTs to subckt's that require it (e.g. BURF/PLL/MMCM). """
problem = constraint.Problem()
site_dict = vpr_grid.get_site_dict()
vpr_loc_cmt = vpr_grid.get_vpr_loc_cmt()
# Any clocks that have LOC's already defined should be respected.
# Store the parent CMT in clock_cmts.
for block, loc in block_locs.items():
if block in self.clock_blocks:
clock = self.clock_blocks[block]
if CLOCKS[clock['subckt']]['type'] == 'BUFGCTRL':
pass
else:
site = site_dict[loc.replace('"', '')]
assert site is not None, (block, loc)
clock_region_pkey = site["clock_region"]
if block in self.clock_cmts:
assert clock_region_pkey == self.clock_cmts[block], (
block, clock_region_pkey
)
else:
self.clock_cmts[block] = clock_region_pkey
# Any clocks that were previously constrained must be preserved
for block, (loc_x, loc_y, _) in blocks.items():
if block in self.clock_blocks:
clock = self.clock_blocks[block]
if CLOCKS[clock['subckt']]['type'] == 'BUFGCTRL':
pass
else:
cmt = vpr_loc_cmt[(loc_x, loc_y)]
if block in self.clock_cmts:
assert cmt == self.clock_cmts[block], (block, cmt)
else:
self.clock_cmts[block] = cmt
# Non-clock IBUF's increase the solution space if they are not
# constrainted by a LOC. Given that non-clock IBUF's don't need to
# solved for, remove them.
unused_blocks = set()
any_variable = False
for clock_name, clock in self.clock_blocks.items():
used = False
if CLOCKS[clock['subckt']]['type'] != 'IBUF':
used = True
for source_net in clock['source_nets']:
if len(self.clock_sources[source_net]) > 0:
used = True
break
if not used:
unused_blocks.add(clock_name)
continue
if CLOCKS[clock['subckt']]['type'] == 'BUFGCTRL':
problem.addVariable(
clock_name, list(self.bufg_from_cmt.keys())
)
any_variable = True
else:
# Constrained objects have a domain of 1
if clock_name in self.clock_cmts:
problem.addVariable(
clock_name, (self.clock_cmts[clock_name], )
)
any_variable = True
else:
problem.addVariable(
clock_name, list(self.cmt_to_bufg_tile.keys())
)
any_variable = True
# Remove unused blocks from solutions.
for clock_name in unused_blocks:
del self.clock_blocks[clock_name]
exclusive_clocks = defaultdict(set)
ibuf_cmt_sinks = defaultdict(set)
for net in self.clock_sources:
is_net_ibuf = False
if net not in self.input_pins:
source_clock_name = self.clock_sources_cname[net]
if source_clock_name not in unused_blocks:
source_block = self.clock_blocks[source_clock_name]
if CLOCKS[source_block['subckt']]['type'] == 'IBUF':
is_net_ibuf = True
for clock_name in self.clock_sources[net]:
if clock_name in unused_blocks:
continue
clock = self.clock_blocks[clock_name]
if CLOCKS[clock['subckt']]['type'] == 'PLLE2_ADV':
problem.addConstraint(
lambda cmt: cmt in self.pll_cmts, (clock_name, )
)
exclusive_clocks["PLLE2_ADV"].add(clock_name)
if is_net_ibuf:
ibuf_cmt_sinks[source_clock_name].add(clock_name)
if CLOCKS[clock['subckt']]['type'] == 'MMCME2_ADV':
problem.addConstraint(
lambda cmt: cmt in self.mmcm_cmts, (clock_name, )
)
exclusive_clocks["MMCME2_ADV"].add(clock_name)
if is_net_ibuf:
ibuf_cmt_sinks[source_clock_name].add(clock_name)
if net in self.input_pins:
if CLOCKS[clock['subckt']]['type'] == 'BUFGCTRL':
# BUFGCTRL do not get a CMT, instead they get either top or
# bottom.
problem.addConstraint(
lambda clock: clock == self.cmt_to_bufg_tile[
self.input_pins[net]], (clock_name, )
)
elif CLOCKS[clock['subckt']]['type'] != 'IBUF':
problem.addConstraint(
lambda clock: clock == self.input_pins[net],
(clock_name, )
)
else:
source_clock_name = self.clock_sources_cname[net]
source_block = self.clock_blocks[source_clock_name]
is_net_bufg = CLOCKS[source_block['subckt']
]['type'] == 'BUFGCTRL'
if is_net_bufg:
continue
if CLOCKS[clock['subckt']]['type'] == 'BUFGCTRL':
# BUFG's need to be in the right half.
problem.addConstraint(
lambda source, sink_bufg: self.cmt_to_bufg_tile[
source] == sink_bufg,
(source_clock_name, clock_name)
)
elif not is_net_ibuf:
problem.addConstraint(
lambda source, sink: source == sink,
(source_clock_name, clock_name)
)
# Ensure that in case of an IBUF driving multiple CMTs at least one of
# them is in the same clock region as the IBUF.
for source, sinks in ibuf_cmt_sinks.items():
problem.addConstraint(lambda s, *k: s in k, (
source,
*sinks,
))
# Prevent constraining exclusive clock source sets to the same resource
for typ, clocks in exclusive_clocks.items():
problem.addConstraint(
constraint.AllDifferentConstraint(), list(clocks)
)
if any_variable:
solutions = problem.getSolutions()
assert len(solutions) > 0, "No solution found!"
self.clock_cmts.update(solutions[0])
def place_clocks(
self, canon_grid, vpr_grid, loc_in_use, block_locs, blocks,
grid_capacities
):
self.assign_cmts(vpr_grid, blocks, block_locs)
site_type_dict = vpr_grid.get_site_type_dict()
# Key is (type, clock_region_pkey)
available_placements = {}
available_locs = {}
vpr_locs = {}
for clock_type in CLOCKS.values():
if clock_type == 'IBUF' or clock_type['type'] not in site_type_dict:
continue
for loc, tile_name, clock_region_pkey in site_type_dict[
clock_type['type']]:
if clock_type['type'] == 'BUFGCTRL':
if '_TOP_' in tile_name:
key = (clock_type['type'], 'TOP')
elif '_BOT_' in tile_name:
key = (clock_type['type'], 'BOT')
else:
key = (clock_type['type'], clock_region_pkey)
if key not in available_placements:
available_placements[key] = []
available_locs[key] = []
available_placements[key].append(loc)
vpr_loc = get_vpr_coords_from_site_name(
canon_grid, vpr_grid, loc, grid_capacities
)
if vpr_loc is None:
continue
available_locs[key].append(vpr_loc)
vpr_locs[loc] = vpr_loc
for clock_name, clock in self.clock_blocks.items():
# All clocks should have an assigned CMTs at this point.
assert clock_name in self.clock_cmts, clock_name
bel_type = CLOCKS[clock['subckt']]['type']
# Skip LOCing the PS7. There is only one
if bel_type == "PS7":
continue
if bel_type == 'IBUF':
continue
key = (bel_type, self.clock_cmts[clock_name])
if clock_name in blocks:
# This block has a LOC constraint from the user, verify that
# this LOC constraint makes sense.
assert blocks[clock_name] in available_locs[key], (
clock_name, blocks[clock_name], available_locs[key]
)
continue
loc = None
for potential_loc in sorted(available_placements[key]):
# This block has no existing placement, make one
vpr_loc = vpr_locs[potential_loc]
if vpr_loc in loc_in_use:
continue
loc_in_use.add(vpr_loc)
yield clock_name, potential_loc
loc = potential_loc
break
# No free LOC!!!
assert loc is not None, (clock_name, available_placements[key])
def has_clock_nets(self):
return self.clock_sources
def get_tile_capacities(arch_xml_filename):
arch = ET.parse(arch_xml_filename, ET.XMLParser(remove_blank_text=True))
root = arch.getroot()
tile_capacities = {}
for el in root.iter('tile'):
tile_name = el.attrib['name']
tile_capacities[tile_name] = 0
for sub_tile in el.iter('sub_tile'):
capacity = 1
if 'capacity' in sub_tile.attrib:
capacity = int(sub_tile.attrib['capacity'])
tile_capacities[tile_name] += capacity
grid = {}
for el in root.iter('single'):
x = int(el.attrib['x'])
y = int(el.attrib['y'])
grid[(x, y)] = tile_capacities[el.attrib['type']]
return grid
def get_vpr_coords_from_site_name(
canon_grid, vpr_grid, site_name, grid_capacities
):
site_name = site_name.replace('"', '')
site_dict = vpr_grid.get_site_dict()
canon_loc = vpr_grid.get_canon_loc()
tile = site_dict[site_name]['tile']
capacity = 0
x, y = site_dict[site_name]['vpr_loc']
canon_x, canon_y = canon_loc[(x, y)]
if (x, y) in grid_capacities.keys():
capacity = grid_capacities[(x, y)]
if not capacity:
# If capacity is zero it means that the site is out of
# the ROI, hence the site needs to be skipped.
return None
elif capacity == 1:
return (x, y, 0)
else:
sites = list(
canon_grid.gridinfo_at_loc((canon_x, canon_y)).sites.keys()
)
assert capacity == len(sites), (tile, capacity, (x, y))
instance_idx = sites.index(site_name)
return (x, y, instance_idx)
def constrain_special_ios(
canon_grid, vpr_grid, io_blocks, blif_data, blocks, place_constraints
):
"""
There are special IOs which need extra handling when dealing with placement constraints.
For instance, the IBUFDS_GTE2 primitive must be placed in correspondance with the location
of its input PADs, as no other route exists other than that.
This function reads the connectivity of the top level nets, which have been previously
constrained, and correctly constrains those blocks which require special handling.
"""
if "subckt" not in blif_data:
return
BEL_TYPES = ["IBUFDS_GTE2_VPR", "GTPE2_CHANNEL_VPR"]
SPECIAL_IPADS = ["IPAD_GTP_VPR"]
special_io_map = dict()
for subckt in blif_data["subckt"]:
if 'cname' not in subckt:
continue
bel = subckt['args'][0]
if bel not in SPECIAL_IPADS:
continue
top_net = None
renamed_net = None
for port in subckt['args'][1:]:
port_name, net = port.split("=")
if port_name == "I":
assert net in io_blocks, (net, io_blocks)
top_net = net
elif port_name == "O":
renamed_net = net
else:
assert False, "ERROR: Special IPAD ports not recognized!"
special_io_map[renamed_net] = top_net
blocks_to_constrain = set()
for subckt in blif_data["subckt"]:
if 'cname' not in subckt:
continue
bel = subckt['args'][0]
if bel not in BEL_TYPES:
continue
assert 'cname' in subckt and len(subckt['cname']) == 1, subckt
cname = subckt['cname'][0]
for port in subckt['args'][1:]:
_, net = port.split("=")
if net not in special_io_map:
continue
net = special_io_map[net]
if net in io_blocks:
x, y, z = io_blocks[net]
canon_loc = vpr_grid.get_canon_loc()[(x, y)]
gridinfo = canon_grid.gridinfo_at_loc(canon_loc)
sites = sorted(gridinfo.sites.keys())
site_name = sites[z]
connected_io_site = vpr_grid.get_site_dict(
)[site_name]['connected_to_site']
assert connected_io_site, (site_name, bel, cname)
new_z = sites.index(connected_io_site)
loc = (x, y, new_z)
blocks_to_constrain.add((cname, loc))
for block, vpr_loc in blocks_to_constrain:
place_constraints.constrain_block(
block, vpr_loc, "Constraining block {}".format(block)
)
blocks[block] = vpr_loc
def main():
parser = argparse.ArgumentParser(
description='Convert a PCF file into a VPR io.place file.'
)
parser.add_argument(
"--input",
'-i',
"-I",
type=argparse.FileType('r'),
default=sys.stdout,
help='The input constraints place file'
)
parser.add_argument(
"--output",
'-o',
"-O",
type=argparse.FileType('w'),
default=sys.stdout,
help='The output constraints place file'
)
parser.add_argument(
"--net",
'-n',
type=argparse.FileType('r'),
required=True,
help='top.net file'
)
parser.add_argument(
'--vpr_grid_map',
help='Map of canonical to VPR grid locations',
required=True
)
parser.add_argument('--arch', help='Arch XML', required=True)
parser.add_argument('--db_root', required=True)
parser.add_argument('--part', required=True)
parser.add_argument(
"--blif",
'-b',
type=argparse.FileType('r'),
required=True,
help='BLIF / eBLIF file'
)
parser.add_argument('--roi', action='store_true', help='Using ROI')
parser.add_argument(
"--allow-bufg-logic-sources",
action="store_true",
help="When set allows BUFGs to be driven by logic"
)
parser.add_argument('--graph_limit', help='Graph limit parameters')
args = parser.parse_args()
part = args.part
device_families = {
"xc7a": "artix7",
"xc7k": "kintex7",
"xc7z": "zynq7",
"xc7s": "spartan7",
}
device_family = None
for device in device_families:
if part.startswith(device):
device_family = device_families[device]
break
assert device_family
db_root = os.path.join(args.db_root, device_family)
db = prjxray.db.Database(db_root, args.part)
canon_grid = db.grid()
io_blocks = {}
loc_in_use = set()
for line in args.input:
args.output.write(line)
if line[0] == '#':
continue
block, x, y, z = line.strip().split()[0:4]
io_blocks[block] = (int(x), int(y), int(z))
loc_in_use.add(io_blocks[block])
place_constraints = vpr_place_constraints.PlaceConstraints(args.net)
place_constraints.load_loc_sites_from_net_file()
grid_capacities = get_tile_capacities(args.arch)
eblif_data = eblif.parse_blif(args.blif)
vpr_grid = VprGrid(args.vpr_grid_map, args.graph_limit)
# Constrain IO blocks and LOCed resources
blocks = {}
block_locs = {}
for block, loc in place_constraints.get_loc_sites():
vpr_loc = get_vpr_coords_from_site_name(
canon_grid, vpr_grid, loc, grid_capacities
)
loc_in_use.add(vpr_loc)
if block in io_blocks:
assert io_blocks[block] == vpr_loc, (
block, vpr_loc, io_blocks[block]
)
blocks[block] = vpr_loc
block_locs[block] = loc
place_constraints.constrain_block(
block, vpr_loc, "Constraining block {}".format(block)
)
# Constrain blocks directly connected to IO in the same x, y location
constrain_special_ios(
canon_grid, vpr_grid, io_blocks, eblif_data, blocks, place_constraints
)
# Constrain clock resources
clock_placer = ClockPlacer(
vpr_grid, io_blocks, eblif_data, args.roi, args.graph_limit,
args.allow_bufg_logic_sources
)
if clock_placer.has_clock_nets():
for block, loc in clock_placer.place_clocks(canon_grid, vpr_grid,
loc_in_use, block_locs,
blocks, grid_capacities):
vpr_loc = get_vpr_coords_from_site_name(
canon_grid, vpr_grid, loc, grid_capacities
)
place_constraints.constrain_block(
block, vpr_loc, "Constraining clock block {}".format(block)
)
""" Constrain IDELAYCTRL sites
Prior to the invocation of this script, the IDELAYCTRL sites must have been
replicated accordingly to the IDELAY specifications.
There can be three different usage combinations of IDELAYCTRL and IDELAYs in a design:
1. IODELAYs and IDELAYCTRLs can be constrained to banks as needed,
through an in-design LOC constraint.
Manual replication of the constrained IDELAYCTRLs is necessary to provide a
controller for each bank.
2. IODELAYs and a single IDELAYCTRL can be left entirely unconstrained,
becoming a default group. The IDELAYCTRLis replicated depending on bank usage.
Replication must have happened prior to this step
3. One or more IODELAY_GROUPs can be defined that contain IODELAYs and a single
IDELAYCTRL each. These components can be otherwise unconstrained and the IDELAYCTRL
for each group has to be replicated as needed (depending on bank usage).
NOTE: IODELAY_GROUPS are not enabled at the moment.
"""
idelayctrl_cmts = set()
idelay_instances = place_constraints.get_used_instances("IDELAYE2")
for inst in idelay_instances:
x, y, z = io_blocks[inst]
idelayctrl_cmt = vpr_grid.get_vpr_loc_cmt()[(x, y)]
idelayctrl_cmts.add(idelayctrl_cmt)
idelayctrl_instances = place_constraints.get_used_instances("IDELAYCTRL")
assert len(idelayctrl_cmts) <= len(
idelayctrl_instances
), "The number of IDELAYCTRL blocks and IO banks with IDELAYs used do not match."
idelayctrl_sites = dict()
for site_name, _, clk_region in vpr_grid.get_site_type_dict(
)['IDELAYCTRL']:
if clk_region in idelayctrl_cmts:
idelayctrl_sites[clk_region] = site_name
# Check and remove user constrained IDELAYCTRLs
for idelayctrl_block in idelayctrl_instances:
if idelayctrl_block in blocks.keys():
x, y, _ = blocks[idelayctrl_block]
idelayctrl_cmt = vpr_grid.get_vpr_loc_cmt()[(x, y)]
assert idelayctrl_cmt in idelayctrl_cmts
idelayctrl_cmts.remove(idelayctrl_cmt)
idelayctrl_instances.remove(idelayctrl_block)
# TODO: Add possibility to bind IDELAY banks to IDELAYCTRL sites using
# the IDELAY_GROUP attribute.
for cmt, idelayctrl_block in zip(idelayctrl_cmts, idelayctrl_instances):
x, y = vpr_grid.get_site_dict()[idelayctrl_sites[cmt]]['vpr_loc']
vpr_loc = (x, y, 0)
place_constraints.constrain_block(
idelayctrl_block, vpr_loc,
"Constraining idelayctrl block {}".format(idelayctrl_block)
)
if len(idelayctrl_instances) > 0:
print(
"Warning: IDELAY_GROUPS parameters are currently being ignored!",
file=sys.stderr
)
place_constraints.output_place_constraints(args.output)
if __name__ == '__main__':
main()