| #!/usr/bin/env python3 | |
| """ | |
| Import the top level tile interconnect information from Project X-Ray database | |
| files. | |
| The Project X-Ray specifies the connections between tiles and the connect | |
| between tiles and their sites. prjxray_tile_import generates a VPR pb_type | |
| that has the correct tile pin names to connect to the routing fabric, and | |
| connects those tile pins to each site within the tile. | |
| """ | |
| from __future__ import print_function | |
| import argparse | |
| import os | |
| import sys | |
| import prjxray.db | |
| import prjxray.site_type | |
| import os.path | |
| import simplejson as json | |
| import re | |
| import sqlite3 | |
| import lxml.etree as ET | |
| from lib.pb_type_xml import ( | |
| start_pb_type, start_tile, add_vpr_tile_prefix, add_tile_direct, | |
| object_ref, add_switchblock_locations, write_xml, ModelXml, add_direct, | |
| XI_INCLUDE, XI_URL | |
| ) | |
| def find_port(pin_name, ports): | |
| if pin_name in ports: | |
| return { | |
| 'pin_name': pin_name, | |
| 'pin_idx': None, | |
| } | |
| # Find trailing digits, which are assumed to be pin indicies, example: | |
| # ADDRARDADDR13 | |
| m = re.search('([0-9]+)$', pin_name) | |
| if m is None: | |
| return None | |
| prefix = pin_name[:-len(m.group(1))] | |
| prefix_pin_idx = int(m.group(1)) | |
| if prefix in ports and prefix_pin_idx < ports[prefix]: | |
| return { | |
| 'pin_name': prefix, | |
| 'pin_idx': prefix_pin_idx, | |
| } | |
| else: | |
| return None | |
| def parse_site_type_instance(site_types): | |
| """ Convert site_types argument into map. | |
| Parameters | |
| ---------- | |
| site_types : str | |
| Returns | |
| ------- | |
| site_type_instances : map of str to list of str | |
| Maps site type to array of site type instances. First instance of | |
| site should use first element, second instance should use second | |
| element, etc. | |
| """ | |
| site_type_instances = {} | |
| for s in 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) | |
| return site_type_instances | |
| def expand_nodes_in_tile_type(conn, tile_type_pkey): | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| CREATE TEMP TABLE phy_tiles AS | |
| SELECT tile_map.phy_tile_pkey | |
| FROM tile_map | |
| INNER JOIN phy_tile | |
| ON phy_tile.pkey = tile_map.phy_tile_pkey | |
| WHERE tile_map.tile_pkey IN ( | |
| SELECT pkey FROM tile WHERE tile_type_pkey = ? | |
| ) | |
| GROUP BY phy_tile.tile_type_pkey;""", (tile_type_pkey, ) | |
| ) | |
| # Set of node pkeys to expand | |
| cur.execute( | |
| """ | |
| SELECT DISTINCT node_pkey | |
| FROM wire | |
| WHERE phy_tile_pkey IN (SELECT phy_tile_pkey FROM phy_tiles);""" | |
| ) | |
| nodes_to_expand = set(node_pkey for (node_pkey, ) in cur) | |
| nodes_to_set = {} | |
| while len(nodes_to_expand) > 0: | |
| node_pkey = nodes_to_expand.pop() | |
| node_set = nodes_to_set.get(node_pkey, set((node_pkey, ))) | |
| nodes_to_set[node_pkey] = node_set | |
| cur.execute( | |
| """ | |
| WITH | |
| other_wires(phy_tile_pkey, pip_in_tile_pkey, other_wire_in_tile_pkey) AS ( | |
| SELECT | |
| wire.phy_tile_pkey, | |
| undirected_pips.pip_in_tile_pkey, | |
| undirected_pips.other_wire_in_tile_pkey | |
| FROM undirected_pips | |
| INNER JOIN wire | |
| ON wire.wire_in_tile_pkey = undirected_pips.wire_in_tile_pkey | |
| INNER JOIN pip_in_tile | |
| ON pip_in_tile.pkey = undirected_pips.pip_in_tile_pkey | |
| WHERE | |
| wire.node_pkey = ? | |
| AND | |
| NOT pip_in_tile.is_pseudo | |
| ) | |
| SELECT wire.node_pkey | |
| FROM wire | |
| INNER JOIN other_wires | |
| ON | |
| wire.wire_in_tile_pkey = other_wires.other_wire_in_tile_pkey | |
| AND | |
| wire.phy_tile_pkey = other_wires.phy_tile_pkey | |
| WHERE | |
| wire.phy_tile_pkey IN (SELECT phy_tile_pkey FROM phy_tiles);""", | |
| (node_pkey, ) | |
| ) | |
| # Update node sets | |
| for (other_node_pkey, ) in cur: | |
| node_set |= nodes_to_set.get( | |
| other_node_pkey, set((other_node_pkey, )) | |
| ) | |
| # Merge node sets | |
| for node_pkey in node_set: | |
| nodes_to_set[node_pkey] = node_set | |
| unique_node_sets = {} | |
| for node_set in nodes_to_set.values(): | |
| if id(node_set) not in unique_node_sets: | |
| unique_node_sets[id(node_set)] = node_set | |
| if False: | |
| # Print node sets for debugging and inspection. | |
| for node_set in unique_node_sets.values(): | |
| print() | |
| print(id(node_set), len(node_set)) | |
| for node_pkey in node_set: | |
| cur.execute( | |
| """ | |
| SELECT name | |
| FROM wire_in_tile | |
| WHERE pkey IN ( | |
| SELECT wire.wire_in_tile_pkey | |
| FROM wire | |
| WHERE node_pkey = ? | |
| )""", (node_pkey, ) | |
| ) | |
| print(node_pkey, ' '.join(name for (name, ) in cur)) | |
| cur.execute("""DROP TABLE phy_tiles""") | |
| return unique_node_sets.values() | |
| def find_connections(conn, input_wires, output_wires, tile_type_pkey): | |
| """ Remove top-level ports only connected to internal sources and generates | |
| tile internal connections.""" | |
| # Create connected node sets | |
| node_sets = list(expand_nodes_in_tile_type(conn, tile_type_pkey)) | |
| # Number of site external connections for ports | |
| is_top_level_pin_external = {} | |
| for wire in input_wires: | |
| is_top_level_pin_external[wire] = False | |
| for wire in output_wires: | |
| is_top_level_pin_external[wire] = False | |
| internal_connections = {} | |
| top_level_connections = {} | |
| wire_to_site_pin = {} | |
| cur = conn.cursor() | |
| for node_set in node_sets: | |
| ipin_count = 0 | |
| opin_count = 0 | |
| node_set_has_external = False | |
| pins_used_in_node_sets = set() | |
| input_pins = set() | |
| output_pins = set() | |
| for node_pkey in node_set: | |
| cur.execute( | |
| """ | |
| SELECT | |
| tile.tile_type_pkey, | |
| wire_in_tile.name, | |
| site_type.name, | |
| site_pin.name, | |
| site_pin.direction, | |
| count() | |
| FROM wire | |
| INNER JOIN wire_in_tile | |
| ON wire.wire_in_tile_pkey = wire_in_tile.pkey | |
| INNER JOIN site_pin | |
| ON site_pin.pkey = wire_in_tile.site_pin_pkey | |
| INNER JOIN tile | |
| ON wire.tile_pkey = tile.pkey | |
| INNER JOIN site_type | |
| ON site_type.pkey = site_pin.site_type_pkey | |
| WHERE | |
| wire.node_pkey = ? | |
| AND | |
| wire_in_tile.site_pin_pkey IS NOT NULL | |
| GROUP BY site_pin.direction; | |
| """, (node_pkey, ) | |
| ) | |
| for (wire_tile_type_pkey, wire_name, site_type_name, site_pin_name, | |
| direction, count) in cur: | |
| if wire_tile_type_pkey == tile_type_pkey: | |
| value = (site_type_name, site_pin_name) | |
| if wire_name in wire_to_site_pin: | |
| assert value == wire_to_site_pin[wire_name], ( | |
| wire_name, value, wire_to_site_pin[wire_name] | |
| ) | |
| else: | |
| wire_to_site_pin[wire_name] = value | |
| pins_used_in_node_sets.add(wire_name) | |
| direction = prjxray.site_type.SitePinDirection(direction) | |
| if direction == prjxray.site_type.SitePinDirection.IN: | |
| output_pins.add(wire_name) | |
| opin_count += count | |
| elif direction == prjxray.site_type.SitePinDirection.OUT: | |
| input_pins.add(wire_name) | |
| ipin_count += count | |
| else: | |
| assert False, (node_pkey, direction) | |
| else: | |
| node_set_has_external = True | |
| assert len(input_pins) in [0, 1], input_pins | |
| if ipin_count == 0 or opin_count == 0 or node_set_has_external: | |
| # This node set is connected externally, mark as such | |
| for wire_name in pins_used_in_node_sets: | |
| is_top_level_pin_external[wire_name] = True | |
| if ipin_count > 0 and opin_count > 0: | |
| # TODO: Add check that pips and site pins on these internal | |
| # connections are 0 delay. | |
| assert len(input_pins) == 1 | |
| input_wire = input_pins.pop() | |
| for wire_name in output_pins: | |
| if wire_name in internal_connections: | |
| assert input_wire == internal_connections[wire_name] | |
| else: | |
| internal_connections[wire_name] = input_wire | |
| for wire in sorted(is_top_level_pin_external): | |
| if not is_top_level_pin_external[wire]: | |
| if wire in input_wires: | |
| input_wires.remove(wire) | |
| elif wire in output_wires: | |
| output_wires.remove(wire) | |
| else: | |
| assert False, wire | |
| else: | |
| top_level_connections[wire] = wire_to_site_pin[wire] | |
| output_internal_connections = {} | |
| for output_wire in internal_connections: | |
| output_internal_connections[wire_to_site_pin[output_wire]] = \ | |
| wire_to_site_pin[internal_connections[output_wire]] | |
| return top_level_connections, output_internal_connections | |
| NORMALIZED_TILE_TYPES = { | |
| "RIOI3": "IOI3_TILE", | |
| "RIOI3_SING": "IOI3_TILE", | |
| "RIOI3_TBYTESRC": "IOI3_TILE", | |
| "RIOI3_TBYTETERM": "IOI3_TILE", | |
| "LIOI3": "IOI3_TILE", | |
| "LIOI3_SING": "IOI3_TILE", | |
| "LIOI3_TBYTESRC": "IOI3_TILE", | |
| "LIOI3_TBYTETERM": "IOI3_TILE", | |
| "LIOB33": "IOB_TILE", | |
| "LIOB33_SING": "IOB_TILE", | |
| "RIOB33": "IOB_TILE", | |
| "RIOB33_SING": "IOB_TILE", | |
| "CMT_TOP_R_UPPER_T": "CMT_TOP_UPPER_T", | |
| "CMT_TOP_L_UPPER_T": "CMT_TOP_UPPER_T", | |
| } | |
| NORMALIZED_SITE_TYPES = { | |
| "IOB33M": "IOB", | |
| "IOB33S": "IOB", | |
| "IOB33": "IOB", | |
| "ILOGICE3": "ILOGIC", | |
| "OLOGICE3": "OLOGIC", | |
| "IDELAYE2": "IDELAY", | |
| } | |
| def get_tile_prefixes(conn, tile_type_pkeys, site_types, site_remap): | |
| cur = conn.cursor() | |
| tile_prefixes = {} | |
| for tile_type_pkey in tile_type_pkeys: | |
| cur.execute( | |
| """ | |
| SELECT | |
| DISTINCT tile_type.name, site_type.pkey, site_type.name | |
| FROM | |
| wire_in_tile | |
| INNER JOIN tile_type ON tile_type.pkey = wire_in_tile.phy_tile_type_pkey | |
| INNER JOIN site ON site.pkey = wire_in_tile.site_pkey | |
| INNER JOIN site_type ON site_type.pkey = site.site_type_pkey | |
| WHERE | |
| wire_in_tile.tile_type_pkey = ? | |
| AND site_pin_pkey IS NOT NULL; | |
| """, (tile_type_pkey, ) | |
| ) | |
| for tile_type_name, site_type_pkey, site_type_name in cur: | |
| norm_tile = NORMALIZED_TILE_TYPES.get( | |
| tile_type_name, tile_type_name | |
| ) | |
| if site_type_pkey not in site_types: | |
| new_site_type_pkey = site_remap[site_type_pkey] | |
| assert new_site_type_pkey in site_types | |
| site_type_pkey = new_site_type_pkey | |
| norm_site = NORMALIZED_SITE_TYPES.get( | |
| site_type_name, site_type_name | |
| ) | |
| prefix = '{' + norm_tile + '}.{' + norm_site + '}' | |
| if site_type_pkey in tile_prefixes: | |
| assert prefix == tile_prefixes[site_type_pkey], ( | |
| prefix, tile_prefixes[site_type_pkey] | |
| ) | |
| else: | |
| tile_prefixes[site_type_pkey] = prefix | |
| return tile_prefixes | |
| def sites_in_tile_type(conn, tile_type_pkey): | |
| cur = conn.cursor() | |
| # Find instances of sites, sorted by their original tile type. | |
| # Then choice the first of each site type as the wire set. | |
| # This ensures a unique and internally consistent site set for analyzing | |
| # connectivity. | |
| # | |
| # Note: This does assume that only one instance of each site is present | |
| # in each tile, which is checked. | |
| sites = {} | |
| sites_in_tiles = set() | |
| cur.execute( | |
| """ | |
| SELECT | |
| site.site_type_pkey, wire_in_tile.site_pkey, wire_in_tile.phy_tile_type_pkey | |
| FROM wire_in_tile | |
| INNER JOIN site | |
| ON site.pkey = wire_in_tile.site_pkey | |
| WHERE | |
| wire_in_tile.tile_type_pkey = ? | |
| GROUP BY site.site_type_pkey, wire_in_tile.phy_tile_type_pkey | |
| ORDER BY wire_in_tile.phy_tile_type_pkey;""", (tile_type_pkey, ) | |
| ) | |
| for site_type_pkey, site_pkey, phy_tile_type_pkey in cur: | |
| if site_type_pkey not in sites: | |
| sites[site_type_pkey] = site_pkey | |
| # Verify that assumption that each site type is only used once per tile | |
| # is true. | |
| key = (site_type_pkey, phy_tile_type_pkey) | |
| assert key not in sites_in_tiles, key | |
| sites_in_tiles.add(key) | |
| return sites, sites_in_tiles | |
| def yield_mapped_site_sets(site_remaps, site_type_pkeys): | |
| yield frozenset( | |
| site_remaps.get(site_type_pkey, site_type_pkey) | |
| for site_type_pkey in site_type_pkeys | |
| ) | |
| def check_site_equiv(conn, general_site_type_pkey, specific_site_type_pkey): | |
| """ Verify assumption that site pin names on specific site are subset of general site. | |
| E.g. every pin on the specific site should be present on the generic site. | |
| """ | |
| cur = conn.cursor() | |
| cur.execute( | |
| "SELECT name FROM site_pin WHERE site_type_pkey = ?", | |
| (general_site_type_pkey, ) | |
| ) | |
| general_site_pins = set(name for (name, ) in cur) | |
| cur.execute( | |
| "SELECT name FROM site_pin WHERE site_type_pkey = ?", | |
| (specific_site_type_pkey, ) | |
| ) | |
| for (name, ) in cur: | |
| assert name in general_site_pins, ( | |
| general_site_pins, name, specific_site_type_pkey | |
| ) | |
| def create_pb_type( | |
| conn, pin_assignments, site_directory, output_directory, pb_type, | |
| site_type_pkeys, tile_type_pkeys, site_remaps, site_name_remaps, | |
| node_tile_map | |
| ): | |
| cur = conn.cursor() | |
| cur2 = conn.cursor() | |
| wire_to_site_pin = {} | |
| internal_connections = {} | |
| top_level_pin_external = set() | |
| top_level_wire_external = set() | |
| all_pb_type_input_pins = None | |
| all_pb_type_output_pins = None | |
| for tile_type_pkey in tile_type_pkeys: | |
| pb_type_input_pins = set() | |
| pb_type_output_pins = set() | |
| for node_set in node_tile_map[tile_type_pkey]: | |
| ipin_count = 0 | |
| opin_count = 0 | |
| node_set_has_external = False | |
| pins_used_in_node_sets = set() | |
| input_pins = set() | |
| output_pins = set() | |
| for node_pkey in node_set: | |
| cur.execute( | |
| """ | |
| SELECT | |
| tile.tile_type_pkey, | |
| wire_in_tile.name, | |
| site_type.pkey, | |
| site_type.name, | |
| site_pin.name, | |
| site_pin.direction, | |
| count() | |
| FROM wire | |
| INNER JOIN wire_in_tile | |
| ON wire.wire_in_tile_pkey = wire_in_tile.pkey | |
| INNER JOIN site_pin | |
| ON site_pin.pkey = wire_in_tile.site_pin_pkey | |
| INNER JOIN tile | |
| ON wire.tile_pkey = tile.pkey | |
| INNER JOIN site_type | |
| ON site_type.pkey = site_pin.site_type_pkey | |
| WHERE | |
| wire.node_pkey = ? | |
| AND | |
| wire_in_tile.site_pin_pkey IS NOT NULL | |
| GROUP BY site_pin.direction; | |
| """, (node_pkey, ) | |
| ) | |
| for (wire_tile_type_pkey, wire_name, site_type_pkey, | |
| site_type_name, site_pin_name, direction, count) in cur: | |
| if wire_tile_type_pkey == tile_type_pkey: | |
| if site_type_pkey not in site_type_pkeys: | |
| # Remap if a mapping exists | |
| site_type_name = site_name_remaps.get( | |
| site_type_name, site_type_name | |
| ) | |
| cur2.execute( | |
| "SELECT pkey FROM site_type WHERE name = ?", | |
| (site_type_name, ) | |
| ) | |
| new_site_type_pkey = cur2.fetchone()[0] | |
| assert new_site_type_pkey in site_type_pkeys, ( | |
| pb_type, tile_type_pkey, node_pkey, | |
| site_type_pkey, new_site_type_pkey, | |
| site_type_pkeys, site_type_name | |
| ) | |
| direction = prjxray.site_type.SitePinDirection( | |
| direction | |
| ) | |
| value = (site_type_name, site_pin_name, direction) | |
| if wire_name in wire_to_site_pin: | |
| assert value == wire_to_site_pin[wire_name], ( | |
| wire_name, value, wire_to_site_pin[wire_name] | |
| ) | |
| else: | |
| wire_to_site_pin[wire_name] = value | |
| pins_used_in_node_sets.add( | |
| (wire_name, site_type_name, site_pin_name) | |
| ) | |
| if direction == prjxray.site_type.SitePinDirection.IN: | |
| output_pins.add(wire_name) | |
| pb_type_output_pins.add( | |
| (site_type_name, site_pin_name) | |
| ) | |
| opin_count += count | |
| elif direction == prjxray.site_type.SitePinDirection.OUT: | |
| input_pins.add(wire_name) | |
| pb_type_input_pins.add( | |
| (site_type_name, site_pin_name) | |
| ) | |
| ipin_count += count | |
| else: | |
| assert False, (node_pkey, direction) | |
| else: | |
| node_set_has_external = True | |
| assert len(input_pins) in [0, 1], input_pins | |
| if ipin_count == 0 or opin_count == 0 or node_set_has_external or len( | |
| site_type_pkeys) == 1: | |
| # This node set is connected externally, mark as such | |
| for wire_name, site_type_name, site_pin_name in pins_used_in_node_sets: | |
| top_level_wire_external.add(wire_name) | |
| top_level_pin_external.add((site_type_name, site_pin_name)) | |
| if len(site_type_pkeys) == 1: | |
| continue | |
| if ipin_count > 0 and opin_count > 0: | |
| # TODO: Add check that pips and site pins on these internal | |
| # connections are 0 delay. | |
| assert len(input_pins) == 1 | |
| input_wire = input_pins.pop() | |
| for wire_name in output_pins: | |
| k = wire_to_site_pin[wire_name] | |
| v = wire_to_site_pin[input_wire] | |
| if k in internal_connections: | |
| assert v == internal_connections[k], ( | |
| v, internal_connections[k] | |
| ) | |
| else: | |
| internal_connections[k] = v | |
| pb_type_input_pins = set( | |
| v for v in pb_type_input_pins if v in top_level_pin_external | |
| ) | |
| pb_type_output_pins = set( | |
| v for v in pb_type_output_pins if v in top_level_pin_external | |
| ) | |
| if all_pb_type_input_pins is None: | |
| all_pb_type_input_pins = pb_type_input_pins | |
| all_pb_type_output_pins = pb_type_output_pins | |
| else: | |
| all_pb_type_input_pins &= pb_type_input_pins | |
| all_pb_type_output_pins &= pb_type_output_pins | |
| ########################################################################## | |
| # Generate the model.xml file # | |
| ########################################################################## | |
| model = ModelXml( | |
| f=os.path.join( | |
| output_directory, pb_type.lower(), | |
| '{}.model.xml'.format(pb_type.lower()) | |
| ), | |
| site_directory=site_directory | |
| ) | |
| for site_type_pkey in site_type_pkeys: | |
| cur.execute( | |
| "SELECT name FROM site_type WHERE pkey = ?", (site_type_pkey, ) | |
| ) | |
| site_type = cur.fetchone()[0] | |
| model.add_model_include(site_type, site_type) | |
| model.write_model() | |
| ########################################################################## | |
| # Generate the pb_type.xml file # | |
| ########################################################################## | |
| pb_type_xml = start_pb_type( | |
| pb_type, | |
| pin_assignments, | |
| ['{}_{}'.format(site, pin) for site, pin in all_pb_type_output_pins], | |
| ['{}_{}'.format(site, pin) for site, pin in all_pb_type_input_pins], | |
| ) | |
| interconnect_xml = ET.Element('interconnect') | |
| site_pbtype = site_directory + "/{0}/{1}.pb_type.xml" | |
| site_prefixes = get_tile_prefixes( | |
| conn, tile_type_pkeys, site_type_pkeys, site_remaps | |
| ) | |
| cell_names = {} | |
| site_type_ports = {} | |
| for site_type_pkey in site_type_pkeys: | |
| cur.execute( | |
| "SELECT name FROM site_type WHERE pkey = ?", (site_type_pkey, ) | |
| ) | |
| site_type = cur.fetchone()[0] | |
| site_prefix = site_prefixes[site_type_pkey] | |
| site_type_path = site_pbtype.format( | |
| site_type.lower(), site_type.lower() | |
| ) | |
| cell_pb_type = ET.ElementTree() | |
| root_element = cell_pb_type.parse(site_type_path) | |
| cell_names[site_type] = 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_type not in site_type_ports, ( | |
| site_type, site_type_ports.keys() | |
| ) | |
| site_type_ports[site_type] = 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 len(site_type_pkeys) > 1: | |
| 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 site_type, site_pin_name in top_level_pin_external: | |
| port = find_port(site_pin_name, site_type_ports[site_type]) | |
| 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_name = cell_names[site_type] | |
| is_input = (site_type, site_pin_name) in all_pb_type_input_pins | |
| is_output = (site_type, site_pin_name) in all_pb_type_output_pins | |
| if not is_input and not is_output: | |
| continue | |
| if is_input: | |
| add_direct( | |
| interconnect_xml, | |
| input=object_ref(site_name, **port), | |
| output=object_ref( | |
| add_vpr_tile_prefix(pb_type), | |
| '{}_{}'.format(site_type, site_pin_name), | |
| ), | |
| ) | |
| for site_type, site_pin_name in top_level_pin_external: | |
| port = find_port(site_pin_name, site_type_ports[site_type]) | |
| if port is None: | |
| continue | |
| site_name = cell_names[site_type] | |
| is_input = (site_type, site_pin_name) in all_pb_type_input_pins | |
| is_output = (site_type, site_pin_name) in all_pb_type_output_pins | |
| if not is_input and not is_output: | |
| continue | |
| if is_output: | |
| add_direct( | |
| interconnect_xml, | |
| input=object_ref( | |
| add_vpr_tile_prefix(pb_type), | |
| '{}_{}'.format(site_type, site_pin_name) | |
| ), | |
| output=object_ref(site_name, **port), | |
| ) | |
| for (dest_site_type, dest_site_pin_name, _), (src_site_type, src_site_pin_name, _) in \ | |
| sorted(internal_connections.items(), key=lambda x: (x[1], x[0])): | |
| src_port = find_port(src_site_pin_name, site_type_ports[src_site_type]) | |
| src_site_name = cell_names[src_site_type] | |
| if src_port is None: | |
| print( | |
| "*** WARNING *** Didn't find port for name {} for site type {}" | |
| .format(src_site_pin_name, src_site_type), | |
| file=sys.stderr | |
| ) | |
| continue | |
| dest_port = find_port( | |
| dest_site_pin_name, site_type_ports[dest_site_type] | |
| ) | |
| dest_site_name = cell_names[dest_site_type] | |
| if dest_port is None: | |
| print( | |
| "*** WARNING *** Didn't find port for name {} for site type {}" | |
| .format(dest_site_pin_name, dest_site_type), | |
| file=sys.stderr | |
| ) | |
| continue | |
| add_direct( | |
| interconnect_xml, | |
| input=object_ref(src_site_name, **src_port), | |
| output=object_ref(dest_site_name, **dest_port), | |
| ) | |
| pb_type_xml.append(interconnect_xml) | |
| write_xml( | |
| os.path.join( | |
| output_directory, pb_type.lower(), | |
| '{}.pb_type.xml'.format(pb_type.lower()) | |
| ), pb_type_xml | |
| ) | |
| top_level_connections = {} | |
| for wire in sorted(top_level_wire_external): | |
| site_type, site_pin_name, _ = wire_to_site_pin[wire] | |
| is_input = (site_type, site_pin_name) in all_pb_type_input_pins | |
| is_output = (site_type, site_pin_name) in all_pb_type_output_pins | |
| if not is_input and not is_output: | |
| continue | |
| top_level_connections[wire] = wire_to_site_pin[wire] | |
| return top_level_connections | |
| def create_tile( | |
| conn, tile_type, tile_type_pkey, tile_connections, pb_types, | |
| pin_assignments, output_directory | |
| ): | |
| cur = conn.cursor() | |
| input_wires = set() | |
| output_wires = set() | |
| cur.execute( | |
| """ | |
| SELECT DISTINCT wire_in_tile.name | |
| FROM wire_in_tile | |
| WHERE | |
| wire_in_tile.tile_type_pkey = ? | |
| AND | |
| wire_in_tile.site_pin_pkey IS NOT NULL; | |
| """, (tile_type_pkey, ) | |
| ) | |
| for (wire_name, ) in cur: | |
| for pb_type in pb_types: | |
| if wire_name in tile_connections[pb_type]: | |
| site_type_name, site_pin_name, direction = tile_connections[ | |
| pb_type][wire_name] | |
| if direction == prjxray.site_type.SitePinDirection.IN: | |
| input_wires.add(wire_name) | |
| elif direction == prjxray.site_type.SitePinDirection.OUT: | |
| output_wires.add(wire_name) | |
| else: | |
| assert False, (wire_name, direction) | |
| tile_xml = start_tile( | |
| tile_type, | |
| pin_assignments, | |
| input_wires, | |
| output_wires, | |
| ) | |
| equivalent_sites_xml = ET.Element('equivalent_sites') | |
| for pb_type in pb_types: | |
| site_xml = ET.Element( | |
| 'site', { | |
| 'pb_type': add_vpr_tile_prefix(pb_type), | |
| 'pin_mapping': 'custom' | |
| } | |
| ) | |
| equivalent_sites_xml.append(site_xml) | |
| for wire_name in input_wires | output_wires: | |
| if wire_name in tile_connections[pb_type]: | |
| site_type_name, site_pin_name, direction = tile_connections[ | |
| pb_type][wire_name] | |
| add_tile_direct( | |
| site_xml, | |
| tile=object_ref( | |
| add_vpr_tile_prefix(tile_type), | |
| wire_name, | |
| ), | |
| pb_type=object_ref( | |
| add_vpr_tile_prefix(pb_type), | |
| '{}_{}'.format(site_type_name, site_pin_name) | |
| ), | |
| ) | |
| sub_tile_xml = tile_xml.find('./sub_tile') | |
| sub_tile_xml.append(equivalent_sites_xml) | |
| add_switchblock_locations(tile_xml) | |
| write_xml( | |
| os.path.join( | |
| output_directory, tile_type.lower(), | |
| '{}.tile.xml'.format(tile_type.lower()) | |
| ), tile_xml | |
| ) | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description=__doc__, fromfile_prefix_chars='@', prefix_chars='-~' | |
| ) | |
| parser.add_argument( | |
| '--site_directory', | |
| required=True, | |
| help="""Diretory where sites are defined""" | |
| ) | |
| parser.add_argument( | |
| '--output_directory', | |
| required=True, | |
| help="Directory to write output XML too.", | |
| ) | |
| parser.add_argument( | |
| '--pin_assignments', | |
| required=True, | |
| ) | |
| parser.add_argument( | |
| '--connection_database', | |
| required=True, | |
| help=( | |
| "Location of connection database to define this tile type. " + | |
| "The tile will be defined by the sites and wires from the " + | |
| "connection database in lue of Project X-Ray." | |
| ) | |
| ) | |
| parser.add_argument( | |
| '--tile_types', | |
| required=True, | |
| help="Comma seperated list of tiles to create equivilance between." | |
| ) | |
| parser.add_argument('--pb_types', nargs='+', required=True, help="") | |
| parser.add_argument( | |
| '--site_equivilances', | |
| help="Comma seperated list of site equivilances to apply." | |
| ) | |
| args = parser.parse_args() | |
| with open(args.pin_assignments) as f: | |
| pin_assignments = json.load(f) | |
| ET.register_namespace('xi', XI_URL) | |
| with sqlite3.connect("file:{}?mode=ro".format(args.connection_database), | |
| uri=True) as conn: | |
| cur = conn.cursor() | |
| tile_types = {} | |
| node_tile_map = {} | |
| sites = {} | |
| sites_in_tiles = {} | |
| pb_types = {} | |
| site_remaps = {} | |
| site_name_remaps = {} | |
| if args.site_equivilances is not None: | |
| for site_remap in args.site_equivilances.split(','): | |
| general_site, specific_site = site_remap.split('=') | |
| assert general_site not in site_name_remaps, general_site | |
| site_name_remaps[general_site] = specific_site | |
| cur.execute( | |
| "SELECT pkey FROM site_type WHERE name = ?", | |
| (general_site, ) | |
| ) | |
| general_site_type_pkey = cur.fetchone()[0] | |
| cur.execute( | |
| "SELECT pkey FROM site_type WHERE name = ?", | |
| (specific_site, ) | |
| ) | |
| specific_site_type_pkey = cur.fetchone()[0] | |
| site_remaps[general_site_type_pkey] = specific_site_type_pkey | |
| check_site_equiv( | |
| conn, general_site_type_pkey, specific_site_type_pkey | |
| ) | |
| site_collections_to_pb_type = {} | |
| for pb_type_def_string in args.pb_types: | |
| pb_type_name, pb_type_sites = pb_type_def_string.split('=') | |
| pb_type_sites = pb_type_sites.split(',') | |
| assert pb_type_name not in pb_types, pb_type_name | |
| pb_types[pb_type_name] = [] | |
| for site in pb_type_sites: | |
| cur.execute( | |
| "SELECT pkey FROM site_type WHERE name = ?", (site, ) | |
| ) | |
| result = cur.fetchone() | |
| assert result is not None, site | |
| site_type_pkey = result[0] | |
| pb_types[pb_type_name].append(site_type_pkey) | |
| pb_types[pb_type_name] = frozenset(pb_types[pb_type_name]) | |
| assert pb_types[pb_type_name] not in site_collections_to_pb_type, ( | |
| pb_type_name, pb_types[pb_type_name] | |
| ) | |
| site_collections_to_pb_type[pb_types[pb_type_name]] = pb_type_name | |
| pb_types_in_tile = {} | |
| tiles_that_instance_pb_type = {} | |
| for tile_type in args.tile_types.split(','): | |
| cur.execute( | |
| "SELECT pkey FROM tile_type WHERE name = ?", (tile_type, ) | |
| ) | |
| tile_type_pkey = cur.fetchone()[0] | |
| tile_types[tile_type] = tile_type_pkey | |
| node_tile_map[tile_type_pkey] = expand_nodes_in_tile_type( | |
| conn, tile_type_pkey | |
| ) | |
| sites[tile_type_pkey], sites_in_tiles[ | |
| tile_type_pkey] = sites_in_tile_type(conn, tile_type_pkey) | |
| seen_sets = set() | |
| site_type_pkeys = frozenset(sites[tile_type_pkey].keys()) | |
| seen_sets.add(site_type_pkeys) | |
| pb_types_in_tile[tile_type_pkey] = [ | |
| site_collections_to_pb_type[site_type_pkeys] | |
| ] | |
| for site_set in yield_mapped_site_sets(site_remaps, | |
| site_type_pkeys): | |
| if site_set in seen_sets: | |
| continue | |
| pb_types_in_tile[tile_type_pkey].append( | |
| site_collections_to_pb_type[site_set] | |
| ) | |
| for pb_type in pb_types_in_tile[tile_type_pkey]: | |
| if pb_type not in tiles_that_instance_pb_type: | |
| tiles_that_instance_pb_type[pb_type] = [] | |
| tiles_that_instance_pb_type[pb_type].append(tile_type_pkey) | |
| tile_connections = {} | |
| for pb_type in tiles_that_instance_pb_type: | |
| tile_connections[pb_type] = create_pb_type( | |
| conn=conn, | |
| pin_assignments=pin_assignments, | |
| site_directory=args.site_directory, | |
| output_directory=args.output_directory, | |
| pb_type=pb_type, | |
| site_type_pkeys=pb_types[pb_type], | |
| tile_type_pkeys=tiles_that_instance_pb_type[pb_type], | |
| site_remaps=site_remaps, | |
| site_name_remaps=site_name_remaps, | |
| node_tile_map=node_tile_map | |
| ) | |
| for tile_type in tile_types: | |
| tile_type_pkey = tile_types[tile_type] | |
| create_tile( | |
| conn=conn, | |
| tile_type=tile_type, | |
| tile_type_pkey=tile_type_pkey, | |
| tile_connections=tile_connections, | |
| pb_types=pb_types_in_tile[tile_type_pkey], | |
| output_directory=args.output_directory, | |
| pin_assignments=pin_assignments, | |
| ) | |
| if __name__ == '__main__': | |
| main() |