| """ 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() |