Exemple #1
0
def quick_test(db_root):
    db = prjxray.db.Database(db_root)
    g = db.grid()

    # Verify that we have some tile information for every tile in grid.
    tile_types_in_grid = set(
        g.gridinfo_at_loc(loc).tile_type for loc in g.tile_locations())
    tile_types_in_db = set(db.get_tile_types())
    site_types = set(db.get_site_types())
    assert len(tile_types_in_grid - tile_types_in_db) == 0

    # Verify that all tile types can be loaded.
    for tile_type in db.get_tile_types():
        tile = db.get_tile_type(tile_type)
        wires = tile.get_wires()
        for site in tile.get_sites():
            assert site.type in site_types
            site_type = db.get_site_type(site.type)
            site_pins = site_type.get_site_pins()
            for site_pin in site.site_pins:
                if site_pin.wire is not None:
                    assert site_pin.wire in wires, (site_pin.wire, )

                assert site_pin.name in site_pins

        for pip in tile.get_pips():
            assert pip.net_to in wires
            assert pip.net_from in wires

    for loc in g.tile_locations():
        gridinfo = g.gridinfo_at_loc(loc)
        assert gridinfo.tile_type in db.get_tile_types()
        for site_name, site_type in gridinfo.sites.items():
            assert site_type in site_types

        tile = db.get_tile_type(gridinfo.tile_type)

        # FIXME: The way sites are named in Tile.get_instance_sites is broken
        # for thes tile types, skip them until the underlying data is fixed.
        BROKEN_TILE_TYPES = [
            'BRAM_L', 'BRAM_R', 'HCLK_IOI3', 'CMT_TOP_L_UPPER_B',
            'CMT_TOP_R_UPPER_B'
        ]
        if gridinfo.tile_type in BROKEN_TILE_TYPES:
            continue

        instance_sites = list(tile.get_instance_sites(gridinfo))
        assert len(instance_sites) == len(tile.get_sites())
def add_wire_to_site_relation(db, c, tile_types, site_types, tile_type_name):
    tile_type = db.get_tile_type(tile_type_name)
    for site in tile_type.get_sites():
        c.execute(
            """
INSERT INTO site(name, x_coord, y_coord, site_type_pkey)
VALUES
  (?, ?, ?, ?)""", (site.name, site.x, site.y, site_types[site.type]))

        site_pkey = c.lastrowid

        for site_pin in site.site_pins:
            c.execute(
                """
SELECT
  pkey
FROM
  site_pin
WHERE
  name = ?
  AND site_type_pkey = ?""", (site_pin.name, site_types[site.type]))

            result = c.fetchone()
            site_pin_pkey = result[0]
            c.execute(
                """
UPDATE
  wire_in_tile
SET
  site_pkey = ?,
  site_pin_pkey = ?
WHERE
  name = ?
  and tile_type_pkey = ?;""", (site_pkey, site_pin_pkey, site_pin.wire,
                               tile_types[tile_type_name]))
Exemple #3
0
def main():
    parser = argparse.ArgumentParser(description=__doc__)

    parser.add_argument('--db_root', required=True)
    parser.add_argument('--part', required=True)
    parser.add_argument('--output_directory', required=True)
    parser.add_argument('--site_directory', required=True)
    parser.add_argument('--tile_type', required=True)
    parser.add_argument('--pb_types', required=True)
    parser.add_argument('--pin_assignments', required=True)

    args = parser.parse_args()

    with open(args.pin_assignments) as f:
        pin_assignments = json.load(f)

    db = prjxray.db.Database(args.db_root, args.part)
    tile_type = db.get_tile_type(args.tile_type)

    sites = {}

    pb_types = args.pb_types.split(',')

    equivalent_sites_dict = dict()
    for pb_type in pb_types:
        try:
            site, equivalent_sites = pb_type.split("/")
        except ValueError:
            site = pb_type
            equivalent_sites = None

        sites[site] = []

        equivalent_sites_dict[site] = equivalent_sites.split(
            ':') if equivalent_sites else []

    for site in tile_type.get_sites():
        if site.type not in sites.keys():
            continue

        site_type = db.get_site_type(site.type)
        input_wires, output_wires = get_wires(site, site_type)

        sites[site.type].append((site, input_wires, output_wires))

    tile_xml = start_heterogeneous_tile(
        args.tile_type,
        pin_assignments,
        sites,
        equivalent_sites_dict,
    )

    add_switchblock_locations(tile_xml)

    with open(
            '{}/{}.tile.xml'.format(args.output_directory,
                                    args.tile_type.lower()), 'w') as f:
        tile_str = ET.tostring(tile_xml, pretty_print=True).decode('utf-8')
        f.write(tile_str)
def initialize_edge_assignments(db, conn):
    """ Create initial edge_assignments map. """
    c = conn.cursor()
    c2 = conn.cursor()

    edge_assignments = {}
    wires_in_tile_types = set()

    # First find out which tile types were split during VPR grid formation.
    # These tile types should not get edge assignments directly, instead
    # their sites will get edge assignements.
    sites_as_tiles = set()
    split_tile_types = set()
    for site_pkey, tile_type_pkey in c.execute("""
        SELECT site_pkey, tile_type_pkey FROM site_as_tile;
        """):
        c2.execute("SELECT name FROM tile_type WHERE pkey = ?",
                   (tile_type_pkey, ))
        split_tile_types.add(c2.fetchone()[0])

        c2.execute(
            """
SELECT name FROM site_type WHERE pkey = (
    SELECT site_type_pkey FROM site WHERE pkey = ?
    );""", (site_pkey, ))
        site_type_name = c2.fetchone()[0]
        sites_as_tiles.add(site_type_name)

    # Initialize edge assignments for split tiles
    for site_type in sites_as_tiles:
        site_obj = db.get_site_type(site_type)
        for site_pin in site_obj.get_site_pins():
            key = (site_type, site_pin)
            assert key not in edge_assignments, key

            edge_assignments[key] = []

    for tile_type in db.get_tile_types():
        # Skip tile types that are split tiles
        if tile_type in split_tile_types:
            continue

        type_obj = db.get_tile_type(tile_type)

        for wire in type_obj.get_wires():
            wires_in_tile_types.add((tile_type, wire))

        for site in type_obj.get_sites():
            for site_pin in site.site_pins:
                if site_pin.wire is None:
                    continue

                key = (tile_type, site_pin.wire)
                assert key not in edge_assignments, key
                edge_assignments[key] = []

    return edge_assignments, wires_in_tile_types
Exemple #5
0
    def add_sites_and_pips_to_node(self, db, grid):
        for tile, wire in self.node:
            tileinfo = grid.gridinfo_at_tilename(tile)

            tile_type = db.get_tile_type(tileinfo.tile_type)

            wire_info = tile_type.get_wire_info(wire)

            for pip in wire_info.pips:
                self.pips.append((tile, pip, wire))

            for site in wire_info.sites:
                self.sites.append((tile, ) + site)
Exemple #6
0
def quick_test(db_root):
    db = prjxray.db.Database(db_root)
    g = db.grid()

    # Verify that we have some tile information for every tile in grid.
    tile_types_in_grid = set(
        g.gridinfo_at_loc(loc).tile_type for loc in g.tile_locations())
    tile_types_in_db = set(db.get_tile_types())
    site_types = set(db.get_site_types())
    assert len(tile_types_in_grid - tile_types_in_db) == 0

    # Verify that all tile types can be loaded.
    for tile_type in db.get_tile_types():
        tile = db.get_tile_type(tile_type)
        wires = tile.get_wires()
        for site in tile.get_sites():
            assert site.type in site_types
            site_type = db.get_site_type(site.type)
            site_pins = site_type.get_site_pins()
            for site_pin in site.site_pins:
                if site_pin.wire is not None:
                    assert site_pin.wire in wires, (site_pin.wire, )

                assert site_pin.name in site_pins

        for pip in tile.get_pips():
            assert pip.net_to in wires
            assert pip.net_from in wires

    for loc in g.tile_locations():
        gridinfo = g.gridinfo_at_loc(loc)
        assert gridinfo.tile_type in db.get_tile_types()
        for site_name, site_type in gridinfo.sites.items():
            assert site_type in site_types

        tile = db.get_tile_type(gridinfo.tile_type)

        instance_sites = list(tile.get_instance_sites(gridinfo))
        assert len(instance_sites) == len(tile.get_sites())
Exemple #7
0
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)

    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():
        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, tile_type_pkey, capacitance, resistance)
VALUES
  (?, ?, ?, ?)""", (wire, tile_types[tile_type_name], capacitance, resistance))
        wires[wire] = write_cur.lastrowid

    for pip in tile_type.get_pips():
        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))

    for site in tile_type.get_sites():
        if site.type not in site_types:
            import_site_type(db, write_cur, site_types, site.type)
def import_tile_type(db, write_cur, tile_types, site_types, tile_type_name):
    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

        write_cur.execute(
            """
INSERT INTO wire_in_tile(name, phy_tile_type_pkey, tile_type_pkey)
VALUES
  (?, ?, ?)""", (
                wire,
                tile_types[tile_type_name],
                tile_types[tile_type_name],
            ))
        wires[wire] = write_cur.lastrowid

    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)
Exemple #9
0
def make_connections(db):
    # Some nodes are just 1 wire, so start by enumerating all wires.
    wires = {}
    grid = db.grid()
    for tile in progressbar.progressbar(grid.tiles()):
        gridinfo = grid.gridinfo_at_tilename(tile)
        tile_type = db.get_tile_type(gridinfo.tile_type)

        for wire in tile_type.get_wires():
            key = (tile, wire)
            wires[key] = set((key, ))

    c = db.connections()

    for connection in progressbar.progressbar(c.get_connections()):
        make_connection(wires, connection)

    nodes = {}

    for wire_node in wires.values():
        nodes[id(wire_node)] = wire_node

    return nodes.values()
def import_tile_type(db, c, tile_types, site_types, tile_type_name):
    assert tile_type_name not in tile_types
    tile_type = db.get_tile_type(tile_type_name)

    c.execute("INSERT INTO tile_type(name) VALUES (?)", (tile_type_name, ))
    tile_types[tile_type_name] = c.lastrowid

    wires = {}
    for wire in tile_type.get_wires():
        c.execute(
            """
INSERT INTO wire_in_tile(name, tile_type_pkey)
VALUES
  (?, ?)""", (wire, tile_types[tile_type_name]))
        wires[wire] = c.lastrowid

    for pip in tile_type.get_pips():
        # Psuedo pips are not part of the routing fabric, so don't add them.
        if pip.is_pseudo:
            continue

        if not pip.is_directional:
            continue

        c.execute(
            """
