|  | #!/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 allows to find missing segbits in the database. | 
|  |  | 
|  | For each tile the script loads its 'tile_type_*.json' file and looks for all | 
|  | non-pseudo pips there. Next it loads corresponding 'segbits_*.db' file (if found) | 
|  | and checks if those pips are listed there. | 
|  |  | 
|  | Missing segbits for pips are reported as well as missing segbit files. | 
|  | """ | 
|  | import sys | 
|  | import logging | 
|  | import json | 
|  | import argparse | 
|  | import os | 
|  | import re | 
|  |  | 
|  | from prjxray.util import OpenSafeFile | 
|  |  | 
|  | # ============================================================================= | 
|  |  | 
|  |  | 
|  | def read_pips_from_tile(tile_file): | 
|  | """ | 
|  | Loads pip definition from a tile type JSON file and returns non-pseudo | 
|  | PIP name strings. Names are formatted as <dst_wire>.<src_wire> | 
|  | """ | 
|  |  | 
|  | with OpenSafeFile(tile_file, "r") as fp: | 
|  | root = json.load(fp) | 
|  | pips = root["pips"] | 
|  |  | 
|  | pip_names = [] | 
|  | for pip in pips.values(): | 
|  | if int(pip["is_pseudo"]) == 0: | 
|  | pip_names.append( | 
|  | "{}.{}".format(pip["dst_wire"], pip["src_wire"])) | 
|  |  | 
|  | return pip_names | 
|  |  | 
|  |  | 
|  | def read_ppips(ppips_file): | 
|  | """ | 
|  | Loads and parses ppips_*.db file. Returns a dict indexed by PIP name which | 
|  | contains their types ("always", "default" or "hint") | 
|  | """ | 
|  | ppips = {} | 
|  |  | 
|  | with OpenSafeFile(ppips_file, "r") as fp: | 
|  | for line in fp.readlines(): | 
|  | line = line.split() | 
|  | if len(line) == 2: | 
|  | full_pip_name = line[0].split(".") | 
|  | pip_name = ".".join(full_pip_name[1:]) | 
|  | ppips[pip_name] = line[1] | 
|  |  | 
|  | return ppips | 
|  |  | 
|  |  | 
|  | def read_segbits(segbits_file): | 
|  | """ | 
|  | Loads and parses segbits_*.db file. Returns only segbit names. | 
|  | """ | 
|  | segbits = [] | 
|  |  | 
|  | with OpenSafeFile(segbits_file, "r") as fp: | 
|  | for line in fp.readlines(): | 
|  | line = line.split() | 
|  | if len(line) > 1: | 
|  | fields = line[0].split(".") | 
|  | segbit = ".".join(fields[1:]) | 
|  | segbits.append(segbit) | 
|  |  | 
|  | return segbits | 
|  |  | 
|  |  | 
|  | # ============================================================================= | 
|  |  | 
|  |  | 
|  | def main(argv): | 
|  | """ | 
|  | The main | 
|  | """ | 
|  |  | 
|  | exitcode = 0 | 
|  |  | 
|  | # Parse arguments | 
|  | parser = argparse.ArgumentParser( | 
|  | description=__doc__, | 
|  | formatter_class=argparse.RawDescriptionHelpFormatter) | 
|  | parser.add_argument( | 
|  | "--db-root", type=str, required=True, help="Database root") | 
|  | parser.add_argument( | 
|  | "--verbose", type=int, default=0, help="Verbosity level 0-5") | 
|  | parser.add_argument( | 
|  | "--skip-tiles", | 
|  | type=str, | 
|  | nargs="*", | 
|  | default=[], | 
|  | help="Tile type name regex list for tile types to skip") | 
|  | parser.add_argument( | 
|  | "--incl-tiles", | 
|  | type=str, | 
|  | nargs="*", | 
|  | default=[], | 
|  | help="Tile type name regex list for tile types to include") | 
|  |  | 
|  | args = parser.parse_args(argv[1:]) | 
|  |  | 
|  | logging.basicConfig(level=50 - args.verbose * 10, format="%(message)s") | 
|  |  | 
|  | # List files in DB root | 
|  | files = os.listdir(args.db_root) | 
|  |  | 
|  | # List tile types | 
|  | tile_types = [] | 
|  | for file in files: | 
|  | match = re.match("^tile_type_(\\w+).json$", file) | 
|  | if match: | 
|  | tile_types.append(match.group(1)) | 
|  |  | 
|  | tile_types.sort() | 
|  |  | 
|  | # Look for missing bits | 
|  | for tile_type in tile_types: | 
|  |  | 
|  | # Check if we should include this tile | 
|  | do_skip = len(args.incl_tiles) > 0 | 
|  | for pattern in args.incl_tiles: | 
|  | if re.match(pattern, tile_type): | 
|  | do_skip = False | 
|  | break | 
|  |  | 
|  | # Check if we should skip this tile | 
|  | for pattern in args.skip_tiles: | 
|  | if re.match(pattern, tile_type): | 
|  | do_skip = True | 
|  | break | 
|  |  | 
|  | if do_skip: | 
|  | continue | 
|  |  | 
|  | logging.critical(tile_type) | 
|  |  | 
|  | # DB file names | 
|  | tile_file = os.path.join( | 
|  | args.db_root, "tile_type_{}.json".format(tile_type.upper())) | 
|  | ppips_file = os.path.join( | 
|  | args.db_root, "ppips_{}.db".format(tile_type.lower())) | 
|  | segbits_file = os.path.join( | 
|  | args.db_root, "segbits_{}.db".format(tile_type.lower())) | 
|  |  | 
|  | # Load pips | 
|  | pips = read_pips_from_tile(tile_file) | 
|  |  | 
|  | # Load ppips (if any) | 
|  | if os.path.isfile(ppips_file): | 
|  | ppips = read_ppips(ppips_file) | 
|  | else: | 
|  | ppips = {} | 
|  |  | 
|  | # Load segbits (if any) | 
|  | if os.path.isfile(segbits_file): | 
|  | segbits = read_segbits(segbits_file) | 
|  | else: | 
|  | segbits = [] | 
|  |  | 
|  | # There are non-pseudo pips in this tile | 
|  | if len(pips): | 
|  | missing_bits = 0 | 
|  | known_bits = 0 | 
|  |  | 
|  | # Build a list of pips to check. If a pip is listed in the ppips | 
|  | # file and it is not "default" then make it a pseudo one | 
|  | pips_to_check = [] | 
|  | for pip in pips: | 
|  | if pip in ppips.keys() and ppips[pip] != "default": | 
|  | continue | 
|  | pips_to_check.append(pip) | 
|  |  | 
|  | # Missing segbits file | 
|  | if len(segbits) == 0: | 
|  | missing_bits = len(pips_to_check) | 
|  | logging.critical(" MISSING: no segbits file!") | 
|  | exitcode = -1 | 
|  |  | 
|  | # Segbits file present | 
|  | else: | 
|  |  | 
|  | # Check pips | 
|  | for pip in pips_to_check: | 
|  | if pip not in segbits: | 
|  |  | 
|  | # A "default" pip | 
|  | if pip in ppips.keys() and ppips[pip] == "default": | 
|  | missing_bits += 1 | 
|  | logging.error( | 
|  | " WARNING: no bits for pip '{}' which defaults to VCC_WIRE" | 
|  | .format(pip)) | 
|  | exitcode = -1 | 
|  |  | 
|  | # A regular pip | 
|  | else: | 
|  | missing_bits += 1 | 
|  | logging.error( | 
|  | " MISSING: no bits for pip '{}'".format(pip)) | 
|  | exitcode = -1 | 
|  |  | 
|  | # The pip has segbits | 
|  | else: | 
|  | known_bits += 1 | 
|  |  | 
|  | # Report missing bit count | 
|  | if missing_bits > 0: | 
|  | logging.critical( | 
|  | " MISSING: no bits for {}/{} pips!".format( | 
|  | missing_bits, missing_bits + known_bits)) | 
|  | exitcode = -1 | 
|  | else: | 
|  | logging.critical(" OK: no missing bits") | 
|  |  | 
|  | # No pips | 
|  | else: | 
|  | logging.warning(" OK: no pips") | 
|  |  | 
|  | return exitcode | 
|  |  | 
|  |  | 
|  | # ============================================================================= | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | sys.exit(main(sys.argv)) |