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