|  | #!/usr/bin/env python3 | 
|  | # -*- coding: utf-8 -*- | 
|  | # | 
|  | # Copyright (C) 2017-2020  The Project X-Ray Authors. | 
|  | # | 
|  | # Use of this source code is governed by a ISC-style | 
|  | # license that can be found in the LICENSE file or at | 
|  | # https://opensource.org/licenses/ISC | 
|  | # | 
|  | # SPDX-License-Identifier: ISC | 
|  | """ Tool to cleanup site pins JSON dumps. | 
|  |  | 
|  | This tool has two behaviors.  This first is to rename site names from global | 
|  | coordinates to site local coordinates.  The second is remove the tile prefix | 
|  | from node names. | 
|  |  | 
|  | For example CLBLM_L_X8Y149 contains two sites named SLICE_X10Y149 and | 
|  | SLICE_X11Y149. SLICE_X10Y149 becomes X0Y0 and SLICE_X11Y149 becomes X1Y0. | 
|  | """ | 
|  |  | 
|  | from __future__ import print_function | 
|  | import json | 
|  | import json5 | 
|  | import re | 
|  | import sys | 
|  | import copy | 
|  |  | 
|  | # All site names appear to follow the pattern <type>_X<abs coord>Y<abs coord>. | 
|  | # Generally speaking, only the tile relatively coordinates are required to | 
|  | # assemble arch defs, so we re-origin the coordinates to be relative to the tile | 
|  | # (e.g. start at X0Y0) and discard the prefix from the name. | 
|  | SITE_COORDINATE_PATTERN = re.compile('^(.+)_X([0-9]+)Y([0-9]+)$') | 
|  |  | 
|  |  | 
|  | def find_origin_coordinate(sites): | 
|  | """ Find the coordinates of each site within the tile, and then subtract the | 
|  | smallest coordinate to re-origin them all to be relative to the tile. | 
|  | """ | 
|  |  | 
|  | if len(sites) == 0: | 
|  | return 0, 0 | 
|  |  | 
|  | def inner_(): | 
|  | for site in sites: | 
|  | coordinate = SITE_COORDINATE_PATTERN.match(site['name']) | 
|  | assert coordinate is not None, site | 
|  |  | 
|  | x_coord = int(coordinate.group(2)) | 
|  | y_coord = int(coordinate.group(3)) | 
|  | yield x_coord, y_coord | 
|  |  | 
|  | x_coords, y_coords = zip(*inner_()) | 
|  | min_x_coord = min(x_coords) | 
|  | min_y_coord = min(y_coords) | 
|  |  | 
|  | return min_x_coord, min_y_coord | 
|  |  | 
|  |  | 
|  | def create_site_pin_to_wire_maps(tile_name, nodes): | 
|  | """ Create a map from site_pin names to nodes. | 
|  |  | 
|  | Create a mapping from site pins to tile local wires.  For each node that is | 
|  | attached to a site pin, there should only be 1 tile local wire. | 
|  |  | 
|  | """ | 
|  |  | 
|  | # Remove tile prefix (e.g. CLBLM_L_X8Y149/) from node names. | 
|  | # Routing resources will not have the prefix. | 
|  | tile_prefix = tile_name + '/' | 
|  | site_pin_to_wires = {} | 
|  |  | 
|  | for node in nodes: | 
|  | if len(node['site_pins']) == 0: | 
|  | continue | 
|  |  | 
|  | wire_names = [ | 
|  | wire for wire in node['wires'] if wire.startswith(tile_prefix) | 
|  | ] | 
|  | assert len(wire_names) == 1, (node, tile_prefix) | 
|  |  | 
|  | for site_pin in node["site_pins"]: | 
|  | assert site_pin not in site_pin_to_wires | 
|  | site_pin_to_wires[site_pin] = wire_names[0] | 
|  |  | 
|  | return site_pin_to_wires | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | site_pins = json5.load(sys.stdin) | 
|  |  | 
|  | output_site_pins = {} | 
|  | output_site_pins["tile_type"] = site_pins["tile_type"] | 
|  | output_site_pins["sites"] = copy.deepcopy(site_pins["sites"]) | 
|  |  | 
|  | site_pin_to_wires = create_site_pin_to_wire_maps( | 
|  | site_pins['tile_name'], site_pins['nodes']) | 
|  | min_x_coord, min_y_coord = find_origin_coordinate(site_pins['sites']) | 
|  |  | 
|  | for site in output_site_pins['sites']: | 
|  | orig_site_name = site['name'] | 
|  | coordinate = SITE_COORDINATE_PATTERN.match(orig_site_name) | 
|  |  | 
|  | x_coord = int(coordinate.group(2)) | 
|  | y_coord = int(coordinate.group(3)) | 
|  | site['name'] = 'X{}Y{}'.format( | 
|  | x_coord - min_x_coord, y_coord - min_y_coord) | 
|  | site['prefix'] = coordinate.group(1) | 
|  | site['x_coord'] = x_coord - min_x_coord | 
|  | site['y_coord'] = y_coord - min_y_coord | 
|  |  | 
|  | for site_pin in site['site_pins']: | 
|  | assert site_pin['name'].startswith(orig_site_name + '/') | 
|  | if site_pin['name'] in site_pin_to_wires: | 
|  | site_pin['wire'] = site_pin_to_wires[site_pin['name']] | 
|  | else: | 
|  | print( | 
|  | ( | 
|  | '***WARNING***: Site pin {} for tile type {} is not connected, ' | 
|  | 'make sure all instaces of this tile type has this site_pin ' | 
|  | 'disconnected.').format( | 
|  | site_pin['name'], site_pins['tile_type']), | 
|  | file=sys.stderr) | 
|  |  | 
|  | site_pin['name'] = site_pin['name'][len(orig_site_name) + 1:] | 
|  |  | 
|  | json.dumps(output_site_pins, indent=2, sort_keys=True) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() |