INSERT INTO pip_in_tile(
  name, tile_type_pkey, src_wire_in_tile_pkey,
  dest_wire_in_tile_pkey
)
VALUES
  (?, ?, ?, ?)""", (pip.name, tile_types[tile_type_name], wires[pip.net_from],
                    wires[pip.net_to]))

    for site in tile_type.get_sites():
        if site.type not in site_types:
            import_site_type(db, c, site_types, site.type)
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 main():
    mydir = os.path.dirname(__file__)
    prjxray_db = os.path.abspath(os.path.join(mydir, "..", "..", "third_party", "prjxray-db"))

    db_types = prjxray.db.get_available_databases(prjxray_db)

    parser = argparse.ArgumentParser(description="Generate arch.xml")
    parser.add_argument(
        '--part', choices=[os.path.basename(db_type) for db_type in db_types],
        help="""Project X-Ray database to use.""")
    parser.add_argument(
            '--output-arch', nargs='?', type=argparse.FileType('w'),
            help="""File to output arch.""")
    parser.add_argument('--tile-types', help="Semi-colon seperated tile types.")
    parser.add_argument(
            '--pin_assignments', required=True, type=argparse.FileType('r'))
    parser.add_argument(
            '--use_roi', required=False)
    parser.add_argument(
            '--synth_tiles', required=False)
    parser.add_argument(
            '--device', required=True)

    args = parser.parse_args()

    tile_types = args.tile_types.split(',')

    tile_model = "../../tiles/{0}/{0}.model.xml"
    tile_pbtype = "../../tiles/{0}/{0}.pb_type.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 tile_type in tile_types:
        ET.SubElement(model_xml, xi_include, {
            'href': tile_model.format(tile_type.lower()),
            'xpointer':"xpointer(models/child::node())",
        })

    complexblocklist_xml = ET.SubElement(arch_xml, 'complexblocklist')
    for tile_type in tile_types:
        ET.SubElement(complexblocklist_xml, xi_include, {
            'href': tile_pbtype.format(tile_type.lower()),
        })

    layout_xml = ET.SubElement(arch_xml, 'layout')
    db = prjxray.db.Database(os.path.join(prjxray_db, args.part))
    g = db.grid()
    x_min, x_max, y_min, y_max = g.dims()

    # FIXME: There is an issue in the routing phase. (https://github.com/SymbiFlow/symbiflow-arch-defs/issues/353)
    # if a zynq device is selected the grid must be expanded by 1
    if args.device == 'xc7z010':
        x_max += 1
        y_max += 1

    name = '{}-test'.format(args.device)
    fixed_layout_xml = ET.SubElement(layout_xml, 'fixed_layout', {
            'name': name,
            'height': str(y_max+1),
            'width': str(x_max+1),
    })

    only_emit_roi = False
    roi_inputs = []
    roi_outputs = []

    synth_tiles = {}
    synth_tiles['tiles'] = {}
    if args.use_roi:
        only_emit_roi = True
        with open(args.use_roi) as f:
            j = 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'],
                )

        synth_tiles['info'] = j['info']
        for port in j['ports']:
            if port['name'].startswith('dout['):
                roi_outputs.append(port)
                port_type = 'input'
                is_clock = False
            elif port['name'].startswith('din['):
                roi_inputs.append(port)
                is_clock = False
                port_type = 'output'
            elif port['name'].startswith('clk'):
                roi_inputs.append(port)
                port_type = 'output'
                is_clock = True
            else:
                assert False, port

            tile, wire = port['wire'].split('/')

            # Make sure connecting wire is not in ROI!
            loc = g.loc_of_tilename(tile)
            if roi.tile_in_roi(loc):
                # Or if in the ROI, make sure it has no sites.
                gridinfo = g.gridinfo_at_tilename(tile)
                assert len(db.get_tile_type(gridinfo.tile_type).get_sites()) == 0, tile



            if tile not in synth_tiles['tiles']:
                synth_tiles['tiles'][tile] = {
                        'pins': [],
                        'loc': g.loc_of_tilename(tile),
                }

            synth_tiles['tiles'][tile]['pins'].append({
                    'roi_name': port['name'].replace('[', '_').replace(']','_'),
                    'wire': wire,
                    'pad': port['pin'],
                    'port_type': port_type,
                    'is_clock': is_clock,
            })

        with open(args.synth_tiles, 'w') as f:
            json.dump(synth_tiles, f)

        synth_tile_map = add_synthetic_tile(complexblocklist_xml)

    for loc in g.tile_locations():
        gridinfo = g.gridinfo_at_loc(loc)
        tile = g.tilename_at_loc(loc)

        if tile in synth_tiles['tiles']:
            synth_tile = synth_tiles['tiles'][tile]

            assert len(synth_tile['pins']) == 1

            vpr_tile_type = synth_tile_map[synth_tile['pins'][0]['port_type']]
        elif only_emit_roi and not roi.tile_in_roi(loc):
            # This tile is outside the ROI, skip it.
            continue
        elif gridinfo.tile_type in tile_types:
            # We want to import this tile type.
            vpr_tile_type = 'BLK_TI-{}'.format(gridinfo.tile_type)
        else:
            # We don't want this tile
            continue

        is_vbrk = gridinfo.tile_type.find('VBRK') != -1

        # VBRK tiles are known to have no bitstream data.
        if not is_vbrk and not gridinfo.bits:
            print('*** WARNING *** Skipping tile {} because it lacks bitstream data.'.format(tile),
                    file=sys.stderr)

        single_xml = ET.SubElement(fixed_layout_xml, 'single', {
                'priority': '1',
                'type': vpr_tile_type,
                'x': str(loc[0]),
                'y': str(loc[1]),
        })
        meta = ET.SubElement(single_xml, 'metadata')
        ET.SubElement(meta, 'meta', {
                'name': 'fasm_prefix',
        }).text = tile

    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',
    })

    switchlist_xml = ET.SubElement(arch_xml, 'switchlist')

    ET.SubElement(switchlist_xml, 'switch', {
                  'type':'mux',
                  'name':'routing',
                  "R":"551", "Cin":".77e-15",  "Cout":"4e-15", "Tdel":"6.8e-12",
                  "mux_trans_size":"2.630740",
                  "buf_size":"27.645901"
                  })
    ET.SubElement(switchlist_xml, 'switch', {
                  'type':'mux',
                  'name':'buffer',
                  "R":"551", "Cin":".77e-15",  "Cout":"4e-15", "Tdel":"6.8e-12",
                  "mux_trans_size":"2.630740",
                  "buf_size":"27.645901"
                  })

    segmentlist_xml = ET.SubElement(arch_xml, 'segmentlist')

    # VPR requires a segment, so add one.
    dummy_xml = ET.SubElement(segmentlist_xml, 'segment', {
            'name': 'dummy',
            'length': '12',
            'freq': '1.0',
            'type': 'bidir',
            'Rmetal':'101',
            'Cmetal':'22.5e15',
    })
    ET.SubElement(dummy_xml, 'wire_switch', {
            'name': 'routing',
    })
    ET.SubElement(dummy_xml, 'opin_switch', {
            'name': 'routing',
    })
    ET.SubElement(dummy_xml, 'sb', {
            'type': 'pattern',
    }).text = ' '.join('1' for _ in range(13))
    ET.SubElement(dummy_xml, 'cb', {
            'type': 'pattern',
    }).text = ' '.join('1' for _ in range(12))

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

    for direct in directs.values():
        _, direct = min(direct, key=lambda v: v[0])

        if direct['from_pin'].split('.')[0] not in tile_types:
            continue
        if direct['to_pin'].split('.')[0] not in tile_types:
            continue

        if direct['x_offset'] == 0 and direct['y_offset'] == 0:
            continue

        ET.SubElement(directlist_xml, 'direct', {
                'name': '{}_to_{}_dx_{}_dy_{}'.format(direct['from_pin'], direct['to_pin'], direct['x_offset'], direct['y_offset']),
                'from_pin': 'BLK_TI-' + direct['from_pin'],
                'to_pin': 'BLK_TI-' + direct['to_pin'],
                'x_offset': str(direct['x_offset']),
                'y_offset': str(direct['y_offset']),
                'z_offset': '0',
                'switch_name': direct['switch_name'],
        })


    arch_xml_str = ET.tostring(arch_xml, pretty_print=True).decode('utf-8')
    args.output_arch.write(arch_xml_str)
    args.output_arch.close()
Exemple #13
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
            '--db_root', required=True, help='Project X-Ray Database')
    parser.add_argument(
            '--read_rr_graph', required=True, help='Input rr_graph file')
    parser.add_argument(
            '--write_rr_graph', required=True, help='Output rr_graph file')
    parser.add_argument(
            '--channels', required=True, help='Channel definitions from prjxray_form_channels')
    parser.add_argument(
            '--synth_tiles', help='If using an ROI, synthetic tile defintion from prjxray-arch-import')

    args = parser.parse_args()

    db = prjxray.db.Database(args.db_root)
    grid = db.grid()

    if args.synth_tiles:
        use_roi = True
        with open(args.synth_tiles) as f:
            synth_tiles = json.load(f)

        roi = Roi(
                db=db,
                x1=synth_tiles['info']['GRID_X_MIN'],
                y1=synth_tiles['info']['GRID_Y_MIN'],
                x2=synth_tiles['info']['GRID_X_MAX'],
                y2=synth_tiles['info']['GRID_Y_MAX'],
                )

        print('{} generating routing graph for ROI.'.format(now()))
    else:
        use_roi = False

    # Convert input rr graph into graph2.Graph object.
    input_rr_graph = read_xml_file(args.read_rr_graph)

    xml_graph = xml_graph2.Graph(
            input_rr_graph,
            progressbar=progressbar.progressbar)

    graph = xml_graph.graph

    tool_version = input_rr_graph.getroot().attrib['tool_version']
    tool_comment = input_rr_graph.getroot().attrib['tool_comment']

    delayless_switch = graph.get_delayless_switch_id()

    print('{} reading channels definitions.'.format(now()))
    with open(args.channels) as f:
        channels = json.load(f)

    segment_id = graph.get_segment_id_from_name('dummy')

    track_wire_map = {}

    print('{} add nodes for all channels.'.format(now()))
    used_channels = 0
    for idx, channel in progressbar.progressbar(enumerate(channels['channels'])):
        # Don't use dead channels if using an ROI.
        # Consider a channel alive if at least 1 wire in the node is part of a
        # live tile.
        if use_roi:
            alive = False
            for tile, wire in channel['wires']:
                loc = grid.loc_of_tilename(tile)
                if roi.tile_in_roi(loc) or tile in synth_tiles['tiles']:
                    alive = True
                    break

            if not alive:
                continue

        used_channels += 1
        nodes = []
        track_list = []
        for idx2, track_dict in enumerate(channel['tracks']):
            if track_dict['direction'] == 'X':
                track_dict['x_low'] = max(track_dict['x_low'], 1)
            elif track_dict['direction'] == 'Y':
                track_dict['y_low'] = max(track_dict['y_low'], 1)
            track = tracks.Track(**track_dict)
            track_list.append(track)

            nodes.append(graph.add_track(track=track, segment_id=segment_id, name='track_{}_{}'.format(idx, idx2)))

        for a_idx, b_idx in channel['track_connections']:
            graph.add_edge(nodes[a_idx], nodes[b_idx], delayless_switch, 'track_{}_to_{}'.format(a_idx, b_idx))
            graph.add_edge(nodes[b_idx], nodes[a_idx], delayless_switch, 'track_{}_to_{}'.format(b_idx, a_idx))

        tracks_model = tracks.Tracks(track_list, channel['track_connections'])

        for tile, wire in channel['wires']:
            track_wire_map[(tile, wire)] = (tracks_model, nodes)

    print('original {} final {}'.format(len(channels['channels']), used_channels))

    routing_switch = graph.get_switch_id('routing')

    pip_map = {}

    edges_with_mux = {}
    for idx, edge_with_mux in progressbar.progressbar(enumerate(channels['edges_with_mux'])):
        if edge_with_mux['pip'] not in edges_with_mux:
            edges_with_mux[edge_with_mux['pip']] = {}

        assert len(edge_with_mux['source_node']) == 1
        edges_with_mux[edge_with_mux['pip']][tuple(edge_with_mux['source_node'][0])] = edge_with_mux['destination_node']

    # Set of (src, sink, switch_id) tuples that pip edges have been sent to
    # VPR.  VPR cannot handle duplicate paths with the same switch id.
    pip_set = set()
    print('{} Adding edges'.format(now()))
    for loc in progressbar.progressbar(grid.tile_locations()):
        gridinfo = grid.gridinfo_at_loc(loc)
        tile_name = grid.tilename_at_loc(loc)

        if use_roi:
            if tile_name in synth_tiles['tiles']:
                assert len(synth_tiles['tiles'][tile_name]['pins']) == 1
                for pin in synth_tiles['tiles'][tile_name]['pins']:
                    tracks_model, track_nodes = track_wire_map[(tile_name, pin['wire'])]

                    option = list(tracks_model.get_tracks_for_wire_at_coord(loc))
                    assert len(option) > 0

                    if pin['port_type'] == 'input':
                        tile_type = 'BLK_SY-OUTPAD'
                        wire = 'outpad'
                    elif pin['port_type'] == 'output':
                        tile_type = 'BLK_SY-INPAD'
                        wire = 'inpad'
                    else:
                        assert False, pin

                    track_node = track_nodes[option[0][0]]
                    pin_name = graph.create_pin_name_from_tile_type_and_pin(
                            tile_type,
                            wire)

                    pin_node = graph.get_nodes_for_pin(loc, pin_name)

                    if pin['port_type'] == 'input':
                        graph.add_edge(
                                src_node=track_node,
                                sink_node=pin_node[0][0],
                                switch_id=routing_switch,
                                name='synth_{}_{}'.format(tile_name, pin['wire']),
                        )
                    elif pin['port_type'] == 'output':
                        graph.add_edge(
                                src_node=pin_node[0][0],
                                sink_node=track_node,
                                switch_id=routing_switch,
                                name='synth_{}_{}'.format(tile_name, pin['wire']),
                        )
                    else:
                        assert False, pin
            else:
                # Not a synth node, check if in ROI.
                if not roi.tile_in_roi(loc):
                    continue

        tile_type = db.get_tile_type(gridinfo.tile_type)

        for pip in tile_type.get_pips():
            if pip.is_pseudo:
                continue

            if not pip.is_directional:
                # TODO: Handle bidirectional pips?
                continue

            edge_node = make_connection(graph, track_wire_map, loc, tile_name, gridinfo.tile_type,
                            pip, routing_switch, edges_with_mux, grid, pip_set)

            if edge_node is not None:
                pip_map[(tile_name, pip.name)] = edge_node

    print('{} Writing node mapping.'.format(now()))
    node_mapping = {
            'pips': [],
            'tracks': []
        }

    for (tile, pip_name), edge in pip_map.items():
        node_mapping['pips'].append({
                'tile': tile,
                'pip': pip_name,
                'edge': edge
        })

    for (tile, wire), (_, nodes) in track_wire_map.items():
        node_mapping['tracks'].append({
                'tile': tile,
                'wire': wire,
                'nodes': nodes,
        })

    with open('node_mapping.pickle', 'wb') as f:
        pickle.dump(node_mapping, f)

    print('{} Create channels and serializing.'.format(now()))
    pool = multiprocessing.Pool(10)
    serialized_rr_graph = xml_graph.serialize_to_xml(
            tool_version=tool_version,
            tool_comment=tool_comment,
            pad_segment=segment_id,
            pool=pool,
    )

    print('{} Writing to disk.'.format(now()))
    with open(args.write_rr_graph, "wb") as f:
        f.write(serialized_rr_graph)
    print('{} Done.'.format(now()))
Exemple #14
0
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.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()

            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.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.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()
Exemple #15
0
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():
        write_cur.execute(
            """
