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