blob: 3a41f3e9cfb6aa393386d4f4171a8b489823bd6c [file] [log] [blame] [edit]
""" Generates the top level VPR arch XML from the Project X-Ray database.
By default this will generate a complete arch XML for all tile types specified.
If the --use_roi flag is passed, only the tiles within the ROI will be included,
and synthetic IO pads will be created and connected to the routing fabric.
The mapping of the pad name to synthetic tile location will be outputted to the
file specified in the --synth_tiles output argument. This can be used to generate
IO placement spefications to target the synthetic IO pads.
"""
from __future__ import print_function
import argparse
import prjxray.db
from prjxray.roi import Roi
from prjxray.overlay import Overlay
from prjxray import grid_types
import simplejson as json
import sys
import lxml.etree as ET
from prjxray_db_cache import DatabaseCache
from prjxray_tile_import import add_vpr_tile_prefix
def create_synth_io_tile(
complexblocklist_xml, tiles_xml, tile_name, num_input, num_output
):
""" Creates synthetic IO pad tiles used to connect ROI inputs and outputs to the routing network.
"""
tile_xml = ET.SubElement(tiles_xml, 'tile', {
'name': tile_name,
})
pad_name_out = 'outpad'
port_type_out = 'input'
pad_name_in = 'inpad'
port_type_in = 'output'
port_pin_in = '{}.{}'.format('SYN-INPAD', pad_name_in)
sub_port_pin_in = '{}.{}'.format('SYN_IN_SUB_TILE', pad_name_in)
port_pin_out = '{}.{}'.format('SYN-OUTPAD', pad_name_out)
sub_port_pin_out = '{}.{}'.format('SYN_OUT_SUB_TILE', pad_name_out)
if num_input != 0:
sub_tile_xml_in = ET.SubElement(
tile_xml, 'sub_tile', {
'name': 'SYN_IN_SUB_TILE',
'capacity': str(num_input)
}
)
ET.SubElement(
sub_tile_xml_in, 'fc', {
'in_type': 'abs',
'in_val': '2',
'out_type': 'abs',
'out_val': '2',
}
)
equivalent_sites_in = ET.SubElement(
sub_tile_xml_in, 'equivalent_sites'
)
site_in = ET.SubElement(
equivalent_sites_in, 'site', {'pb_type': 'SYN-INPAD'}
)
ET.SubElement(
sub_tile_xml_in, port_type_in, {
'name': pad_name_in,
'num_pins': '1',
}
)
ET.SubElement(
site_in, 'direct', {
'from': sub_port_pin_in,
'to': port_pin_in
}
)
if num_output != 0:
sub_tile_xml_out = ET.SubElement(
tile_xml, 'sub_tile', {
'name': 'SYN_OUT_SUB_TILE',
'capacity': str(num_output)
}
)
ET.SubElement(
sub_tile_xml_out, 'fc', {
'in_type': 'abs',
'in_val': '2',
'out_type': 'abs',
'out_val': '2',
}
)
equivalent_sites_out = ET.SubElement(
sub_tile_xml_out, 'equivalent_sites'
)
site_out = ET.SubElement(
equivalent_sites_out, 'site', {'pb_type': 'SYN-OUTPAD'}
)
ET.SubElement(
sub_tile_xml_out, port_type_out, {
'name': pad_name_out,
'num_pins': '1',
}
)
ET.SubElement(
site_out, 'direct', {
'from': sub_port_pin_out,
'to': port_pin_out
}
)
def create_synth_pb_types(model_xml, complexblocklist_xml, is_overlay=False):
""" Creates synthetic IO pad tiles used to connect ROI inputs and outputs to the routing network.
"""
pb_xml_in = ET.SubElement(
complexblocklist_xml, 'pb_type', {
'name': 'SYN-INPAD',
}
)
pb_xml_out = ET.SubElement(
complexblocklist_xml, 'pb_type', {
'name': 'SYN-OUTPAD',
}
)
blif_model_out = '.output'
pad_name_out = 'outpad'
port_type_out = 'input'
blif_model_in = '.input'
pad_name_in = 'inpad'
port_type_in = 'output'
port_pin_in = '{}.{}'.format('SYN-INPAD', pad_name_in)
pad_pin_in = '{}.{}'.format(pad_name_in, pad_name_in)
port_pin_out = '{}.{}'.format('SYN-OUTPAD', pad_name_out)
pad_pin_out = '{}.{}'.format(pad_name_out, pad_name_out)
input_name_out = port_pin_out
output_name_out = pad_pin_out
input_name_in = pad_pin_in
output_name_in = port_pin_in
interconnect_xml_in = ET.SubElement(pb_xml_in, 'interconnect')
interconnect_xml_out = ET.SubElement(pb_xml_out, 'interconnect')
ET.SubElement(
pb_xml_in, port_type_in, {
'name': pad_name_in,
'num_pins': '1',
}
)
ET.SubElement(
pb_xml_out, port_type_out, {
'name': pad_name_out,
'num_pins': '1',
}
)
if is_overlay:
# Add model for SYN_IBUF
ibuf_model = ET.SubElement(model_xml, 'model', {'name': 'SYN_IBUF'})
ibuf_input_ports = ET.SubElement(ibuf_model, 'input_ports', {})
ET.SubElement(
ibuf_input_ports, 'port', {
'name': 'I',
'combinational_sink_ports': 'O'
}
)
ibuf_output_ports = ET.SubElement(ibuf_model, 'output_ports', {})
ET.SubElement(ibuf_output_ports, 'port', {
'name': 'O',
})
# Add model for SYN_OBUF
obuf_model = ET.SubElement(model_xml, 'model', {'name': 'SYN_OBUF'})
obuf_input_ports = ET.SubElement(obuf_model, 'input_ports', {})
ET.SubElement(
obuf_input_ports, 'port', {
'name': 'I',
'combinational_sink_ports': 'O'
}
)
obuf_output_ports = ET.SubElement(obuf_model, 'output_ports', {})
ET.SubElement(obuf_output_ports, 'port', {
'name': 'O',
})
obuf_pb = ET.SubElement(
pb_xml_out, 'pb_type', {
'name': "SYN-OBUF",
'blif_model': '.subckt SYN_OBUF',
'num_pb': '1',
}
)
ET.SubElement(obuf_pb, 'input', {
'name': 'I',
'num_pins': '1',
})
ET.SubElement(obuf_pb, 'output', {
'name': 'O',
'num_pins': '1',
})
ET.SubElement(
obuf_pb, 'delay_constant', {
'max': '1e-11',
'in_port': 'I',
'out_port': 'O',
}
)
ibuf_pb = ET.SubElement(
pb_xml_in, 'pb_type', {
'name': "SYN-IBUF",
'blif_model': '.subckt SYN_IBUF',
'num_pb': '1',
}
)
ET.SubElement(ibuf_pb, 'input', {
'name': 'I',
'num_pins': '1',
})
ET.SubElement(ibuf_pb, 'output', {
'name': 'O',
'num_pins': '1',
})
ET.SubElement(
ibuf_pb, 'delay_constant', {
'max': '1e-11',
'in_port': 'I',
'out_port': 'O',
}
)
pin_pb_type_in = ET.SubElement(
pb_xml_in, 'pb_type', {
'name': pad_name_in,
'blif_model': blif_model_in,
'num_pb': '1',
}
)
pin_pb_type_out = ET.SubElement(
pb_xml_out, 'pb_type', {
'name': pad_name_out,
'blif_model': blif_model_out,
'num_pb': '1',
}
)
ET.SubElement(
pin_pb_type_in, port_type_in, {
'name': pad_name_in,
'num_pins': '1',
}
)
ET.SubElement(
pin_pb_type_out, port_type_out, {
'name': pad_name_out,
'num_pins': '1',
}
)
if is_overlay:
direct_xml_out = ET.SubElement(
interconnect_xml_out, 'direct', {
'name': '{}_to_{}'.format('SYN-OBUF.O', 'outpad.outpad'),
'input': 'SYN-OBUF.O',
'output': 'outpad.outpad',
}
)
ET.SubElement(
direct_xml_out, 'pack_pattern', {
'in_port': 'SYN-OBUF.O',
'name': '{}to{}'.format('OBUF', 'outpad'),
'out_port': 'outpad.outpad',
}
)
ET.SubElement(
interconnect_xml_out, 'direct', {
'name': '{}_to_{}'.format('SYN-OUTPAD.outpad', 'SYN-OBUF.I'),
'input': 'SYN-OUTPAD.outpad',
'output': 'SYN-OBUF.I',
}
)
direct_xml_in = ET.SubElement(
interconnect_xml_in, 'direct', {
'name': '{}_to_{}'.format('inpad.inpad', 'SYN-IBUF.I'),
'input': 'inpad.inpad',
'output': 'SYN-IBUF.I',
}
)
ET.SubElement(
direct_xml_in, 'pack_pattern', {
'in_port': 'inpad.inpad',
'name': '{}to{}'.format('inpad', 'IBUF'),
'out_port': 'SYN-IBUF.I',
}
)
ET.SubElement(
interconnect_xml_in, 'direct', {
'name': '{}_to_{}'.format('SYN-IBUF.O', 'SYN-INPAD.inpad'),
'input': 'SYN-IBUF.O',
'output': 'SYN-INPAD.inpad',
}
)
else:
direct_xml_out = ET.SubElement(
interconnect_xml_out, 'direct', {
'name': '{}_to_{}'.format(input_name_out, output_name_out),
'input': input_name_out,
'output': output_name_out,
}
)
ET.SubElement(
direct_xml_out, 'delay_constant', {
'max': '1e-11',
'in_port': input_name_out,
'out_port': output_name_out,
}
)
direct_xml_in = ET.SubElement(
interconnect_xml_in, 'direct', {
'name': '{}_to_{}'.format(input_name_in, output_name_in),
'input': input_name_in,
'output': output_name_in,
}
)
ET.SubElement(
direct_xml_in, 'delay_constant', {
'max': '1e-11',
'in_port': input_name_in,
'out_port': output_name_in,
}
)
def create_synth_constant_tiles(
model_xml, complexblocklist_xml, tiles_xml, pb_name, signal
):
""" Creates synthetic constant tile generates some constant signal.
Routing import will create a global network to fan this signal to local
constant sources.
"""
pb_xml = ET.SubElement(
complexblocklist_xml, 'pb_type', {
'name': pb_name,
}
)
tile_xml = ET.SubElement(tiles_xml, 'tile', {
'name': pb_name,
})
sub_tile_xml = ET.SubElement(tile_xml, 'sub_tile', {'name': pb_name})
equivalent_sites = ET.SubElement(sub_tile_xml, 'equivalent_sites')
site = ET.SubElement(equivalent_sites, 'site', {'pb_type': pb_name})
ET.SubElement(
sub_tile_xml, 'fc', {
'in_type': 'abs',
'in_val': '2',
'out_type': 'abs',
'out_val': '2',
}
)
interconnect_xml = ET.SubElement(pb_xml, 'interconnect')
blif_model = '.subckt ' + signal
port_type = 'output'
pin_name = signal
ET.SubElement(pb_xml, port_type, {
'name': pin_name,
'num_pins': '1',
})
ET.SubElement(
sub_tile_xml, port_type, {
'name': pin_name,
'num_pins': '1',
}
)
port_pin = '{}.{}'.format(pb_name, pin_name)
pad_pin = '{}.{}'.format(pin_name, pin_name)
ET.SubElement(site, 'direct', {'from': port_pin, 'to': port_pin})
input_name = pad_pin
output_name = port_pin
pin_pb_type = ET.SubElement(
pb_xml, 'pb_type', {
'name': pin_name,
'blif_model': blif_model,
'num_pb': '1',
}
)
ET.SubElement(
pin_pb_type, port_type, {
'name': pin_name,
'num_pins': '1',
}
)
direct_xml = ET.SubElement(
interconnect_xml, 'direct', {
'name': '{}_to_{}'.format(input_name, output_name),
'input': input_name,
'output': output_name,
}
)
ET.SubElement(
direct_xml, 'delay_constant', {
'max': '1e-11',
'in_port': input_name,
'out_port': output_name,
}
)
model = ET.SubElement(model_xml, 'model', {
'name': signal,
})
ET.SubElement(model, 'input_ports')
output_ports = ET.SubElement(model, 'output_ports')
ET.SubElement(output_ports, 'port', {
'name': pin_name,
})
def get_phy_tiles(conn, tile_pkey):
""" Returns the locations of all physical tiles for specified tile. """
c = conn.cursor()
c2 = conn.cursor()
phy_locs = []
for (phy_tile_pkey, ) in c.execute("""
SELECT phy_tile_pkey FROM tile_map WHERE tile_pkey = ?""", (tile_pkey, )):
c2.execute(
"SELECT grid_x, grid_y FROM phy_tile WHERE pkey = ?",
(phy_tile_pkey, )
)
loc = c2.fetchone()
phy_locs.append(grid_types.GridLoc(*loc))
return phy_locs
def is_in_roi(conn, roi, tile_pkey):
""" Returns if the specified tile is in the ROI. """
phy_locs = get_phy_tiles(conn, tile_pkey)
return any(roi.tile_in_roi(loc) for loc in phy_locs)
# Map instance type (e.g. IOB_X1Y10) to:
# - Which coordinates are required (e.g. X, Y or X and Y)
# - Modulus on the coordinates
#
# For example, IO sites only need the Y coordinate, use a modulus of 2.
# So IOB_X1Y10 becomes IOB_Y0, IOB_X1Y11 becomes IOB_Y1, etc.
# Setting modulo to 0 results in omitting modulo operation.
#
# Sites which do not require prefix will contain a (None, None) pair.
PREFIX_REQUIRED = {
"IOB": ("Y", 2),
"IDELAY": ("Y", 2),
"ILOGIC": ("Y", 2),
"OLOGIC": ("Y", 2),
"BUFGCTRL": ("XY", (2, 16)),
"SLICEM": ("X", 2),
"SLICEL": ("X", 2),
"GTPE2_COMMON": (None, None),
"GTPE2_CHANNEL": (None, None),
"IBUFDS_GTE2": ("Y", 2),
"IPAD": (None, None),
"OPAD": (None, None),
}
def make_prefix(site, x, y, from_site_name=False):
""" Make tile FASM prefix for a given site. """
if from_site_name:
site_type, _ = site.split('_')
else:
site_type = site
prefix_required = PREFIX_REQUIRED[site_type]
if prefix_required[0] == 'Y':
return site_type, '{}_Y{}'.format(site_type, y % prefix_required[1])
elif prefix_required[0] == 'X':
return site_type, '{}_X{}'.format(site_type, x % prefix_required[1])
elif prefix_required[0] == 'XY':
mod_x, mod_y = prefix_required[1]
return site_type, '{}_X{}Y{}'.format(site_type, x % mod_x, y % mod_y)
elif prefix_required[0] is None:
return site_type, None
else:
assert False, (site_type, prefix_required)
def get_site_prefixes(conn, tile_pkey):
cur = conn.cursor()
site_prefixes = {}
cur.execute(
"""
WITH
sites_in_tile(site_pkey) AS (
SELECT
DISTINCT wire_in_tile.site_pkey
FROM
wire_in_tile
INNER JOIN site ON site.pkey = wire_in_tile.site_pkey
WHERE
wire_in_tile.tile_type_pkey = (SELECT tile_type_pkey FROM tile WHERE pkey = ?)
AND
wire_in_tile.phy_tile_type_pkey IN (SELECT tile_type_pkey FROM phy_tile WHERE pkey IN
(SELECT phy_tile_pkey FROM tile_map WHERE tile_pkey = ?))
AND
site_pin_pkey IS NOT NULL)
SELECT site_instance.name, site_instance.x_coord, site_instance.y_coord
FROM site_instance
WHERE
site_instance.site_pkey IN (SELECT site_pkey FROM sites_in_tile)
AND
site_instance.phy_tile_pkey IN (SELECT phy_tile_pkey FROM tile_map WHERE tile_pkey = ?);
""", (tile_pkey, tile_pkey, tile_pkey)
)
for site_name, x, y in cur:
site_type, prefix = make_prefix(site_name, x, y, from_site_name=True)
assert site_type not in site_prefixes
site_prefixes[site_type] = prefix
return site_prefixes
def create_capacity_prefix(c, tile_prefix, tile_pkey, tile_capacity):
""" Create FASM prefixes for all sites located within specified tile.
This function should be invoke when the relevant tile has capacity > 1
and therefore there should be prefixes for each instance.
"""
c.execute(
"""
SELECT site_type.name, site_instance.name, site_instance.x_coord, site_instance.y_coord
FROM site_instance
INNER JOIN site ON site_instance.site_pkey = site.pkey
INNER JOIN site_type ON site.site_type_pkey = site_type.pkey
WHERE site_instance.phy_tile_pkey IN (
SELECT
phy_tile_pkey
FROM
tile
WHERE
pkey = ?
)
ORDER BY site_instance.x_coord, site_instance.y_coord, site_type.name;""",
(tile_pkey, )
)
NO_SITE_TYPE_PREFIX = ["SLICEL", "SLICEM", "IBUFDS_GTE2"]
prefixes = []
for site_type, site_name, x, y in sorted(
c, key=lambda site: (site[0], int(site[2]), int(site[3]))):
_, prefix = make_prefix(site_type, x, y)
if prefix is None:
prefixes.append('{}.{}'.format(tile_prefix, site_type))
else:
if site_type in NO_SITE_TYPE_PREFIX:
prefixes.append('{}.{}'.format(tile_prefix, prefix))
else:
prefixes.append(
'{}.{}.{}'.format(tile_prefix, site_type, prefix)
)
assert len(prefixes
) == tile_capacity, (tile_pkey, tile_capacity, len(prefixes))
return " ".join(prefixes)
def get_fasm_tile_prefix(conn, g, tile_pkey, site_as_tile_pkey, tile_capacity):
""" Returns FASM prefix of specified tile. """
c = conn.cursor()
c.execute(
"""
SELECT
phy_tile.name,
tile_type.name
FROM phy_tile
INNER JOIN tile_type
ON phy_tile.tile_type_pkey = tile_type.pkey
WHERE
phy_tile.pkey IN (SELECT phy_tile_pkey FROM tile_map WHERE tile_pkey = ?);
""", (tile_pkey, )
)
# If this tile has multiples phy_tile's, make sure only one has bitstream
# data, otherwise the tile split was invalid.
tile_type_map = {}
for tilename, tile_type in c:
gridinfo = g.gridinfo_at_tilename(tilename)
is_vbrk = gridinfo.tile_type.find('VBRK') != -1
is_pss = gridinfo.tile_type.startswith('PSS')
# VBRK and PSS* tiles are known to have no bitstream data.
if not is_vbrk and not is_pss and not gridinfo.bits:
print(
'*** WARNING *** Tile {} appears to be missing bitstream data.'
.format(tilename),
file=sys.stderr
)
if gridinfo.bits:
# Each VPR tile can only have one prefix.
# If this assumption is violated, a more dramatic
# restructing is required.
tile_type_map[tile_type] = tilename
if len(tile_type_map) > 1:
assert tile_capacity == 1
tile_type_map.update(get_site_prefixes(conn, tile_pkey))
return lambda single_xml: attach_multiple_prefixes_to_tile(
single_xml, tile_type_map
)
else:
assert len(tile_type_map) == 1, tile_pkey
tile_prefix = list(tile_type_map.values())[0]
# If this tile is site_as_tile, add an additional prefix of the site
# that is embedded in the tile.
if site_as_tile_pkey is not None:
assert tile_capacity == 1
c.execute(
"SELECT site_pkey FROM site_as_tile WHERE pkey = ?",
(site_as_tile_pkey, )
)
site_pkey = c.fetchone()[0]
c.execute(
"""
SELECT site_type_pkey, x_coord FROM site WHERE pkey = ?
""", (site_pkey, )
)
site_type_pkey, x = c.fetchone()
c.execute(
"SELECT name FROM site_type WHERE pkey = ?",
(site_type_pkey, )
)
site_type_name = c.fetchone()[0]
tile_prefix = '{}.{}_X{}'.format(tile_prefix, site_type_name, x)
if tile_capacity > 1:
tile_prefix = create_capacity_prefix(
c, tile_prefix, tile_pkey, tile_capacity
)
return lambda single_xml: attach_prefix_to_tile(
single_xml, tile_prefix
)
def attach_prefix_to_tile(single_xml, fasm_tile_prefix):
meta = ET.SubElement(single_xml, 'metadata')
ET.SubElement(
meta, 'meta', {
'name': 'fasm_prefix',
}
).text = fasm_tile_prefix
# Map the following tile types to a more general name
TYPE_REMAP = {
"LIOI3_SING": "IOI3_TILE",
"LIOI3": "IOI3_TILE",
"LIOI3_TBYTESRC": "IOI3_TILE",
"LIOI3_TBYTETERM": "IOI3_TILE",
"RIOI3_SING": "IOI3_TILE",
"RIOI3": "IOI3_TILE",
"RIOI3_TBYTESRC": "IOI3_TILE",
"RIOI3_TBYTETERM": "IOI3_TILE",
"LIOB33": "IOB_TILE",
"LIOB33_SING": "IOB_TILE",
"RIOB33": "IOB_TILE",
"RIOB33_SING": "IOB_TILE",
}
def attach_multiple_prefixes_to_tile(single_xml, tile_type_map):
meta = ET.SubElement(single_xml, 'metadata')
ET.SubElement(meta, 'meta', {
'name': 'fasm_placeholders',
}).text = '\n' + '\n'.join(
'{} : {}'.format(TYPE_REMAP.get(k, k), v)
for k, v in tile_type_map.items()
) + '\n'
def get_tiles(
conn, g, roi, synth_loc_map, synth_tile_map, tile_types, tile_capacity
):
""" Yields tiles in grid.
Yields
------
vpr_tile_type : str
VPR tile type at this grid location.
grid_x, grid_y : int
Grid coordinate of tile
metadata_function : function that takes lxml.Element
Function for attaching metadata tags to <single> elements.
Function must be supplied, but doesn't need to add metadata if not
required.
"""
c = conn.cursor()
c2 = conn.cursor()
only_emit_roi = roi is not None
for tile_pkey, grid_x, grid_y, phy_tile_pkey, tile_type_pkey, site_as_tile_pkey in c.execute(
"""
SELECT pkey, grid_x, grid_y, phy_tile_pkey, tile_type_pkey, site_as_tile_pkey FROM tile
"""):
if phy_tile_pkey is not None:
c2.execute(
"SELECT prohibited FROM site_instance WHERE phy_tile_pkey = ?",
(phy_tile_pkey, )
)
any_prohibited_sites = any(
prohibited for (prohibited, ) in c2.fetchall()
)
# Skip generation of tiles containing prohibited sites
if any_prohibited_sites:
continue
# Just output synth tiles, no additional processing is required here.
if (grid_x, grid_y) in synth_loc_map:
vpr_tile_type = synth_loc_map[(grid_x, grid_y)]
yield vpr_tile_type, grid_x, grid_y, lambda x: None
continue
c2.execute(
"SELECT name FROM tile_type WHERE pkey = ?", (tile_type_pkey, )
)
tile_type = c2.fetchone()[0]
if tile_type not in tile_types:
# We don't want this tile
continue
if only_emit_roi and not is_in_roi(conn, roi, tile_pkey):
# Tile is outside ROI, skip it
continue
vpr_tile_type = add_vpr_tile_prefix(tile_type)
# For Zynq PSS* tiles do not emit fasm prefixes
if tile_type.startswith('PSS'):
def get_none_tile_prefix(single_xml):
return None
meta_fun = get_none_tile_prefix
else:
meta_fun = get_fasm_tile_prefix(
conn, g, tile_pkey, site_as_tile_pkey, tile_capacity[tile_type]
)
yield vpr_tile_type, grid_x, grid_y, meta_fun
def add_constant_synthetic_tiles(model_xml, complexblocklist_xml, tiles_xml):
synth_tile_types = {}
create_synth_constant_tiles(
model_xml, complexblocklist_xml, tiles_xml, 'SYN-VCC', 'VCC'
)
create_synth_constant_tiles(
model_xml, complexblocklist_xml, tiles_xml, 'SYN-GND', 'GND'
)
synth_tile_types['VCC'] = 'SYN-VCC'
synth_tile_types['GND'] = 'SYN-GND'
return synth_tile_types
def add_direct(directlist_xml, direct):
direct_dict = {
'name':
'{}_to_{}_dx_{}_dy_{}_dz_{}'.format(
direct['from_pin'], direct['to_pin'], direct['x_offset'],
direct['y_offset'], direct['z_offset']
),
'from_pin':
add_vpr_tile_prefix(direct['from_pin']),
'to_pin':
add_vpr_tile_prefix(direct['to_pin']),
'x_offset':
str(direct['x_offset']),
'y_offset':
str(direct['y_offset']),
'z_offset':
str(direct['z_offset']),
}
# If the switch is a delayless_switch, the switch name
# needs to be avoided as VPR automatically assigns
# the delayless switch to this direct connection
if direct['switch_name'] != '__vpr_delayless_switch__':
direct_dict['switch_name'] = direct['switch_name']
ET.SubElement(directlist_xml, 'direct', direct_dict)
def insert_constant_tiles(conn, model_xml, complexblocklist_xml, tiles_xml):
c = conn.cursor()
# Always add 'GND' and 'VCC' synth tiles
synth_tile_map = add_constant_synthetic_tiles(
model_xml, complexblocklist_xml, tiles_xml
)
synth_loc_map = {}
c.execute('SELECT pkey FROM tile_type WHERE name = "NULL";')
null_tile_type_pkey = c.fetchone()[0]
# Get all 'NULL' tile locations
c.execute(
"""
SELECT grid_x, grid_y
FROM phy_tile
WHERE tile_type_pkey = ?
ORDER BY grid_y, grid_x ASC
LIMIT 2
""", (null_tile_type_pkey, )
)
locs = list(c.fetchall())
assert len(locs) >= 2
loc = {'VCC': locs[0], 'GND': locs[1]}
c.execute(
"""
SELECT pkey, tile_type_pkey FROM phy_tile
WHERE grid_x = ? AND grid_y = ?""", loc['VCC']
)
vcc_phy_tile_pkey, vcc_tile_type_pkey = c.fetchone()
assert vcc_tile_type_pkey == null_tile_type_pkey, vcc_tile_type_pkey
c.execute(
"""
SELECT pkey, grid_x, grid_y FROM tile WHERE phy_tile_pkey = ?
""", (vcc_phy_tile_pkey, )
)
results = c.fetchall()
assert len(results) == 1, results
_, vcc_grid_x, vcc_grid_y = results[0]
synth_loc_map[(vcc_grid_x, vcc_grid_y)] = synth_tile_map['VCC']
c.execute(
"""
SELECT pkey, tile_type_pkey FROM phy_tile
WHERE grid_x = ? AND grid_y = ?""", loc['GND']
)
gnd_phy_tile_pkey, gnd_tile_type_pkey = c.fetchone()
assert gnd_tile_type_pkey == null_tile_type_pkey
c.execute(
"""
SELECT pkey, grid_x, grid_y FROM tile WHERE phy_tile_pkey = ?
""", (gnd_phy_tile_pkey, )
)
results = c.fetchall()
assert len(results) == 1, results
_, gnd_grid_x, gnd_grid_y = results[0]
synth_loc_map[(gnd_grid_x, gnd_grid_y)] = synth_tile_map['GND']
return synth_tile_map, synth_loc_map
def main():
parser = argparse.ArgumentParser(description="Generate arch.xml")
parser.add_argument(
'--db_root', required=True, help="Project X-Ray database to use."
)
parser.add_argument('--part', required=True, help="FPGA part")
parser.add_argument(
'--output-arch',
nargs='?',
type=argparse.FileType('w'),
help="""File to output arch."""
)
parser.add_argument(
'--tile-types', required=True, help="Semi-colon seperated tile types."
)
parser.add_argument(
'--pb_types',
required=True,
help="Semi-colon seperated pb_types types."
)
parser.add_argument(
'--pin_assignments', required=True, type=argparse.FileType('r')
)
parser.add_argument('--use_roi', required=False)
parser.add_argument('--use_overlay', required=False)
parser.add_argument('--device', required=True)
parser.add_argument('--synth_tiles', required=False)
parser.add_argument('--connection_database', required=True)
parser.add_argument(
'--graph_limit',
help='Limit grid to specified dimensions in x_min,y_min,x_max,y_max',
)
args = parser.parse_args()
tile_types = args.tile_types.split(',')
pb_types = args.pb_types.split(',')
model_xml_spec = "../../tiles/{0}/{0}.model.xml"
pbtype_xml_spec = "../../tiles/{0}/{0}.pb_type.xml"
tile_xml_spec = "../../tiles/{0}/{0}.tile.xml"
xi_url = "http://www.w3.org/2001/XInclude"
ET.register_namespace('xi', xi_url)
xi_include = "{%s}include" % xi_url
arch_xml = ET.Element(
'architecture',
{},
nsmap={'xi': xi_url},
)
model_xml = ET.SubElement(arch_xml, 'models')
for pb_type in pb_types:
ET.SubElement(
model_xml, xi_include, {
'href': model_xml_spec.format(pb_type.lower()),
'xpointer': "xpointer(models/child::node())",
}
)
tiles_xml = ET.SubElement(arch_xml, 'tiles')
tile_capacity = {}
for tile_type in tile_types:
uri = tile_xml_spec.format(tile_type.lower())
ET.SubElement(tiles_xml, xi_include, {
'href': uri,
})
with open(uri) as f:
tile_xml = ET.parse(f, ET.XMLParser())
tile_root = tile_xml.getroot()
assert tile_root.tag == 'tile'
tile_capacity[tile_type] = 0
for sub_tile in tile_root.iter('sub_tile'):
if 'capacity' in sub_tile.attrib:
tile_capacity[tile_type] += int(
sub_tile.attrib['capacity']
)
else:
tile_capacity[tile_type] += 1
complexblocklist_xml = ET.SubElement(arch_xml, 'complexblocklist')
for pb_type in pb_types:
ET.SubElement(
complexblocklist_xml, xi_include, {
'href': pbtype_xml_spec.format(pb_type.lower()),
}
)
layout_xml = ET.SubElement(arch_xml, 'layout')
db = prjxray.db.Database(args.db_root, args.part)
g = db.grid()
synth_tiles = {}
synth_tiles['tiles'] = {}
synth_loc_map = {}
synth_tile_map = {}
roi = None
if args.use_roi:
with open(args.use_roi) as f:
j = json.load(f)
with open(args.synth_tiles) as f:
synth_tiles = json.load(f)
roi = Roi(
db=db,
x1=j['info']['GRID_X_MIN'],
y1=j['info']['GRID_Y_MIN'],
x2=j['info']['GRID_X_MAX'],
y2=j['info']['GRID_Y_MAX'],
)
for _, tile_info in synth_tiles['tiles'].items():
if tile_info['pins'][0]['port_type'] in ['GND', 'VCC']:
continue
assert tuple(tile_info['loc']) not in synth_loc_map
tile_name = tile_info['tile_name']
num_input = len(
list(
filter(
lambda t: t['port_type'] == 'output', tile_info['pins']
)
)
)
num_output = len(
list(
filter(
lambda t: t['port_type'] == 'input', tile_info['pins']
)
)
)
create_synth_io_tile(
complexblocklist_xml, tiles_xml, tile_name, num_input,
num_output
)
synth_loc_map[tuple(tile_info['loc'])] = tile_name
create_synth_pb_types(model_xml, complexblocklist_xml)
synth_tile_map = add_constant_synthetic_tiles(
model_xml, complexblocklist_xml, tiles_xml
)
for _, tile_info in synth_tiles['tiles'].items():
if tile_info['pins'][0]['port_type'] not in ['GND', 'VCC']:
continue
assert tuple(tile_info['loc']) not in synth_loc_map
vpr_tile_type = synth_tile_map[tile_info['pins'][0]['port_type']]
synth_loc_map[tuple(tile_info['loc'])] = vpr_tile_type
elif args.graph_limit:
x_min, y_min, x_max, y_max = map(int, args.graph_limit.split(','))
roi = Roi(
db=db,
x1=x_min,
y1=y_min,
x2=x_max,
y2=y_max,
)
elif args.use_overlay:
with open(args.use_overlay) as f:
j = json.load(f)
with open(args.synth_tiles) as f:
synth_tiles = json.load(f)
region_dict = dict()
for r in synth_tiles['info']:
bounds = (
r['GRID_X_MIN'], r['GRID_X_MAX'], r['GRID_Y_MIN'],
r['GRID_Y_MAX']
)
region_dict[r['name']] = bounds
roi = Overlay(region_dict=region_dict)
for _, tile_info in synth_tiles['tiles'].items():
if tile_info['pins'][0]['port_type'] in ['GND', 'VCC']:
continue
assert tuple(tile_info['loc']) not in synth_loc_map
tile_name = tile_info['tile_name']
num_input = len(
list(
filter(
lambda t: t['port_type'] == 'output', tile_info['pins']
)
)
)
num_output = len(
list(
filter(
lambda t: t['port_type'] == 'input', tile_info['pins']
)
)
)
create_synth_io_tile(
complexblocklist_xml, tiles_xml, tile_name, num_input,
num_output
)
synth_loc_map[tuple(tile_info['loc'])] = tile_name
create_synth_pb_types(model_xml, complexblocklist_xml, True)
with DatabaseCache(args.connection_database, read_only=True) as conn:
c = conn.cursor()
if 'GND' not in synth_tile_map:
synth_tile_map, synth_loc_map_const = insert_constant_tiles(
conn, model_xml, complexblocklist_xml, tiles_xml
)
synth_loc_map.update(synth_loc_map_const)
# Find the grid extent.
y_max = 0
x_max = 0
for grid_x, grid_y in c.execute("SELECT grid_x, grid_y FROM tile"):
x_max = max(grid_x + 2, x_max)
y_max = max(grid_y + 2, y_max)
name = '{}-test'.format(args.device)
fixed_layout_xml = ET.SubElement(
layout_xml, 'fixed_layout', {
'name': name,
'height': str(y_max),
'width': str(x_max),
}
)
for vpr_tile_type, grid_x, grid_y, metadata_function in get_tiles(
conn=conn,
g=g,
roi=roi,
synth_loc_map=synth_loc_map,
synth_tile_map=synth_tile_map,
tile_types=tile_types,
tile_capacity=tile_capacity,
):
single_xml = ET.SubElement(
fixed_layout_xml, 'single', {
'priority': '1',
'type': vpr_tile_type,
'x': str(grid_x),
'y': str(grid_y),
}
)
metadata_function(single_xml)
switchlist_xml = ET.SubElement(arch_xml, 'switchlist')
for name, internal_capacitance, drive_resistance, intrinsic_delay, \
switch_type in c.execute("""
SELECT
name,
internal_capacitance,
drive_resistance,
intrinsic_delay,
switch_type
FROM
switch
WHERE
name != "__vpr_delayless_switch__";"""):
attrib = {
'type': switch_type,
'name': name,
"R": str(drive_resistance),
"Cin": str(0),
"Cout": str(0),
"Tdel": str(intrinsic_delay),
}
if internal_capacitance != 0:
attrib["Cinternal"] = str(internal_capacitance)
if False:
attrib["mux_trans_size"] = str(0)
attrib["buf_size"] = str(0)
ET.SubElement(switchlist_xml, 'switch', attrib)
segmentlist_xml = ET.SubElement(arch_xml, 'segmentlist')
# VPR requires a segment, so add one.
dummy_xml = ET.SubElement(
segmentlist_xml, 'segment', {
'name': 'dummy',
'length': '2',
'freq': '1.0',
'type': 'bidir',
'Rmetal': '0',
'Cmetal': '0',
}
)
ET.SubElement(dummy_xml, 'wire_switch', {
'name': 'buffer',
})
ET.SubElement(dummy_xml, 'opin_switch', {
'name': 'buffer',
})
ET.SubElement(dummy_xml, 'sb', {
'type': 'pattern',
}).text = ' '.join('1' for _ in range(3))
ET.SubElement(dummy_xml, 'cb', {
'type': 'pattern',
}).text = ' '.join('1' for _ in range(2))
for (name, length) in c.execute("SELECT name, length FROM segment"):
if length is None:
length = 1
segment_xml = ET.SubElement(
segmentlist_xml, 'segment', {
'name': name,
'length': str(length),
'freq': '1.0',
'type': 'bidir',
'Rmetal': '0',
'Cmetal': '0',
}
)
ET.SubElement(segment_xml, 'wire_switch', {
'name': 'buffer',
})
ET.SubElement(segment_xml, 'opin_switch', {
'name': 'buffer',
})
ET.SubElement(segment_xml, 'sb', {
'type': 'pattern',
}).text = ' '.join('1' for _ in range(length + 1))
ET.SubElement(segment_xml, 'cb', {
'type': 'pattern',
}).text = ' '.join('1' for _ in range(length))
ET.SubElement(
switchlist_xml,
'switch',
{
'type': 'mux',
'name': 'buffer',
"R": "551",
"Cin": ".77e-15",
"Cout": "4e-15",
# TODO: This value should be the "typical" pip switch delay from
# This value is the dominate term in the inter-cluster delay
# estimate.
"Tdel": "0.178e-9",
"mux_trans_size": "2.630740",
"buf_size": "27.645901"
}
)
device_xml = ET.SubElement(arch_xml, 'device')
ET.SubElement(
device_xml, 'sizing', {
"R_minW_nmos": "6065.520020",
"R_minW_pmos": "18138.500000",
}
)
ET.SubElement(device_xml, 'area', {
"grid_logic_tile_area": "14813.392",
})
ET.SubElement(
device_xml, 'connection_block', {
"input_switch_name": "buffer",
}
)
ET.SubElement(device_xml, 'switch_block', {
"type": "wilton",
"fs": "3",
})
chan_width_distr_xml = ET.SubElement(device_xml, 'chan_width_distr')
ET.SubElement(
chan_width_distr_xml, 'x', {
'distr': 'uniform',
'peak': '1.0',
}
)
ET.SubElement(
chan_width_distr_xml, 'y', {
'distr': 'uniform',
'peak': '1.0',
}
)
directlist_xml = ET.SubElement(arch_xml, 'directlist')
pin_assignments = json.load(args.pin_assignments)
# Choose smallest distance for block to block connections with multiple
# direct_connections. VPR cannot handle multiple block to block connections.
directs = {}
for direct in pin_assignments['direct_connections']:
key = (direct['from_pin'], direct['to_pin'])
if key not in directs:
directs[key] = []
directs[key].append(
(abs(direct['x_offset']) + abs(direct['y_offset']), direct)
)
ALLOWED_ZERO_OFFSET_DIRECT = [
"GTP_CHANNEL_0",
"GTP_CHANNEL_1",
"GTP_CHANNEL_2",
"GTP_CHANNEL_3",
"GTP_CHANNEL_0_MID_LEFT",
"GTP_CHANNEL_1_MID_LEFT",
"GTP_CHANNEL_2_MID_LEFT",
"GTP_CHANNEL_3_MID_LEFT",
"GTP_CHANNEL_0_MID_RIGHT",
"GTP_CHANNEL_1_MID_RIGHT",
"GTP_CHANNEL_2_MID_RIGHT",
"GTP_CHANNEL_3_MID_RIGHT",
"GTP_COMMON_MID_LEFT",
"GTP_COMMON_MID_RIGHT",
]
zero_offset_directs = dict()
for direct in directs.values():
_, direct = min(direct, key=lambda v: v[0])
from_tile = direct['from_pin'].split('.')[0]
to_tile = direct['to_pin'].split('.')[0]
if from_tile not in tile_types:
continue
if to_tile not in tile_types:
continue
# In general, the Z offset is 0, except for special cases
# such as for the GTP tiles, where there are direct connections
# within the same (x, y) cooredinates, but between different sub_tiles
direct['z_offset'] = 0
if direct['x_offset'] == 0 and direct['y_offset'] == 0:
if from_tile == to_tile and from_tile in ALLOWED_ZERO_OFFSET_DIRECT:
if from_tile not in zero_offset_directs:
zero_offset_directs[from_tile] = list()
zero_offset_directs[from_tile].append(direct)
continue
add_direct(directlist_xml, direct)
for tile, directs in zero_offset_directs.items():
uri = tile_xml_spec.format(tile.lower())
ports = list()
with open(uri) as f:
tile_xml = ET.parse(f, ET.XMLParser())
tile_root = tile_xml.getroot()
for capacity, sub_tile in enumerate(tile_root.iter('sub_tile')):
for in_port in sub_tile.iter('input'):
ports.append((in_port.attrib["name"], capacity))
for out_port in sub_tile.iter('output'):
ports.append((out_port.attrib["name"], capacity))
for clk_port in sub_tile.iter('clock'):
ports.append((clk_port.attrib["name"], capacity))
for direct in directs:
tile_type, from_port = direct['from_pin'].split('.')
_, to_port = direct['to_pin'].split('.')
if tile != tile_type:
continue
from_port_capacity = None
to_port_capacity = None
for port, capacity in ports:
if port == from_port:
from_port_capacity = capacity
if port == to_port:
to_port_capacity = capacity
assert from_port_capacity is not None and to_port_capacity is not None, (
tile, from_port, to_port
)
direct["z_offset"] = to_port_capacity - from_port_capacity
add_direct(directlist_xml, direct)
arch_xml_str = ET.tostring(arch_xml, pretty_print=True).decode('utf-8')
args.output_arch.write(arch_xml_str)
args.output_arch.close()
if __name__ == '__main__':
main()