INSERT INTO site(name, x_coord, y_coord, site_type_pkey)
VALUES
  (?, ?, ?, ?)""", (site.name, site.x, site.y, site_types[site.type]))

        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 import_tile(db, args):
    """ Create a root-level pb_type with the pin names that match tile wires.

    This will either have 1 intermediate pb_type per site, or 1 large site
    for the entire tile if args.fused_sites is set to true.
    """

    tile = db.get_tile_type(args.tile)

    # Wires sink to a site within the tile are input wires.
    input_wires = set()

    # Wires source from a site within the tile are output wires.
    output_wires = set()

    if args.filter_x:
        xs = list(map(int, args.filter_x.split(',')))

        def x_filter_func(site):
            return site.x in xs

        x_filter = x_filter_func
    else:

        def x_filter_func(site):
            return True

        x_filter = x_filter_func

    if not args.fused_sites:
        site_type_instances = parse_site_type_instance(args.site_types)

        imported_site_types = set()
        ignored_site_types = set()

        for site in tile.get_sites():
            site_type = db.get_site_type(site.type)

            if site.type not in site_type_instances:
                ignored_site_types.add(site.type)
                continue

            imported_site_types.add(site.type)

            for site_pin in site.site_pins:
                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    if site_pin.wire is not None:
                        input_wires.add(site_pin.wire)
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    if site_pin.wire is not None:
                        output_wires.add(site_pin.wire)
                else:
                    if site.type != "PS7":
                        assert False, site_type_pin.direction

        # Make sure all requested site types actually get imported.
        assert len(set(site_type_instances.keys()) -
                   imported_site_types) == 0, (site_type_instances.keys(),
                                               imported_site_types)

        for ignored_site_type in ignored_site_types:
            print(
                '*** WARNING *** Ignored site type {} in tile type {}'.format(
                    ignored_site_type, args.tile),
                file=sys.stderr)
    else:
        for site in tile.get_sites():
            site_type = db.get_site_type(site.type)

            for site_pin in site.site_pins:
                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    if site_pin.wire is not None:
                        input_wires.add(site_pin.wire)
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    if site_pin.wire is not None:
                        output_wires.add(site_pin.wire)
                else:
                    assert False, site_type_pin.direction

    site_pbtype = args.site_directory + "/{0}/{1}.pb_type.xml"

    ##########################################################################
    # Generate the model.xml file                                            #
    ##########################################################################

    model = ModelXml(f=args.output_model, site_directory=args.site_directory)

    if args.fused_sites:
        fused_site_name = args.tile.lower()

        model.add_model_include(fused_site_name, fused_site_name)

    ##########################################################################
    # Utility functions for pb_type                                          #
    ##########################################################################
    tile_name = args.tile

    pb_type_xml = start_pb_type(tile_name, args.pin_assignments, input_wires,
                                output_wires)

    cell_names = {}

    interconnect_xml = ET.Element('interconnect')

    if not args.fused_sites:
        site_type_count = {}
        site_prefixes = {}
        cells_idx = {}
        models_added = set()

        site_type_ports = {}
        idx = 0
        for site in tile.get_sites():
            if site.type in ignored_site_types:
                continue

            if not x_filter(site):
                continue

            if site.type not in site_type_count:
                site_type_count[site.type] = 0
                site_prefixes[site.type] = []

            cells_idx[idx] = site_type_count[site.type]
            site_type_count[site.type] += 1

            site_coords = args.site_coords.upper()
            if site_coords == 'X':
                site_prefix = '{}_X{}'.format(site.type, site.x)
            elif site_coords == 'Y':
                site_prefix = '{}_Y{}'.format(site.type, site.y)
            elif site_coords == 'XY':
                site_prefix = '{}.{}_X{}Y{}'.format(site.type, site.type,
                                                    site.x, site.y)
            else:
                assert False, "Invalid --site-coords value '{}'".format(
                    site_coords)

            site_instance = site_type_instances[site.type][cells_idx[idx]]
            idx += 1

            if (site.type, site_instance) not in models_added:
                models_added.add((site.type, site_instance))
                model.add_model_include(site.type, site_instance)

            site_type_path = site_pbtype.format(site.type.lower(),
                                                site_instance.lower())
            cell_pb_type = ET.ElementTree()
            root_element = cell_pb_type.parse(site_type_path)
            cell_names[site_instance] = root_element.attrib['name']

            ports = {}
            for inputs in root_element.iter('input'):
                ports[inputs.attrib['name']] = int(inputs.attrib['num_pins'])

            for clocks in root_element.iter('clock'):
                ports[clocks.attrib['name']] = int(clocks.attrib['num_pins'])

            for outputs in root_element.iter('output'):
                ports[outputs.attrib['name']] = int(outputs.attrib['num_pins'])

            assert site_instance not in site_type_ports, (
                site_instance, site_type_ports.keys())
            site_type_ports[site_instance] = ports

            attrib = dict(root_element.attrib)
            include_xml = ET.SubElement(pb_type_xml, 'pb_type', attrib)
            ET.SubElement(
                include_xml, XI_INCLUDE, {
                    'href':
                    site_type_path,
                    'xpointer':
                    "xpointer(pb_type/child::node()[local-name()!='metadata'])",
                })

            metadata_xml = ET.SubElement(include_xml, 'metadata')

            if not args.no_fasm_prefix:
                ET.SubElement(metadata_xml, 'meta', {
                    'name': 'fasm_prefix',
                }).text = site_prefix

            # Import pb_type metadata if it exists.
            if any(child.tag == 'metadata' for child in root_element):
                ET.SubElement(
                    metadata_xml, XI_INCLUDE, {
                        'href': site_type_path,
                        'xpointer': "xpointer(pb_type/metadata/child::node())",
                    })

            # Prevent emitting empty metadata
            if not any(child.tag == 'meta' for child in metadata_xml):
                include_xml.remove(metadata_xml)

        idx = 0
        for site in tile.get_sites():
            if site.type in ignored_site_types:
                continue

            site_idx = cells_idx[idx]
            idx += 1

            if not x_filter(site):
                continue

            site_instance = site_type_instances[site.type][site_idx]
            site_name = cell_names[site_instance]

            site_type = db.get_site_type(site.type)

            interconnect_xml.append(ET.Comment(" Tile->Site "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port = find_port(site_pin.name, site_type_ports[site_instance])
                if port is None:
                    print(
                        "*** WARNING *** Didn't find port for name {} for site type {}"
                        .format(site_pin.name, site.type),
                        file=sys.stderr)
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    add_direct(interconnect_xml,
                               input=object_ref(add_vpr_tile_prefix(tile_name),
                                                site_pin.wire),
                               output=object_ref(site_name, **port))
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    pass
                else:
                    if site.type != "PS7":
                        assert False, site_type_pin.direction

            interconnect_xml.append(ET.Comment(" Site->Tile "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port = find_port(site_pin.name, site_type_ports[site_instance])
                if port is None:
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    pass
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    add_direct(
                        interconnect_xml,
                        input=object_ref(site_name, **port),
                        output=object_ref(add_vpr_tile_prefix(tile_name),
                                          site_pin.wire),
                    )
                else:
                    if site.type != "PS7":
                        assert False, site_type_pin.direction
    else:
        site_type_ports = {}

        site_type_path = site_pbtype.format(fused_site_name, fused_site_name)
        cell_pb_type = ET.ElementTree()
        root_element = cell_pb_type.parse(site_type_path)

        ports = {}
        for inputs in root_element.iter('input'):
            ports[inputs.attrib['name']] = int(inputs.attrib['num_pins'])

        for clocks in root_element.iter('clock'):
            ports[clocks.attrib['name']] = int(clocks.attrib['num_pins'])

        for outputs in root_element.iter('output'):
            ports[outputs.attrib['name']] = int(outputs.attrib['num_pins'])

        attrib = dict(root_element.attrib)
        include_xml = ET.SubElement(pb_type_xml, 'pb_type', attrib)
        ET.SubElement(
            include_xml, XI_INCLUDE, {
                'href': site_type_path,
                'xpointer': "xpointer(pb_type/child::node())",
            })

        site_name = root_element.attrib['name']

        def fused_port_name(site, site_pin):
            return '{}_{}_{}'.format(site.prefix, site.name, site_pin.name)

        for site in tile.get_sites():
            site_type = db.get_site_type(site.type)

            interconnect_xml.append(ET.Comment(" Tile->Site "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port_name = fused_port_name(site, site_pin)
                port = find_port(port_name, ports)
                if port is None:
                    print(
                        "*** WARNING *** Didn't find port for name {} for site type {}"
                        .format(port_name, site.type),
                        file=sys.stderr)
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    add_direct(interconnect_xml,
                               input=object_ref(add_vpr_tile_prefix(tile_name),
                                                site_pin.wire),
                               output=object_ref(site_name, **port))
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    pass
                else:
                    if site.type != "PS7":
                        assert False, site_type_pin.direction

            interconnect_xml.append(ET.Comment(" Site->Tile "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port = find_port(fused_port_name(site, site_pin), ports)
                if port is None:
                    # Already warned above
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    pass
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    add_direct(
                        interconnect_xml,
                        input=object_ref(site_name, **port),
                        output=object_ref(add_vpr_tile_prefix(tile_name),
                                          site_pin.wire),
                    )
                else:
                    if site.type != "PS7":
                        assert False, site_type_pin.direction

    pb_type_xml.append(interconnect_xml)

    model.write_model()
    write_xml(args.output_pb_type, pb_type_xml)
Exemple #17
0
def initialize_edge_assignments(db, conn):
    """ Create initial edge_assignments map. """
    c = conn.cursor()
    c2 = conn.cursor()

    c.execute("""
