| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright (C) 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 |
| """ This script creates node_wires.json, which describes how nodes are named. |
| |
| This script consumes the raw node data from root_dir and outputs |
| node_wires.json to the output_dir. |
| |
| The class prjxray.node_model.NodeModel can be used to reconstruct node names |
| and node <-> wire mapping. |
| |
| The contents of node_wires.json is: |
| - The set of tile type wires that are always nodes, key "node_pattern_wires" |
| - The set of tile wires that are nodes within the graph, key |
| "specific_node_wires". |
| |
| """ |
| |
| import argparse |
| import datetime |
| import json |
| import multiprocessing |
| import progressbar |
| import pyjson5 as json5 |
| import os.path |
| |
| from prjxray import util, lib |
| from prjxray.grid import Grid |
| |
| |
| def read_json5(fname): |
| with open(fname, 'r') as f: |
| return json5.load(f) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description="Reduce node names for wire connections.") |
| parser.add_argument('--root_dir', required=True) |
| parser.add_argument('--output_dir', required=True) |
| parser.add_argument('--max_cpu', type=int, default=10) |
| |
| args = parser.parse_args() |
| |
| _, nodes = lib.read_root_csv(args.root_dir) |
| |
| processes = min(multiprocessing.cpu_count(), args.max_cpu) |
| pool = multiprocessing.Pool(processes=processes) |
| |
| # Read tile grid and raw node data. |
| print('{} Reading tilegrid'.format(datetime.datetime.now())) |
| with open(os.path.join(util.get_db_root(), util.get_fabric(), |
| 'tilegrid.json')) as f: |
| grid = Grid(db=None, tilegrid=json.load(f)) |
| |
| raw_node_data = [] |
| with progressbar.ProgressBar(max_value=len(nodes)) as bar: |
| for idx, node in enumerate(pool.imap_unordered( |
| read_json5, |
| nodes, |
| chunksize=20, |
| )): |
| bar.update(idx) |
| raw_node_data.append(node) |
| bar.update(idx + 1) |
| |
| node_wires = set() |
| remove_node_wires = set() |
| specific_node_wires = set() |
| |
| # Create initial node wire pattern |
| for node in progressbar.progressbar(raw_node_data): |
| if len(node['wires']) <= 1: |
| continue |
| |
| node_tile, node_wire = node['node'].split('/') |
| |
| for wire in node['wires']: |
| wire_tile, wire_name = wire['wire'].split('/') |
| |
| if node['node'] == wire['wire']: |
| assert node_tile == wire_tile |
| assert node_wire == wire_name |
| gridinfo = grid.gridinfo_at_tilename(node_tile) |
| node_wires.add((gridinfo.tile_type, wire_name)) |
| |
| print( |
| 'Initial number of wires that are node drivers: {}'.format( |
| len(node_wires))) |
| |
| # Remove exceptional node wire names, create specific_node_wires set, |
| # which is simply the list of wires that are nodes in the graph. |
| for node in progressbar.progressbar(raw_node_data): |
| if len(node['wires']) <= 1: |
| continue |
| |
| for wire in node['wires']: |
| wire_tile, wire_name = wire['wire'].split('/') |
| gridinfo = grid.gridinfo_at_tilename(wire_tile) |
| key = gridinfo.tile_type, wire_name |
| |
| if node['node'] == wire['wire']: |
| assert key in node_wires |
| else: |
| if key in node_wires: |
| specific_node_wires.add(node['node']) |
| remove_node_wires.add(key) |
| |
| # Complete the specific_node_wires list after the pruning of the |
| # node_pattern_wires sets. |
| for node in progressbar.progressbar(raw_node_data): |
| if len(node['wires']) <= 1: |
| continue |
| |
| for wire in node['wires']: |
| wire_tile, wire_name = wire['wire'].split('/') |
| gridinfo = grid.gridinfo_at_tilename(wire_tile) |
| key = gridinfo.tile_type, wire_name |
| |
| if key in remove_node_wires and node['node'] == wire['wire']: |
| specific_node_wires.add(node['node']) |
| |
| node_wires -= remove_node_wires |
| print( |
| 'Final number of wires that are node drivers: {}'.format( |
| len(node_wires))) |
| print( |
| 'Number of wires that are node drivers: {}'.format( |
| len(specific_node_wires))) |
| |
| # Verify the node wire data. |
| for node in progressbar.progressbar(raw_node_data): |
| if len(node['wires']) <= 1: |
| continue |
| |
| found_node_wire = False |
| for wire in node['wires']: |
| if wire['wire'] in specific_node_wires: |
| assert wire['wire'] == node['node'] |
| |
| found_node_wire = True |
| break |
| |
| if not found_node_wire: |
| for wire in node['wires']: |
| wire_tile, wire_name = wire['wire'].split('/') |
| gridinfo = grid.gridinfo_at_tilename(wire_tile) |
| key = gridinfo.tile_type, wire_name |
| |
| if key in node_wires: |
| assert node['node'] == wire['wire'] |
| else: |
| assert node['node'] != wire['wire'] |
| |
| # Normalize output. |
| tile_types = {} |
| for tile_type, tile_wire in node_wires: |
| if tile_type not in tile_types: |
| tile_types[tile_type] = [] |
| |
| tile_types[tile_type].append(tile_wire) |
| |
| for tile_type in tile_types: |
| tile_types[tile_type].sort() |
| |
| out = { |
| 'node_pattern_wires': tile_types, |
| 'specific_node_wires': sorted(specific_node_wires), |
| } |
| |
| with open(os.path.join(args.output_dir, 'node_wires.json'), 'w') as f: |
| json.dump(out, f, indent=2, sort_keys=True) |
| |
| |
| if __name__ == '__main__': |
| main() |