#!/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 _XY. # 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()