SELECT name, pkey FROM tile_type WHERE pkey IN (
    SELECT DISTINCT tile_type_pkey FROM tile
    );""")
    tiles = dict(c)

    edge_assignments = {}
    wires_in_tile_types = set()

    # First find out which tile types were split during VPR grid formation.
    # These tile types should not get edge assignments directly, instead
    # their sites will get edge assignements.
    sites_as_tiles = set()
    split_tile_types = set()
    for site_pkey, tile_type_pkey in c.execute("""
        SELECT site_pkey, tile_type_pkey FROM site_as_tile;
        """):
        c2.execute("SELECT name FROM tile_type WHERE pkey = ?",
                   (tile_type_pkey, ))
        split_tile_types.add(c2.fetchone()[0])

        c2.execute(
            """
SELECT name FROM site_type WHERE pkey = (
    SELECT site_type_pkey FROM site WHERE pkey = ?
    );""", (site_pkey, ))
        site_type_name = c2.fetchone()[0]
        sites_as_tiles.add(site_type_name)

    # Initialize edge assignments for split tiles
    for site_type in sites_as_tiles:
        del tiles[site_type]

        site_obj = db.get_site_type(site_type)
        for site_pin in site_obj.get_site_pins():
            key = (site_type, site_pin)
            assert key not in edge_assignments, key

            edge_assignments[key] = []

    for tile_type in db.get_tile_types():
        if tile_type not in tiles:
            continue

        del tiles[tile_type]

        # Skip tile types that are split tiles
        if tile_type in split_tile_types:
            continue

        (tile_type_pkey, ) = c.execute(
            """
    SELECT pkey
    FROM tile_type
    WHERE name = ?
        """, (tile_type, )).fetchone()

        for (wire, ) in c.execute(
                """
    SELECT name
    FROM wire_in_tile
    WHERE tile_type_pkey = ?""", (tile_type_pkey, )):
            wires_in_tile_types.add((tile_type, wire))

        type_obj = db.get_tile_type(tile_type)
        for site in type_obj.get_sites():
            for site_pin in site.site_pins:
                if site_pin.wire is None:
                    continue

                # Skip if this wire is not in the database
                c.execute(
                    """
    SELECT pkey
    FROM wire_in_tile
    WHERE name = ?
""", (site_pin.wire, ))
                if not c.fetchone():
                    continue

                key = (tile_type, site_pin.wire)
                assert key not in edge_assignments, key
                edge_assignments[key] = []

    for tile_type, tile_pkey in tiles.items():
        assert tile_type not in split_tile_types

        for (wire, ) in c.execute(
                """
    SELECT name
    FROM wire_in_tile
    WHERE pkey in (
        SELECT DISTINCT wire_in_tile_pkey
        FROM wire
        WHERE tile_pkey IN (
            SELECT pkey
            FROM tile
            WHERE tile_type_pkey = ?)
        );""", (tile_pkey, )):
            wires_in_tile_types.add((tile_type, wire))

        for (wire, ) in c.execute(
                """
