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