blob: 1228497e52cd455714640b1b3757fafe2dcb0d3b [file] [log] [blame] [edit]
""" Create segment definitions for prjxray interconnect.
"""
import argparse
from prjxray.db import Database
import re
from collections import OrderedDict
def add_segment_wires(db, tile, wires, segments):
""" Adds to the set segment wires. """
tile = db.get_tile_type(tile)
for pip in tile.get_pips():
# Ignore wires that sink to a site
if 'GCLK' in pip.net_to:
segments['CLKFEED'].add(pip.net_to)
segments['HCLK_COLUMNS'].add(pip.net_from)
continue
elif 'IMUX' in pip.net_to or \
'CTRL' in pip.net_to or \
'CLK' in pip.net_to or \
re.match('BYP[0-7]', pip.net_to) or \
re.match('FAN[0-7]', pip.net_to):
segments['INPINFEED'].add(pip.net_to)
continue
wires.add(pip.net_to)
if not pip.is_directional:
wires.add(pip.net_from)
def reduce_wires_to_segments(wires, segments):
""" Reduce wire names to segment definitions.
For purposes of creating the routing heuristic, it is assumed that if two
source wires share a prefix, they can be considered segments for the
purposes of the routing heuristic.
This is definitely true for wires like SR1BEG1 or LV18.
This may apply to the local fanout wires like GFAN0 or FAN_BOUNCE0.
"""
WIRE_PARTS = re.compile('^(.*?)([0-9]+)$')
for wire in wires:
m = WIRE_PARTS.match(wire)
assert m is not None
segment = m.group(1)
if segment not in segments:
segments[segment] = set()
segments[segment].add(wire)
def get_segments(db):
""" Return segment approximation for device.
Returns
-------
segments : dict of str to list of str
Each key is a segment, with the elements of the values as the wires
that belong to that segment type.
"""
wires = set()
segments = OrderedDict()
for segment in [
'INPINFEED',
'CLKFEED',
'OUTPINFEED',
'BRAM_CASCADE',
'BUFG_CASCADE',
'GCLK',
'GCLK_OUTPINFEED',
'GCLK_INPINFEED',
'HCLK_CK_IN',
'BRAM_IMUX',
'HCLK_COLUMNS',
'HCLK_ROWS',
'HCLK_ROW_TO_COLUMN',
'CCIO_OUTPINFEED',
'CCIO_CLK_IN',
'PLL_OUTPINFEED',
'PLL_INPINFEED',
]:
segments[segment] = set()
for tile in ['INT_L', 'INT_R']:
add_segment_wires(db, tile, wires, segments)
reduce_wires_to_segments(wires, segments)
return segments
GCLK_MATCH = re.compile('GCLK_(L_)?B[0-9]+')
LOGIC_OUT_MATCH = re.compile('LOGIC_OUTS')
BRAM_CASCADE = re.compile('BRAM_CASC(OUT|IN|INBOT)_')
HCLK_R2C_MATCH = re.compile('HCLK_CK_(OUTIN|INOUT)')
HCLK_CK_IN = re.compile('HCLK_CK_IN[0-9]+')
CCIO_CLK_IN = re.compile('HCLK_CCIO[0-3]')
class SegmentWireMap(object):
""" SegmentWireMap provides a way to map node wires to segments.
The default segment should be used for non-routing wires, e.g. the wires
that go from the interconnect switch box to sites. This default segment
will generally be low delay extremely short wires.
Routing segments (e.g. LH = 12-length horizontal wire) will have
specialized lookahead entries, and should be given their own segment.
"""
def __init__(self, default_segment, db):
self.default_segment = default_segment
self.segments = get_segments(db)
self.wire_to_segment = {}
for segment, wires in self.segments.items():
for wire in wires:
assert wire not in self.wire_to_segment
self.wire_to_segment[wire] = segment
def get_segment_for_wires(self, wires):
wires = list(wires)
segments = set()
# BRAM_IMUX cannot use INPINFEED because it doesn't obey typically
# connection box definitions.
is_bram_imux = False
for wire in wires:
if 'BRAM_IMUX' in wire:
is_bram_imux = True
break
for wire in wires:
if wire in self.wire_to_segment and not is_bram_imux:
segments.add(self.wire_to_segment[wire])
m = LOGIC_OUT_MATCH.match(wire)
if m is not None:
segments.add('OUTPINFEED')
m = BRAM_CASCADE.search(wire)
if m is not None:
segments.add('BRAM_CASCADE')
if 'CK_BUFG_CASC' in wire:
segments.add('BUFG_CASCADE')
m = HCLK_CK_IN.match(wire)
if m is not None:
segments.add('HCLK_CK_IN')
if 'R_CK_GCLK' in wire:
segments.add('GCLK')
if 'INT_INTERFACE_LOGIC_OUTS' in wire:
segments.add('OUTPINFEED')
m = CCIO_CLK_IN.match(wire)
if m is not None:
segments.add('CCIO_CLK_IN')
if wire.startswith('CLK_BUFG_CK_GCLK'):
segments.add('GCLK')
if 'CK_BUFHCLK' in wire:
segments.add('HCLK_ROWS')
if HCLK_R2C_MATCH.match(wire):
segments.add('HCLK_ROW_TO_COLUMN')
if wire.startswith('BRAM_IMUX'):
segments.add('BRAM_IMUX')
assert len(segments) <= 1, (wires, segments)
if len(segments) == 1:
return list(segments)[0]
else:
return self.default_segment
def get_segments(self):
return self.segments.keys()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--db_root', required=True)
parser.add_argument('--part', required=True)
args = parser.parse_args()
db = Database(args.db_root, args.part)
segments = get_segments(db)
for segment in sorted(segments):
print('Segment = {}'.format(segment))
print('Wires:')
for wire in sorted(segments[segment]):
print(' {}'.format(wire))
if __name__ == '__main__':
main()