SELECT DISTINCT name
FROM wire_in_tile
WHERE pkey in (
    SELECT DISTINCT wire_in_tile_pkey
    FROM wire
    WHERE tile_pkey IN (
        SELECT pkey
        FROM tile
        WHERE tile_type_pkey = ?)
    )
    AND
        site_pin_pkey IS NOT NULL""", (tile_pkey, )):
            key = (tile_type, wire)
            assert key not in edge_assignments, key
            edge_assignments[key] = []

    return edge_assignments, wires_in_tile_types
def main():
    parser = argparse.ArgumentParser(description=__doc__)

    parser.add_argument('--db_root', required=True)
    parser.add_argument('--part', required=True)
    parser.add_argument('--output_directory', required=True)
    parser.add_argument('--site_directory', required=True)
    parser.add_argument('--tile_type', required=True)
    parser.add_argument('--pb_types', required=True)
    parser.add_argument('--pin_assignments', required=True)
    parser.add_argument(
        '--unused_wires',
        help="Comma seperated list of site wires to exclude in this tile.")

    args = parser.parse_args()

    with open(args.pin_assignments) as f:
        pin_assignments = json.load(f)

    db = prjxray.db.Database(args.db_root, args.part)
    grid = db.grid()
    tile_type = db.get_tile_type(args.tile_type)

    pb_types = args.pb_types.split(',')

    equivalent_sites_dict = dict()

    gridinfo = None
    for tile in grid.tiles():
        if args.tile_type in tile:
            gridinfo = grid.gridinfo_at_tilename(tile)

            break

    assert gridinfo

    for pb_type in pb_types:
        try:
            site, equivalent_sites = pb_type.split("/")
        except ValueError:
            site = pb_type
            equivalent_sites = None

        equivalent_sites_dict[site] = equivalent_sites.split(
            ':') if equivalent_sites else []

    sites = list()

    for site in tile_type.get_sites():
        site_type = db.get_site_type(site.type)
        input_wires, output_wires = get_wires(site, site_type,
                                              args.unused_wires)

        sites.append((site_type, site, input_wires, output_wires))

    sites = sorted(sites,
                   key=lambda site:
                   (site[1].type, int(site[1].x), int(site[1].y)))

    tile_xml = start_heterogeneous_tile(
        args.tile_type,
        pin_assignments,
        sites,
        equivalent_sites_dict,
    )

    add_switchblock_locations(tile_xml)

    with open(
            '{}/{}.tile.xml'.format(args.output_directory,
                                    args.tile_type.lower()), 'w') as f:
        tile_str = ET.tostring(tile_xml, pretty_print=True).decode('utf-8')
        f.write(tile_str)
Exemple #19
0
def main():
    parser = argparse.ArgumentParser(description=__doc__)

    parser.add_argument('--db_root', required=True)
    parser.add_argument('--output_directory', required=True)
    parser.add_argument('--site_directory', required=True)
    parser.add_argument('--tile_type', required=True)
    parser.add_argument('--pb_type', required=True)
    parser.add_argument('--pin_assignments', required=True)
    parser.add_argument('--site_coords', required=True)

    args = parser.parse_args()

    with open(args.pin_assignments) as f:
        pin_assignments = json.load(f)

    db = prjxray.db.Database(args.db_root)
    tile_type = db.get_tile_type(args.tile_type)

    input_wires = set()
    output_wires = set()

    sites = []

    for site in tile_type.get_sites():
        if site.type != args.pb_type:
            continue

        site_type = db.get_site_type(site.type)
        sites.append(site)

        for site_pin in site.site_pins:
            if site_type.get_site_pin(
                    site_pin.name).direction == SitePinDirection.IN:
                input_wires.add(site_pin.wire)
            elif site_type.get_site_pin(
                    site_pin.name).direction == SitePinDirection.OUT:
                output_wires.add(site_pin.wire)
            else:
                assert False, site_pin

    sites.sort(key=lambda site: (site.x, site.y))

    tile_xml = start_pb_type(
        args.tile_type,
        pin_assignments,
        input_wires,
        output_wires,
        root_pb_type=True,
        root_tag='tile',
    )

    tile_xml.attrib['capacity'] = str(len(sites))
    tile_xml.attrib['capacity_type'] = "explicit"

    equivalent_sites_xml = ET.Element('equivalent_sites')

    site_xml = ET.Element('site', {
        'pb_type': add_vpr_tile_prefix(site.type),
        'pin_mapping': 'custom'
    })
    for site_idx, site in enumerate(sites):
        if site.type != args.pb_type:
            continue

        for site_pin in site.site_pins:
            add_tile_direct(
                site_xml,
                tile=object_ref(
                    add_vpr_tile_prefix(args.tile_type),
                    site_pin.wire,
                ),
                pb_type=object_ref(
                    pb_name=add_vpr_tile_prefix(site.type),
                    pb_idx=site_idx,
                    pin_name=site_pin.name,
                ),
            )

    equivalent_sites_xml.append(site_xml)
    tile_xml.append(equivalent_sites_xml)
    add_switchblock_locations(tile_xml)

    with open(
            '{}/{}.tile.xml'.format(args.output_directory,
                                    args.tile_type.lower()), 'w') as f:
        tile_str = ET.tostring(tile_xml, pretty_print=True).decode('utf-8')
        f.write(tile_str)
Exemple #20
0
def main():
    parser = argparse.ArgumentParser(description="Generate synth_tiles.json")
    parser.add_argument(
            '--db_root', required=True)
    parser.add_argument(
            '--roi', required=True)
    parser.add_argument(
            '--synth_tiles', required=False)

    args = parser.parse_args()

    db = prjxray.db.Database(args.db_root)
    g = db.grid()

    synth_tiles = {}
    synth_tiles['tiles'] = {}

    with open(args.roi) as f:
        j = 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'],
            )

    synth_tiles['info'] = j['info']
    for port in j['ports']:
        if port['name'].startswith('dout['):
            port_type = 'input'
            is_clock = False
        elif port['name'].startswith('din['):
            is_clock = False
            port_type = 'output'
        elif port['name'].startswith('clk'):
            port_type = 'output'
            is_clock = True
        else:
            assert False, port

        tile, wire = port['wire'].split('/')

        # Make sure connecting wire is not in ROI!
        loc = g.loc_of_tilename(tile)
        if roi.tile_in_roi(loc):
            # Or if in the ROI, make sure it has no sites.
            gridinfo = g.gridinfo_at_tilename(tile)
            assert len(db.get_tile_type(gridinfo.tile_type).get_sites()) == 0, tile

        if tile not in synth_tiles['tiles']:
            synth_tiles['tiles'][tile] = {
                    'pins': [],
                    'loc': g.loc_of_tilename(tile),
            }

        synth_tiles['tiles'][tile]['pins'].append({
                'roi_name': port['name'].replace('[', '_').replace(']','_'),
                'wire': wire,
                'pad': port['pin'],
                'port_type': port_type,
                'is_clock': is_clock,
        })

    with open(args.synth_tiles, 'w') as f:
        json.dump(synth_tiles, f)
def main():
    parser = argparse.ArgumentParser(description="Generate synth_tiles.json")
    parser.add_argument('--db_root', required=True)
    parser.add_argument('--part', required=True)
    parser.add_argument('--roi', required=False)
    parser.add_argument('--overlay', required=False)
    parser.add_argument('--connection_database',
                        help='Connection database',
                        required=True)
    parser.add_argument('--synth_tiles', required=True)

    args = parser.parse_args()

    db = prjxray.db.Database(args.db_root, args.part)
    g = db.grid()

    synth_tiles = {}
    synth_tiles['tiles'] = {}

    rois = dict()
    if args.roi:
        with open(args.roi) as f:
            j = json.load(f)

        synth_tiles['info'] = j['info']

        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'],
        )

        rois[roi] = j
    elif args.overlay:
        with open(args.overlay) as f:
            j = json.load(f)

        synth_tiles['info'] = list()

        for r in j:
            roi = Roi(
                db=db,
                x1=r['info']['GRID_X_MIN'],
                y1=r['info']['GRID_Y_MIN'],
                x2=r['info']['GRID_X_MAX'],
                y2=r['info']['GRID_Y_MAX'],
            )

            rois[roi] = r
    else:
        assert False, 'Synth tiles must be for roi or overlay'

    with DatabaseCache(args.connection_database, read_only=True) as conn:
        tile_in_use = set()
        for roi, j in rois.items():
            if args.overlay:
                synth_tiles['info'].append(j['info'])

            tile_pin_count = dict()
            num_synth_tiles = 0
            for port in sorted(j['ports'],
                               key=lambda i: (i['type'], i['name'])):
                if port['type'] == 'out':
                    port_type = 'input' if not args.overlay else 'output'
                    is_clock = False
                elif port['type'] == 'in':
                    is_clock = False
                    port_type = 'output' if not args.overlay else 'input'
                elif port['type'] == 'clk':
                    port_type = 'output' if not args.overlay else 'input'
                    is_clock = True
                else:
                    assert False, port

                if 'wire' not in port:
                    tile, wire = find_wire_from_node(conn,
                                                     g,
                                                     roi,
                                                     port['node'],
                                                     overlay=bool(
                                                         args.overlay))
                else:
                    tile, wire = port['wire'].split('/')

                tile_in_use.add(tile)

                # Make sure connecting wire is not in ROI!
                loc = g.loc_of_tilename(tile)

                if bool(args.overlay) ^ roi.tile_in_roi(loc):
                    # Or if in the ROI, make sure it has no sites.
                    gridinfo = g.gridinfo_at_tilename(tile)
                    assert len(
                        db.get_tile_type(gridinfo.tile_type).get_sites()
                    ) == 0, "{}/{}".format(tile, wire)

                vpr_loc = map_tile_to_vpr_coord(conn, tile)

                if tile not in synth_tiles['tiles']:
                    tile_name = 'SYN-IOPAD-{}'.format(num_synth_tiles)
                    synth_tiles['tiles'][tile] = {
                        'pins': [],
                        'loc': vpr_loc,
                        'tile_name': tile_name,
                    }
                    num_synth_tiles += 1
                    tile_pin_count[tile] = 0

                synth_tiles['tiles'][tile]['pins'].append({
                    'roi_name':
                    port['name'].replace('[', '_').replace(']', '_'),
                    'wire':
                    wire,
                    'pad':
                    port['pin'],
                    'port_type':
                    port_type,
                    'is_clock':
                    is_clock,
                    'z_loc':
                    tile_pin_count[tile],
                })

                tile_pin_count[tile] += 1

        if not args.overlay:
            # Find two VBRK's in the corner of the fabric to use as the synthetic VCC/
            # GND source.
            vbrk_loc = None
            vbrk_tile = None
            vbrk2_loc = None
            vbrk2_tile = None
            for tile in g.tiles():
                if tile in tile_in_use:
                    continue

                loc = g.loc_of_tilename(tile)
                if not roi.tile_in_roi(loc):
                    continue

                gridinfo = g.gridinfo_at_tilename(tile)
                if 'VBRK' not in gridinfo.tile_type:
                    continue

                assert len(db.get_tile_type(
                    gridinfo.tile_type).get_sites()) == 0, tile

                if vbrk_loc is None:
                    vbrk2_loc = vbrk_loc
                    vbrk2_tile = vbrk_tile
                    vbrk_loc = loc
                    vbrk_tile = tile
                else:
                    if (loc.grid_x < vbrk_loc.grid_x and
                            loc.grid_y < vbrk_loc.grid_y) or vbrk2_loc is None:
                        vbrk2_loc = vbrk_loc
                        vbrk2_tile = vbrk_tile
                        vbrk_loc = loc
                        vbrk_tile = tile

            assert vbrk_loc is not None
            assert vbrk_tile is not None
            assert vbrk_tile not in synth_tiles['tiles']

            vbrk_vpr_loc = map_tile_to_vpr_coord(conn, vbrk_tile)
            synth_tiles['tiles'][vbrk_tile] = {
                'loc':
                vbrk_vpr_loc,
                'pins': [
                    {
                        'wire': 'VCC',
                        'pad': 'VCC',
                        'port_type': 'VCC',
                        'is_clock': False,
                        'z_loc': '0',
                    },
                ],
            }

            assert vbrk2_loc is not None
            assert vbrk2_tile is not None
            assert vbrk2_tile not in synth_tiles['tiles']
            vbrk2_vpr_loc = map_tile_to_vpr_coord(conn, vbrk2_tile)
            synth_tiles['tiles'][vbrk2_tile] = {
                'loc':
                vbrk2_vpr_loc,
                'pins': [
                    {
                        'wire': 'GND',
                        'pad': 'GND',
                        'port_type': 'GND',
                        'is_clock': False,
                        'z_loc': '0',
                    },
                ],
            }

    with open(args.synth_tiles, 'w') as f:
        json.dump(synth_tiles, f, indent=2)
Exemple #22
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
            '--db_root', help='Project X-Ray Database', required=True)
    parser.add_argument(
            '--connection_database', help='Database of fabric connectivity', required=True)
    parser.add_argument(
            '--pin_assignments', help='Output JSON assigning pins to tile types and direction connections', required=True)

    args = parser.parse_args()

    db = prjxray.db.Database(args.db_root)
    grid = db.grid()

    edge_assignments = {}

    wires_in_tile_types = set()

    for tile_type in db.get_tile_types():
        type_obj = db.get_tile_type(tile_type)

        for wire in type_obj.get_wires():
            wires_in_tile_types.add((tile_type, wire))

        for site in type_obj.get_sites():
            for site_pin in site.site_pins:
                if site_pin.wire is None:
                    continue

                key = (tile_type, site_pin.wire)
                assert key not in edge_assignments, key
                edge_assignments[key] = []

    with DatabaseCache(args.connection_database, read_only=True) as conn:

        direct_connections = set()
        print('{} Processing direct connections.'.format(now()))
        handle_direction_connections(conn, direct_connections, edge_assignments)

        wires_not_in_channels = {}
        c = conn.cursor()
        print('{} Processing non-channel nodes.'.format(now()))
        for node_pkey, classification in progressbar.progressbar(c.execute("""
    SELECT pkey, classification FROM node WHERE classification != ?;
    """, (NodeClassification.CHANNEL.value,))):
            reason = NodeClassification(classification)

            for (tile, tile_type, wire) in yield_wire_info_from_node(conn, node_pkey):
                key = (tile_type, wire)

                # Sometimes nodes in particular tile instances are disconnected,
                # disregard classification changes if this is the case.
                if reason != NodeClassification.NULL:
                    if key not in wires_not_in_channels:
                        wires_not_in_channels[key] = reason
                    else:
                        other_reason = wires_not_in_channels[key]
                        assert reason == other_reason, (tile, wire, reason, other_reason)

                if key in wires_in_tile_types:
                    wires_in_tile_types.remove(key)

        # List of nodes that are channels.
        channel_nodes = []

        # Map of (tile, wire) to track.  This will be used to find channels for pips
        # that come from EDGES_TO_CHANNEL.
        channel_wires_to_tracks = {}

        # Generate track models and verify that wires are either in a channel
        # or not in a channel.
        print('{} Creating models from tracks.'.format(now()))
        for node_pkey, track_pkey in progressbar.progressbar(c.execute("""
    SELECT pkey, track_pkey FROM node WHERE classification = ?;
    """, (NodeClassification.CHANNEL.value,))):
            assert track_pkey is not None

            tracks_model, _ = get_track_model(conn, track_pkey)
            channel_nodes.append(tracks_model)
            channel_wires_to_tracks[track_pkey] = tracks_model

            for (tile, tile_type, wire) in yield_wire_info_from_node(conn, node_pkey):
                tileinfo = grid.gridinfo_at_tilename(tile)
                key = (tileinfo.tile_type, wire)
                # Make sure all wires in channels always are in channels
                assert key not in wires_not_in_channels

                if key in wires_in_tile_types:
                    wires_in_tile_types.remove(key)

        # Make sure all wires appear to have been assigned.
        assert len(wires_in_tile_types) == 0

        # Verify that all tracks are sane.
        for node in channel_nodes:
            node.verify_tracks()

        null_tile_wires = set()

        # Verify that all nodes that are classified as edges to channels have at
        # least one site, and at least one live connection to a channel.
        #
        # If no live connections from the node are present, this node should've
        # been marked as NULL during channel formation.
        print('{} Handling edges to channels.'.format(now()))
        handle_edges_to_channels(conn, null_tile_wires, edge_assignments, channel_wires_to_tracks)

        print('{} Processing edge assignments.'.format(now()))
        final_edge_assignments = {}
        for key, available_pins in progressbar.progressbar(edge_assignments.items()):
            (tile_type, wire) = key
            if len(available_pins) == 0:
                if (tile_type, wire) not in null_tile_wires:
                    # TODO: Figure out what is going on with these wires.  Appear to
                    # tile internal connections sometimes?
                    print((tile_type, wire))

                final_edge_assignments[key] = [tracks.Direction.RIGHT]
                continue

            pins = set(available_pins[0])
            for p in available_pins[1:]:
                pins &= set(p)

            if len(pins) > 0:
                final_edge_assignments[key] = [list(pins)[0]]
            else:
                # More than 2 pins are required, final the minimal number of pins
                pins = set()
                for p in available_pins:
                    pins |= set(p)

                while len(pins) > 2:
                    pins = list(pins)

                    prev_len = len(pins)

                    for idx in range(len(pins)):
                        pins_subset = list(pins)
                        del pins_subset[idx]

                        pins_subset = set(pins_subset)

                        bad_subset = False
                        for p in available_pins:
                            if len(pins_subset & set(p)) == 0:
                                bad_subset = True
                                break

                        if not bad_subset:
                            pins = list(pins_subset)
                            break

                    # Failed to remove any pins, stop.
                    if len(pins) == prev_len:
                        break

                final_edge_assignments[key] = pins

        for key, available_pins in edge_assignments.items():
            (tile_type, wire) = key
            pins = set(final_edge_assignments[key])

            for required_pins in available_pins:
                assert len(pins & set(required_pins)) > 0, (
                        tile_type, wire, pins, required_pins)

        pin_directions = {}
        for key, pins in progressbar.progressbar(final_edge_assignments.items()):
            (tile_type, wire) = key
            if tile_type not in pin_directions:
                pin_directions[tile_type] = {}

            pin_directions[tile_type][wire] = [pin._name_ for pin in pins]

        with open(args.pin_assignments, 'w') as f:
            json.dump({
                'pin_directions': pin_directions,
                'direct_connections': [d._asdict() for d in direct_connections],
            }, f, indent=2)

        print('{} Flushing database back to file "{}"'.format(now(), args.connection_database))
Exemple #23
0
def main():
    mydir = os.path.dirname(__file__)
    prjxray_db = os.path.abspath(
        os.path.join(mydir, "..", "..", "third_party", "prjxray-db"))

    db_types = prjxray.db.get_available_databases(prjxray_db)

    parser = argparse.ArgumentParser(description=__doc__,
                                     fromfile_prefix_chars='@',
                                     prefix_chars='-~')

    parser.add_argument(
        '--part',
        choices=[os.path.basename(db_type) for db_type in db_types],
        help="""Project X-Ray database to use.""")

    parser.add_argument('--tile', help="""Tile to generate for""")

    parser.add_argument('--site_directory',
                        help="""Diretory where sites are defined""")

    parser.add_argument('--output-pb-type',
                        nargs='?',
                        type=argparse.FileType('w'),
                        default=sys.stdout,
                        help="""File to write the output too.""")

    parser.add_argument('--output-model',
                        nargs='?',
                        type=argparse.FileType('w'),
                        default=sys.stdout,
                        help="""File to write the output too.""")

    parser.add_argument('--pin_assignments',
                        required=True,
                        type=argparse.FileType('r'))

    parser.add_argument(
        '--site_types',
        required=True,
        help="Comma seperated list of site types to include in this tile.")

    parser.add_argument(
        '--fused_sites',
        action='store_true',
        help=
        "Typically a tile can treat the sites within the tile as independent.  For tiles where this is not true, fused sites only imports 1 primatative for the entire tile, which should be named the same as the tile type."
    )

    args = parser.parse_args()

    db = prjxray.db.Database(os.path.join(prjxray_db, args.part))

    tile = db.get_tile_type(args.tile)

    # Wires sink to a site within the tile are input wires.
    input_wires = set()

    # Wires source from a site within the tile are output wires.
    output_wires = set()

    if not args.fused_sites:
        site_type_instances = {}
        for s in args.site_types.split(','):
            site_type, site_type_instance = s.split('/')

            if site_type not in site_type_instances:
                site_type_instances[site_type] = []

            site_type_instances[site_type].append(site_type_instance)

        imported_site_types = set()
        ignored_site_types = set()

        for site in tile.get_sites():
            site_type = db.get_site_type(site.type)

            if site.type not in site_type_instances:
                ignored_site_types.add(site.type)
                continue

            imported_site_types.add(site.type)

            for site_pin in site.site_pins:
                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    if site_pin.wire is not None:
                        input_wires.add(site_pin.wire)
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    if site_pin.wire is not None:
                        output_wires.add(site_pin.wire)
                else:
                    assert False, site_type_pin.direction

        # Make sure all requested site types actually get imported.
        assert len(set(site_type_instances.keys()) -
                   imported_site_types) == 0, (site_type_instances.keys(),
                                               imported_site_types)

        for ignored_site_type in ignored_site_types:
            print(
                '*** WARNING *** Ignored site type {} in tile type {}'.format(
                    ignored_site_type, args.tile),
                file=sys.stderr)
    else:
        for site in tile.get_sites():
            site_type = db.get_site_type(site.type)

            for site_pin in site.site_pins:
                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    if site_pin.wire is not None:
                        input_wires.add(site_pin.wire)
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    if site_pin.wire is not None:
                        output_wires.add(site_pin.wire)
                else:
                    assert False, site_type_pin.direction

    site_model = args.site_directory + "/{0}/{1}.model.xml"
    site_pbtype = args.site_directory + "/{0}/{1}.pb_type.xml"

    xi_url = "http://www.w3.org/2001/XInclude"
    ET.register_namespace('xi', xi_url)
    xi_include = "{%s}include" % xi_url

    ##########################################################################
    # Generate the model.xml file                                            #
    ##########################################################################

    model_xml = ET.Element(
        'models',
        nsmap={'xi': xi_url},
    )

    def add_model_include(site_type, instance_name):
        ET.SubElement(
            model_xml, xi_include, {
                'href': site_model.format(site_type.lower(),
                                          instance_name.lower()),
                'xpointer': "xpointer(models/child::node())"
            })

    if not args.fused_sites:
        site_types = set(site.type for site in tile.get_sites())
        for site_type in site_types:
            if site_type in ignored_site_types:
                continue

            for instance in site_type_instances[site_type]:
                add_model_include(site_type, instance)
    else:
        fused_site_name = args.tile.lower()

        add_model_include(fused_site_name, fused_site_name)

    model_str = ET.tostring(model_xml, pretty_print=True).decode('utf-8')
    args.output_model.write(model_str)
    args.output_model.close()

    ##########################################################################
    # Generate the pb_type.xml file                                          #
    ##########################################################################

    def add_direct(xml, input, output):
        ET.SubElement(
            xml, 'direct', {
                'name': '{}_to_{}'.format(input, output),
                'input': input,
                'output': output
            })

    tile_name = "BLK_TI-{}".format(args.tile)

    pb_type_xml = ET.Element(
        'pb_type',
        {
            'name': tile_name,
        },
        nsmap={'xi': xi_url},
    )

    fc_xml = ET.SubElement(pb_type_xml, 'fc', {
        'in_type': 'abs',
        'in_val': '2',
        'out_type': 'abs',
        'out_val': '2',
    })

    interconnect_xml = ET.Element('interconnect')

    pb_type_xml.append(ET.Comment(" Tile Inputs "))

    # Input definitions for the TILE
    for name in sorted(input_wires):
        input_type = 'input'

        if 'CLK' in name:
            input_type = 'clock'

        ET.SubElement(
            pb_type_xml,
            input_type,
            {
                'name': name,
                'num_pins': '1'
            },
        )

    pb_type_xml.append(ET.Comment(" Tile Outputs "))
    for name in sorted(output_wires):
        # Output definitions for the TILE
        ET.SubElement(
            pb_type_xml,
            'output',
            {
                'name': name,
                'num_pins': '1'
            },
        )

    pb_type_xml.append(ET.Comment(" Internal Sites "))

    cell_names = {}

    if not args.fused_sites:
        site_type_count = {}
        site_prefixes = {}
        cells_idx = []

        site_type_ports = {}
        for idx, site in enumerate(tile.get_sites()):
            if site.type in ignored_site_types:
                continue

            if site.type not in site_type_count:
                site_type_count[site.type] = 0
                site_prefixes[site.type] = []

            cells_idx.append(site_type_count[site.type])
            site_type_count[site.type] += 1
            site_prefix = '{}_X{}'.format(site.type, site.x)

            site_instance = site_type_instances[site.type][cells_idx[idx]]

            print(site_prefix, site_instance)

            site_type_path = site_pbtype.format(site.type.lower(),
                                                site_instance.lower())
            cell_pb_type = ET.ElementTree()
            root_element = cell_pb_type.parse(site_type_path)
            cell_names[site_instance] = root_element.attrib['name']

            ports = {}
            for inputs in root_element.iter('input'):
                ports[inputs.attrib['name']] = int(inputs.attrib['num_pins'])

            for clocks in root_element.iter('clock'):
                ports[clocks.attrib['name']] = int(clocks.attrib['num_pins'])

            for outputs in root_element.iter('output'):
                ports[outputs.attrib['name']] = int(outputs.attrib['num_pins'])

            assert site_instance not in site_type_ports, (
                site_instance, site_type_ports.keys())
            site_type_ports[site_instance] = ports

            attrib = dict(root_element.attrib)
            include_xml = ET.SubElement(pb_type_xml, 'pb_type', attrib)
            ET.SubElement(
                include_xml, xi_include, {
                    'href':
                    site_type_path,
                    'xpointer':
                    "xpointer(pb_type/child::node()[local-name()!='metadata'])",
                })

            metadata_xml = ET.SubElement(include_xml, 'metadata')
            ET.SubElement(metadata_xml, 'meta', {
                'name': 'fasm_prefix',
            }).text = site_prefix

            # Import pb_type metadata if it exists.
            if any(child.tag == 'metadata' for child in root_element):
                ET.SubElement(
                    metadata_xml, xi_include, {
                        'href': site_type_path,
                        'xpointer': "xpointer(pb_type/metadata/child::node())",
                    })

        for idx, site in enumerate(tile.get_sites()):
            if site.type in ignored_site_types:
                continue

            site_idx = cells_idx[idx]
            site_instance = site_type_instances[site.type][site_idx]
            site_name = cell_names[site_instance]

            site_type = db.get_site_type(site.type)

            interconnect_xml.append(ET.Comment(" Tile->Site "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port = find_port(site_pin.name, site_type_ports[site_instance])
                if port is None:
                    print(
                        "*** WARNING *** Didn't find port for name {} for site type {}"
                        .format(site_pin.name, site.type),
                        file=sys.stderr)
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    add_direct(interconnect_xml,
                               input=object_ref(tile_name, site_pin.wire),
                               output=object_ref(site_name, **port))
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    pass
                else:
                    assert False, site_type_pin.direction

            interconnect_xml.append(ET.Comment(" Site->Tile "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port = find_port(site_pin.name, site_type_ports[site_instance])
                if port is None:
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    pass
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    add_direct(
                        interconnect_xml,
                        input=object_ref(site_name, **port),
                        output=object_ref(tile_name, site_pin.wire),
                    )
                else:
                    assert False, site_type_pin.direction
    else:
        site_type_ports = {}

        site_type_path = site_pbtype.format(fused_site_name, fused_site_name)
        cell_pb_type = ET.ElementTree()
        root_element = cell_pb_type.parse(site_type_path)

        ports = {}
        for inputs in root_element.iter('input'):
            ports[inputs.attrib['name']] = int(inputs.attrib['num_pins'])

        for clocks in root_element.iter('clock'):
            ports[clocks.attrib['name']] = int(clocks.attrib['num_pins'])

        for outputs in root_element.iter('output'):
            ports[outputs.attrib['name']] = int(outputs.attrib['num_pins'])

        attrib = dict(root_element.attrib)
        include_xml = ET.SubElement(pb_type_xml, 'pb_type', attrib)
        ET.SubElement(
            include_xml, xi_include, {
                'href': site_type_path,
                'xpointer': "xpointer(pb_type/child::node())",
            })

        site_name = root_element.attrib['name']

        def fused_port_name(site, site_pin):
            return '{}_{}_{}'.format(site.prefix, site.name, site_pin.name)

        for idx, site in enumerate(tile.get_sites()):
            site_type = db.get_site_type(site.type)

            interconnect_xml.append(ET.Comment(" Tile->Site "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port_name = fused_port_name(site, site_pin)
                port = find_port(port_name, ports)
                if port is None:
                    print(
                        "*** WARNING *** Didn't find port for name {} for site type {}"
                        .format(port_name, site.type),
                        file=sys.stderr)
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    add_direct(interconnect_xml,
                               input=object_ref(tile_name, site_pin.wire),
                               output=object_ref(site_name, **port))
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    pass
                else:
                    assert False, site_type_pin.direction

            interconnect_xml.append(ET.Comment(" Site->Tile "))
            for site_pin in sorted(site.site_pins,
                                   key=lambda site_pin: site_pin.name):
                if site_pin.wire is None:
                    continue

                port = find_port(fused_port_name(site, site_pin), ports)
                if port is None:
                    # Already warned above
                    continue

                site_type_pin = site_type.get_site_pin(site_pin.name)

                if site_type_pin.direction == prjxray.site_type.SitePinDirection.IN:
                    pass
                elif site_type_pin.direction == prjxray.site_type.SitePinDirection.OUT:
                    add_direct(
                        interconnect_xml,
                        input=object_ref(site_name, **port),
                        output=object_ref(tile_name, site_pin.wire),
                    )
                else:
                    assert False, site_type_pin.direction

    pb_type_xml.append(interconnect_xml)

    ET.SubElement(pb_type_xml, 'switchblock_locations', {
        'pattern': 'all',
    })

    pinlocations_xml = ET.SubElement(pb_type_xml, 'pinlocations', {
        'pattern': 'custom',
    })

    if len(input_wires) > 0 or len(output_wires) > 0:
        pin_assignments = json.load(args.pin_assignments)

        sides = {}
        for pin in input_wires | output_wires:
            for side in pin_assignments['pin_directions'][args.tile][pin]:
                if side not in sides:
                    sides[side] = []

                sides[side].append(object_ref(tile_name, pin))

        for side, pins in sides.items():
            ET.SubElement(pinlocations_xml, 'loc', {
                'side': side.lower(),
            }).text = ' '.join(pins)

    metadata_xml = ET.SubElement(pb_type_xml, 'metadata')
    ET.SubElement(metadata_xml, 'meta', {
        'name': 'fasm_prefix',
    }).text = args.tile

    direct_pins = set()
    for direct in pin_assignments['direct_connections']:
        if direct['from_pin'].split('.')[0] == args.tile:
            direct_pins.add(direct['from_pin'].split('.')[1])

        if direct['to_pin'].split('.')[0] == args.tile:
            direct_pins.add(direct['to_pin'].split('.')[1])

    for fc_override in direct_pins:
        ET.SubElement(fc_xml, 'fc_override', {
            'fc_type': 'frac',
            'fc_val': '0.0',
            'port_name': fc_override,
        })

    pb_type_str = ET.tostring(pb_type_xml, pretty_print=True).decode('utf-8')
    args.output_pb_type.write(pb_type_str)
    args.output_pb_type.close()
Exemple #24
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--db_root',
                        required=True,
                        help='Project X-Ray Database')
    parser.add_argument('--connection_database',
                        help='Database of fabric connectivity',
                        required=True)
    parser.add_argument('--pin_assignments',
                        help='Pin assignments JSON',
                        required=True)
    parser.add_argument(
        '--synth_tiles',
        help=
        'If using an ROI, synthetic tile defintion from prjxray-arch-import')

    args = parser.parse_args()

    pool = multiprocessing.Pool(20)

    db = prjxray.db.Database(args.db_root)
    grid = db.grid()

    with DatabaseCache(args.connection_database) as conn:

        with open(args.pin_assignments) as f:
            pin_assignments = json.load(f)

        tile_wires = []
        for tile_type, wire_map in pin_assignments['pin_directions'].items():
            for wire in wire_map.keys():
                tile_wires.append((tile_type, wire))

        for tile_type, wire in progressbar.progressbar(tile_wires):
            pins = [
                direction_to_enum(pin)
                for pin in pin_assignments['pin_directions'][tile_type][wire]
            ]
            add_graph_nodes_for_pins(conn, tile_type, wire, pins)

        if args.synth_tiles:
            use_roi = True
            with open(args.synth_tiles) as f:
                synth_tiles = json.load(f)

            roi = Roi(
                db=db,
                x1=synth_tiles['info']['GRID_X_MIN'],
                y1=synth_tiles['info']['GRID_Y_MIN'],
                x2=synth_tiles['info']['GRID_X_MAX'],
                y2=synth_tiles['info']['GRID_Y_MAX'],
            )

            print('{} generating routing graph for ROI.'.format(now()))
        else:
            use_roi = False

        output_only_nodes = set()
        input_only_nodes = set()

        find_pip = create_find_pip(conn)
        find_wire = create_find_wire(conn)
        find_connector = create_find_connector(conn)

        print('{} Finding nodes belonging to ROI'.format(now()))
        if use_roi:
            for loc in progressbar.progressbar(grid.tile_locations()):
                gridinfo = grid.gridinfo_at_loc(loc)
                tile_name = grid.tilename_at_loc(loc)

                if tile_name in synth_tiles['tiles']:
                    assert len(synth_tiles['tiles'][tile_name]['pins']) == 1
                    for pin in synth_tiles['tiles'][tile_name]['pins']:
                        _, _, node_pkey = find_wire(tile_name,
                                                    gridinfo.tile_type,
                                                    pin['wire'])

                        if pin['port_type'] == 'input':
                            # This track can output be used as a sink.
                            input_only_nodes |= set((node_pkey, ))
                        elif pin['port_type'] == 'output':
                            # This track can output be used as a src.
                            output_only_nodes |= set((node_pkey, ))
                        else:
                            assert False, pin

        c = conn.cursor()
        c.execute('SELECT pkey FROM switch WHERE name = ?;', ('routing', ))
        switch_pkey = c.fetchone()[0]

        edges = []

        edge_set = set()

        for loc in progressbar.progressbar(grid.tile_locations()):
            gridinfo = grid.gridinfo_at_loc(loc)
            tile_name = grid.tilename_at_loc(loc)

            # Not a synth node, check if in ROI.
            if use_roi and not roi.tile_in_roi(loc):
                continue

            tile_type = db.get_tile_type(gridinfo.tile_type)

            for pip in tile_type.get_pips():
                if pip.is_pseudo:
                    continue

                if not pip.is_directional:
                    # TODO: Handle bidirectional pips?
                    continue

                connections = make_connection(
                    conn=conn,
                    input_only_nodes=input_only_nodes,
                    output_only_nodes=output_only_nodes,
                    find_pip=find_pip,
                    find_wire=find_wire,
                    find_connector=find_connector,
                    tile_name=tile_name,
                    loc=loc,
                    tile_type=gridinfo.tile_type,
                    pip=pip,
                    switch_pkey=switch_pkey)
                if connections:
                    # TODO: Skip duplicate connections, until they have unique
                    # switches
                    for connection in connections:
                        key = tuple(connection[0:3])
                        if key in edge_set:
                            continue

                        edge_set.add(key)

                        edges.append(connection)

        print('{} Created {} edges, inserting'.format(now(), len(edges)))

        c.execute("""BEGIN EXCLUSIVE TRANSACTION;""")
        for edge in progressbar.progressbar(edges):
            c.execute(
                """
                    INSERT INTO graph_edge(
                        src_graph_node_pkey, dest_graph_node_pkey, switch_pkey,
                        tile_pkey, pip_in_tile_pkey)  VALUES (?, ?, ?, ?, ?)""",
                edge)

        c.execute("""COMMIT TRANSACTION;""")

        print('{} Inserted edges'.format(now()))

        c.execute(
            """CREATE INDEX src_node_index ON graph_edge(src_graph_node_pkey);"""
        )
        c.execute(
            """CREATE INDEX dest_node_index ON graph_edge(dest_graph_node_pkey);"""
        )
        c.connection.commit()

        print('{} Indices created, marking track liveness'.format(now()))
        mark_track_liveness(conn, pool, input_only_nodes, output_only_nodes)

        print('{} Flushing database back to file "{}"'.format(
            now(), args.connection_database))
Exemple #25
0
    def classify_node(self, db, grid, wire_to_nodes):
        if len(self.pips) + len(self.sites) <= 1:
            # Some nodes don't go anywhere.
            return NodeClassification(type='NULL', edge_with_mux=None)

        if len(self.pips) == 1 and len(self.sites) == 0:
            # Some nodes don't go anywhere.
            return NodeClassification(type='NULL', edge_with_mux=None)

        if len(self.pips) > 1:
            if len(self.sites) == 0:
                return NodeClassification(type='CHANNEL', edge_with_mux=None)
            else:
                if len(self.sites) == 1:
                    return NodeClassification(type='EDGES_TO_CHANNEL',
                                              edge_with_mux=None)
                else:
                    assert False, (self.pips, self.sites, self.node)

        if len(self.sites) == 2 and len(self.pips) == 0:
            return NodeClassification(type='EDGE_WITH_SHORT',
                                      edge_with_mux=None)

        if len(self.sites) == 1 and len(self.pips) == 1:
            # This could be a site pin -> pip -> site pin, check.
            tile, pip, wire = self.pips[0]

            tileinfo = grid.gridinfo_at_tilename(tile)
            tile_type = db.get_tile_type(tileinfo.tile_type)

            for tile_pip in tile_type.get_pips():
                if tile_pip.name == pip:
                    if tile_pip.net_to == wire:
                        other_wire = tile_pip.net_from
                        other_node = wire_to_nodes[(tile, other_wire)]

                        source_node = other_node.node
                        destination_node = self.node
                    elif tile_pip.net_from == wire:
                        other_wire = tile_pip.net_to
                        other_node = wire_to_nodes[(tile, other_wire)]

                        source_node = self.node
                        destination_node = other_node.node
                    else:
                        assert False, (tile, pip, wire, tile_pip)

                    if len(other_node.sites) == 1 and len(
                            other_node.pips) == 1:
                        edge_with_mux = EdgeWithMux(
                            source_node=tuple(sorted(source_node)),
                            pip=pip,
                            destination_node=tuple(sorted(destination_node)),
                        )
                        return NodeClassification(type='EDGE_WITH_MUX',
                                                  edge_with_mux=edge_with_mux)
                    elif len(other_node.sites) == 0 and len(
                            other_node.pips) == 1:
                        # Sometimes (e.g. top of carry chain) will end up with
                        # site pin -> pip -> nothing.
                        return NodeClassification(type='NULL',
                                                  edge_with_mux=None)
                    else:
                        break

            # This node is an edge to/from a channel, but not a
            # channel itself.
            return NodeClassification(type='EDGES_TO_CHANNEL',
                                      edge_with_mux=None)

        assert False, (self.pips, self.sites, self.node)
Exemple #26
0
def main():
    parser = argparse.ArgumentParser(description="Generate synth_tiles.json")
    parser.add_argument('--db_root', required=True)
    parser.add_argument('--part', required=True)
    parser.add_argument('--roi', required=True)
    parser.add_argument('--connection_database',
                        help='Connection database',
                        required=True)
    parser.add_argument('--synth_tiles', required=True)

    args = parser.parse_args()

    db = prjxray.db.Database(args.db_root, args.part)
    g = db.grid()

    synth_tiles = {}
    synth_tiles['tiles'] = {}

    with open(args.roi) as f:
        j = 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'],
    )

    with DatabaseCache(args.connection_database, read_only=True) as conn:
        synth_tiles['info'] = j['info']
        vbrk_in_use = set()
        for port in j['ports']:
            if port['name'].startswith('dout['):
                port_type = 'input'
                is_clock = False
            elif port['name'].startswith('din['):
                is_clock = False
                port_type = 'output'
            elif port['name'].startswith('clk'):
                port_type = 'output'
                is_clock = True
            else:
                assert False, port

            tile, wire = port['wire'].split('/')

            vbrk_in_use.add(tile)

            # Make sure connecting wire is not in ROI!
            loc = g.loc_of_tilename(tile)
            if roi.tile_in_roi(loc):
                # Or if in the ROI, make sure it has no sites.
                gridinfo = g.gridinfo_at_tilename(tile)
                assert len(db.get_tile_type(
                    gridinfo.tile_type).get_sites()) == 0, tile

            vpr_loc = map_tile_to_vpr_coord(conn, tile)

            if tile not in synth_tiles['tiles']:
                synth_tiles['tiles'][tile] = {
                    'pins': [],
                    'loc': vpr_loc,
                }

            synth_tiles['tiles'][tile]['pins'].append({
                'roi_name':
                port['name'].replace('[', '_').replace(']', '_'),
                'wire':
                wire,
                'pad':
                port['pin'],
                'port_type':
                port_type,
                'is_clock':
                is_clock,
            })

        # Find two VBRK's in the corner of the fabric to use as the synthetic VCC/
        # GND source.
        vbrk_loc = None
        vbrk_tile = None
        vbrk2_loc = None
        vbrk2_tile = None
        for tile in g.tiles():
            if tile in vbrk_in_use:
                continue

            loc = g.loc_of_tilename(tile)
            if not roi.tile_in_roi(loc):
                continue

            gridinfo = g.gridinfo_at_tilename(tile)
            if 'VBRK' not in gridinfo.tile_type:
                continue

            assert len(db.get_tile_type(
                gridinfo.tile_type).get_sites()) == 0, tile

            if vbrk_loc is None:
                vbrk2_loc = vbrk_loc
                vbrk2_tile = vbrk_tile
                vbrk_loc = loc
                vbrk_tile = tile
            else:
                if (loc.grid_x < vbrk_loc.grid_x
                        and loc.grid_y < vbrk_loc.grid_y) or vbrk2_loc is None:
                    vbrk2_loc = vbrk_loc
                    vbrk2_tile = vbrk_tile
                    vbrk_loc = loc
                    vbrk_tile = tile

        assert vbrk_loc is not None
        assert vbrk_tile is not None
        assert vbrk_tile not in synth_tiles['tiles']

        vbrk_vpr_loc = map_tile_to_vpr_coord(conn, vbrk_tile)
        synth_tiles['tiles'][vbrk_tile] = {
            'loc':
            vbrk_vpr_loc,
            'pins': [
                {
                    'wire': 'VCC',
                    'pad': 'VCC',
                    'port_type': 'VCC',
                    'is_clock': False,
                },
            ],
        }

        assert vbrk2_loc is not None
        assert vbrk2_tile is not None
        assert vbrk2_tile not in synth_tiles['tiles']
        vbrk2_vpr_loc = map_tile_to_vpr_coord(conn, vbrk2_tile)
        synth_tiles['tiles'][vbrk2_tile] = {
            'loc':
            vbrk2_vpr_loc,
            'pins': [
                {
                    'wire': 'GND',
                    'pad': 'GND',
                    'port_type': 'GND',
                    'is_clock': False,
                },
            ],
        }

    with open(args.synth_tiles, 'w') as f:
        json.dump(synth_tiles, f, indent=2)
Exemple #27
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--db_root',
                        help='Project X-Ray Database',
                        required=True)
    parser.add_argument('--channels',
                        help='Input JSON defining channel assignments',
                        required=True)
    parser.add_argument(
        '--pin_assignments',
        help=
        'Output JSON assigning pins to tile types and direction connections',
        required=True)

    args = parser.parse_args()

    db = prjxray.db.Database(args.db_root)
    grid = db.grid()

    edge_assignments = {}

    wires_in_tile_types = set()

    for tile_type in db.get_tile_types():
        type_obj = db.get_tile_type(tile_type)

        for wire in type_obj.get_wires():
            wires_in_tile_types.add((tile_type, wire))

        for site in type_obj.get_sites():
            for site_pin in site.site_pins:
                if site_pin.wire is None:
                    continue

                key = (tile_type, site_pin.wire)
                assert key not in edge_assignments, key
                edge_assignments[key] = []

    print('{} Reading channel data'.format(datetime.datetime.now()))
    with open(args.channels) as f:
        channels = json.load(f)
    print('{} Done reading channel data'.format(datetime.datetime.now()))

    direct_connections = set()

    # Edges with mux should have one source tile and one destination_tile.
    # The pin from the source_tile should face the destination_tile.
    #
    # It is expected that all edges_with_mux will lies in a line (e.g. X only or
    # Y only).
    for edge_with_mux in progressbar.progressbar(channels['edges_with_mux']):
        source_tile = None
        source_tile_type = None
        source_wire = None
        destination_tile = None
        destination_tile_type = None
        destination_wire = None

        for tile, wire in edge_with_mux['source_node']:
            tileinfo = grid.gridinfo_at_tilename(tile)
            tile_type = db.get_tile_type(tileinfo.tile_type)
            wire_info = tile_type.get_wire_info(wire)

            if len(wire_info.sites) == 1:
                assert source_tile is None, (tile, wire, source_tile)
                source_tile = tile
                source_tile_type = tileinfo.tile_type
                source_wire = wire

        for tile, wire in edge_with_mux['destination_node']:
            tileinfo = grid.gridinfo_at_tilename(tile)
            tile_type = db.get_tile_type(tileinfo.tile_type)
            wire_info = tile_type.get_wire_info(wire)

            if len(wire_info.sites) == 1:
                assert destination_tile is None, (tile, wire, destination_tile,
                                                  wire_info)
                destination_tile = tile
                destination_tile_type = tileinfo.tile_type
                destination_wire = wire

        assert source_tile is not None
        assert destination_tile is not None

        source_loc = grid.loc_of_tilename(source_tile)
        destination_loc = grid.loc_of_tilename(destination_tile)

        assert source_loc.grid_x == destination_loc.grid_x or source_loc.grid_y == destination_loc.grid_y, (
            source_tile, destination_tile, edge_with_mux['pip'])

        direct_connections.add(
            DirectConnection(
                from_pin='{}.{}'.format(source_tile_type, source_wire),
                to_pin='{}.{}'.format(destination_tile_type, destination_wire),
                switch_name='routing',
                x_offset=destination_loc.grid_x - source_loc.grid_x,
                y_offset=destination_loc.grid_y - source_loc.grid_y,
            ))

        if destination_loc.grid_x == source_loc.grid_x:
            if destination_loc.grid_y > source_loc.grid_y:
                source_dir = tracks.Direction.TOP
                destination_dir = tracks.Direction.BOTTOM
            else:
                source_dir = tracks.Direction.BOTTOM
                destination_dir = tracks.Direction.TOP
        else:
            if destination_loc.grid_x > source_loc.grid_x:
                source_dir = tracks.Direction.RIGHT
                destination_dir = tracks.Direction.LEFT
            else:
                source_dir = tracks.Direction.LEFT
                destination_dir = tracks.Direction.RIGHT

        edge_assignments[(source_tile_type, source_wire)].append(
            (source_dir, ))
        edge_assignments[(destination_tile_type, destination_wire)].append(
            (destination_dir, ))

    wires_not_in_channels = {}
    for node in progressbar.progressbar(channels['node_not_in_channels']):
        reason = node['classification']

        for tile, wire in node['wires']:
            tileinfo = grid.gridinfo_at_tilename(tile)
            key = (tileinfo.tile_type, wire)

            # Sometimes nodes in particular tile instances are disconnected,
            # disregard classification changes if this is the case.
            if reason != 'NULL':
                if key not in wires_not_in_channels:
                    wires_not_in_channels[key] = reason
                else:
                    other_reason = wires_not_in_channels[key]
                    assert reason == other_reason, (tile, wire, reason,
                                                    other_reason)

            if key in wires_in_tile_types:
                wires_in_tile_types.remove(key)

    # List of nodes that are channels.
    channel_nodes = []

    # Map of (tile, wire) to track.  This will be used to find channels for pips
    # that come from EDGES_TO_CHANNEL.
    channel_wires_to_tracks = {}

    # Generate track models and verify that wires are either in a channel
    # or not in a channel.
    for channel in progressbar.progressbar(channels['channels']):
        track_list = []
        for track in channel['tracks']:
            track_list.append(tracks.Track(**track))

        tracks_model = tracks.Tracks(track_list, channel['track_connections'])
        channel_nodes.append(tracks_model)

        for tile, wire in channel['wires']:
            tileinfo = grid.gridinfo_at_tilename(tile)
            key = (tileinfo.tile_type, wire)
            # Make sure all wires in channels always are in channels
            assert key not in wires_not_in_channels

            if key in wires_in_tile_types:
                wires_in_tile_types.remove(key)

            channel_wires_to_tracks[(tile, wire)] = tracks_model

    # Make sure all wires appear to have been assigned.
    assert len(wires_in_tile_types) == 0

    # Verify that all tracks are sane.
    for node in channel_nodes:
        node.verify_tracks()

    null_tile_wires = set()

    # Verify that all nodes that are classified as edges to channels have at
    # least one site, and at least one live connection to a channel.
    #
    # If no live connections from the node are present, this node should've
    # been marked as NULL during channel formation.
    for node in progressbar.progressbar(channels['node_not_in_channels']):
        reason = node['classification']

        assert reason != 'EDGE_WITH_SHORT'

        if reason == 'NULL':
            for tile, wire in node['wires']:
                tileinfo = grid.gridinfo_at_tilename(tile)
                tile_type = db.get_tile_type(tileinfo.tile_type)
                null_tile_wires.add((tileinfo.tile_type, wire))

        if reason == 'EDGES_TO_CHANNEL':

            num_sites = 0

            for tile, wire in node['wires']:
                tileinfo = grid.gridinfo_at_tilename(tile)
                loc = grid.loc_of_tilename(tile)
                tile_type = db.get_tile_type(tileinfo.tile_type)

                wire_info = tile_type.get_wire_info(wire)
                num_sites += len(wire_info.sites)
                for pip in wire_info.pips:
                    other_wire = prjxray.tile.get_other_wire_from_pip(
                        tile_type.get_pip_by_name(pip), wire)

                    key = (tile, other_wire)
                    if key in channel_wires_to_tracks:
                        tracks_model = channel_wires_to_tracks[key]

                        if len(wire_info.sites) > 0:
                            available_pins = set(
                                pin_dir for _, pin_dir in tracks_model.
                                get_tracks_for_wire_at_coord((loc.grid_x,
                                                              loc.grid_y)))
                            edge_assignments[(tileinfo.tile_type,
                                              wire)].append(available_pins)

    final_edge_assignments = {}
    for (tile_type, wire), available_pins in progressbar.progressbar(
            edge_assignments.items()):
        if len(available_pins) == 0:
            if (tile_type, wire) not in null_tile_wires:
                # TODO: Figure out what is going on with these wires.  Appear to
                # tile internal connections sometimes?
                print((tile_type, wire))

            final_edge_assignments[(tile_type,
                                    wire)] = [tracks.Direction.RIGHT]
            continue

        pins = set(available_pins[0])
        for p in available_pins[1:]:
            pins &= set(p)

        if len(pins) > 0:
            final_edge_assignments[(tile_type, wire)] = [list(pins)[0]]
        else:
            # More than 2 pins are required, final the minimal number of pins
            pins = set()
            for p in available_pins:
                pins |= set(p)

            while len(pins) > 2:
                pins = list(pins)

                prev_len = len(pins)

                for idx in range(len(pins)):
                    pins_subset = list(pins)
                    del pins_subset[idx]

                    pins_subset = set(pins_subset)

                    bad_subset = False
                    for p in available_pins:
                        if len(pins_subset & set(p)) == 0:
                            bad_subset = True
                            break

                    if not bad_subset:
                        pins = list(pins_subset)
                        break

                # Failed to remove any pins, stop.
                if len(pins) == prev_len:
                    break

            final_edge_assignments[(tile_type, wire)] = pins

    for (tile_type, wire), available_pins in edge_assignments.items():
        pins = set(final_edge_assignments[(tile_type, wire)])

        for required_pins in available_pins:
            assert len(pins & set(required_pins)) > 0, (tile_type, wire, pins,
                                                        required_pins)

    pin_directions = {}
    for (tile_type, wire), pins in final_edge_assignments.items():
        if tile_type not in pin_directions:
            pin_directions[tile_type] = {}

        pin_directions[tile_type][wire] = [pin._name_ for pin in pins]

    with open(args.pin_assignments, 'w') as f:
        json.dump(
            {
                'pin_directions': pin_directions,
                'direct_connections':
                [d._asdict() for d in direct_connections],
            },
            f,
            indent=2)