| #!/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 | 
 | """ | 
 | This script loads the pin dump of the desired BEL from Vivado and groups pins into ports. | 
 |  | 
 | Ports that are not connected to the PL are not included. These ports are usually test and | 
 | debug related ports, which are not useful from a P&R perspective. | 
 |  | 
 | Ports are then written to a JSON file. | 
 | """ | 
 | import argparse | 
 | import csv | 
 | import json | 
 | import re | 
 |  | 
 | from collections import defaultdict | 
 |  | 
 | from prjxray.util import OpenSafeFile | 
 |  | 
 |  | 
 | def main(): | 
 |  | 
 |     BUS_REGEX = re.compile("(.*[A-Z_])([0-9]+)$") | 
 |  | 
 |     # Parse arguments | 
 |     parser = argparse.ArgumentParser( | 
 |         description=__doc__, | 
 |         formatter_class=argparse.RawDescriptionHelpFormatter) | 
 |  | 
 |     parser.add_argument("csv", type=str, help="BEL pin dump file") | 
 |     parser.add_argument( | 
 |         "json", | 
 |         type=str, | 
 |         help="Output JSON file with BEL pins grouped into ports") | 
 |  | 
 |     parser.add_argument( | 
 |         "--special-pins", | 
 |         default="", | 
 |         type=str, | 
 |         required=False, | 
 |         help="Some pins cannot be decoded with the regex") | 
 |  | 
 |     args = parser.parse_args() | 
 |  | 
 |     # Load pin dump | 
 |     with OpenSafeFile(args.csv, "r") as fp: | 
 |         pin_dump = list(csv.DictReader(fp)) | 
 |  | 
 |     # Group pins into ports | 
 |     ports = defaultdict(lambda: {"direction": None, "width": 0}) | 
 |  | 
 |     special_pins = args.special_pins.split(",") | 
 |  | 
 |     for pin in list(pin_dump): | 
 |         pin_name = pin["name"] | 
 |  | 
 |         name = None | 
 |         if pin_name in special_pins: | 
 |             # Full match | 
 |             name = pin_name | 
 |         else: | 
 |             # Partial match | 
 |             for special_pin in special_pins: | 
 |                 if pin_name.startswith(special_pin): | 
 |                     name = special_pin | 
 |                     break | 
 |  | 
 |         if name is None: | 
 |             match = BUS_REGEX.match(pin_name) | 
 |             if match: | 
 |                 name = match.group(1) | 
 |             else: | 
 |                 name = pin_name | 
 |  | 
 |         # Get direction | 
 |         is_input = int(pin["is_input"]) | 
 |         is_output = int(pin["is_output"]) | 
 |         is_clock = int(pin["is_clock"]) | 
 |  | 
 |         if is_input: | 
 |             direction = "input" | 
 |             if is_clock: | 
 |                 direction = "clock" | 
 |         elif is_output: | 
 |             direction = "output" | 
 |         else: | 
 |             assert False, pin | 
 |  | 
 |         # Add to port | 
 |         port = ports[name] | 
 |  | 
 |         if port["direction"] is None: | 
 |             port["direction"] = direction | 
 |         else: | 
 |             assert port["direction"] == direction, (port, direction, name) | 
 |  | 
 |         port["width"] += 1 | 
 |  | 
 |     # Write pin ports to a JSON file | 
 |     with OpenSafeFile(args.json, "w") as fp: | 
 |         json.dump(ports, fp, indent=1, sort_keys=True) | 
 |  | 
 |  | 
 | if __name__ == "__main__": | 
 |     main() |