|  | #!/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 | 
|  |  | 
|  | from utils import xjson | 
|  |  | 
|  |  | 
|  | def load_tiles(tiles_fn): | 
|  | ''' | 
|  | "$type $tile $grid_x $grid_y $skip_tile $clock_region $typed_sites" | 
|  | typed_sites: foreach t $site_types s $sites | 
|  | ''' | 
|  | tiles = list() | 
|  |  | 
|  | with open(tiles_fn) as f: | 
|  | for line in f: | 
|  | # CLBLM_L CLBLM_L_X10Y98 30 106 SLICEL SLICE_X13Y98 SLICEM SLICE_X12Y98 | 
|  | record = line.split() | 
|  | tile_type, tile_name, grid_x, grid_y, skip_tile = record[0:5] | 
|  | grid_x, grid_y = int(grid_x), int(grid_y) | 
|  | skip_tile = int(skip_tile) != 0 | 
|  | sites = {} | 
|  |  | 
|  | # prohibits is the list of sites that the Vivado placer will not | 
|  | # place at. | 
|  | # | 
|  | # Speculation: These sites are prohibited not because the hardware | 
|  | # doesn't work, but because the interconnect around these sites is | 
|  | # extremely narrow due to the hardblocks to the left and right of | 
|  | # tiles.  As a result, these sites should be avoided because | 
|  | # congestion and delays when using these sites might be very very | 
|  | # high. | 
|  | prohibits = [] | 
|  | clock_region = None | 
|  | if len(record) >= 6: | 
|  | clock_region = record[5] | 
|  | if clock_region == "NA": | 
|  | clock_region = None | 
|  | for i in range(6, len(record), 3): | 
|  | site_type, site_name, prohibited = record[i:i + 3] | 
|  | sites[site_name] = site_type | 
|  | if int(prohibited): | 
|  | prohibits.append(site_name) | 
|  |  | 
|  | if not skip_tile: | 
|  | tile = { | 
|  | 'type': tile_type, | 
|  | 'name': tile_name, | 
|  | 'grid_x': grid_x, | 
|  | 'grid_y': grid_y, | 
|  | 'sites': sites, | 
|  | 'prohibited_sites': sorted(prohibits), | 
|  | 'clock_region': clock_region, | 
|  | } | 
|  | else: | 
|  | # Replace tiles within the exclude_roi with NULL tiles to | 
|  | # ensure no gaps in the tilegrid. | 
|  | # | 
|  | # The name will reflect the original tile. | 
|  | tile = { | 
|  | 'type': 'NULL', | 
|  | 'name': tile_name, | 
|  | 'grid_x': grid_x, | 
|  | 'grid_y': grid_y, | 
|  | 'sites': {}, | 
|  | 'prohibited_sites': [], | 
|  | 'clock_region': clock_region, | 
|  | } | 
|  |  | 
|  | tiles.append(tile) | 
|  |  | 
|  | return tiles | 
|  |  | 
|  |  | 
|  | def load_pin_functions(pin_func_fn): | 
|  | pin_functions = {} | 
|  |  | 
|  | with open(pin_func_fn) as f: | 
|  | for line in f: | 
|  | site, pin_func = line.split() | 
|  | assert site not in pin_functions, site | 
|  | pin_functions[site] = pin_func | 
|  |  | 
|  | return pin_functions | 
|  |  | 
|  |  | 
|  | def make_database(tiles, pin_func): | 
|  | # tile database with X, Y, and list of sites | 
|  | # tile name as keys | 
|  | database = dict() | 
|  |  | 
|  | for tile in tiles: | 
|  | database[tile["name"]] = { | 
|  | "type": tile["type"], | 
|  | "sites": tile["sites"], | 
|  | "grid_x": tile["grid_x"], | 
|  | "grid_y": tile["grid_y"], | 
|  | "bits": {}, | 
|  | "pin_functions": {}, | 
|  | "prohibited_sites": tile['prohibited_sites'], | 
|  | } | 
|  |  | 
|  | if tile["clock_region"]: | 
|  | database[tile["name"]]["clock_region"] = tile["clock_region"] | 
|  |  | 
|  | for site in database[tile["name"]]["sites"]: | 
|  | if site in pin_func: | 
|  | database[tile["name"]]["pin_functions"][site] = pin_func[site] | 
|  |  | 
|  | return database | 
|  |  | 
|  |  | 
|  | def run(tiles_fn, pin_func_fn, json_fn, verbose=False): | 
|  | # Load input files | 
|  | tiles = load_tiles(tiles_fn) | 
|  |  | 
|  | # Read site map | 
|  | pin_func = load_pin_functions(pin_func_fn) | 
|  |  | 
|  | # Index input | 
|  | database = make_database(tiles, pin_func) | 
|  |  | 
|  | # Save | 
|  | xjson.pprint(open(json_fn, 'w'), database) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | import argparse | 
|  |  | 
|  | parser = argparse.ArgumentParser( | 
|  | description='Generate tilegrid.json from bitstream deltas') | 
|  |  | 
|  | parser.add_argument('--verbose', action='store_true', help='') | 
|  | parser.add_argument('--out', default='/dev/stdout', help='Output JSON') | 
|  | parser.add_argument( | 
|  | '--tiles', | 
|  | default='tiles.txt', | 
|  | help='Input tiles.txt tcl output', | 
|  | required=True) | 
|  | parser.add_argument( | 
|  | '--pin_func', help='List of sites with pin functions', required=True) | 
|  | args = parser.parse_args() | 
|  |  | 
|  | run(args.tiles, args.pin_func, args.out, verbose=args.verbose) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |