|  | #!/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 | 
|  | ''' | 
|  | Check: | 
|  | -Individual files are valid | 
|  | -No overlap between any tile | 
|  |  | 
|  | TODO: | 
|  | Can we use prjxray? | 
|  | Relies on 074, which is too far into the process | 
|  | ''' | 
|  |  | 
|  | from prjxray import util | 
|  | from prjxray import db as prjxraydb | 
|  | import os | 
|  | import utils.parsedb as parsedb | 
|  | #from prjxray import db as prjxraydb | 
|  | import glob | 
|  |  | 
|  |  | 
|  | def gen_tile_bits(tile_segbits, tile_bits): | 
|  | ''' | 
|  | For given tile and corresponding db_file structure yield | 
|  | (absolute address, absolute FDRI bit offset, tag) | 
|  |  | 
|  | For each tag bit in the corresponding block_type entry, calculate absolute address and bit offsets | 
|  | ''' | 
|  |  | 
|  | for block_type in tile_segbits: | 
|  | assert block_type in tile_bits, "block type %s is not present in current tile" % block_type | 
|  |  | 
|  | block = tile_bits[block_type] | 
|  |  | 
|  | baseaddr = block.base_address | 
|  | bitbase = 32 * block.offset | 
|  | frames = block.frames | 
|  |  | 
|  | for tag in tile_segbits[block_type]: | 
|  | for bit in tile_segbits[block_type][tag]: | 
|  | # 31_06 | 
|  | word_column = bit.word_column | 
|  | word_bit = bit.word_bit | 
|  | assert word_column <= frames, "ERROR: bit out of bound --> tag: %s; word_column = %s; frames = %s" % ( | 
|  | tag, word_column, frames) | 
|  | yield word_column + baseaddr, word_bit + bitbase, tag | 
|  |  | 
|  |  | 
|  | def make_tile_mask(tile_segbits, tile_name, tile_bits): | 
|  | ''' | 
|  | Return dict | 
|  | key: (address, bit index) | 
|  | val: sample description of where it came from (there may be multiple, only one) | 
|  | ''' | 
|  |  | 
|  | # FIXME: fix mask files https://github.com/SymbiFlow/prjxray/issues/301 | 
|  | # in the meantime build them on the fly | 
|  | # We may want this to build them anyway | 
|  |  | 
|  | ret = dict() | 
|  | for absaddr, bitaddr, tag in gen_tile_bits(tile_segbits, tile_bits): | 
|  | name = "%s.%s" % (tile_name, tag) | 
|  | ret.setdefault((absaddr, bitaddr), name) | 
|  | return ret | 
|  |  | 
|  |  | 
|  | def parsedb_all(db_root, verbose=False): | 
|  | '''Verify .db files are individually valid''' | 
|  |  | 
|  | files = 0 | 
|  | for bit_fn in glob.glob('%s/segbits_*.db' % db_root): | 
|  | # Don't parse db files with fuzzer origin information | 
|  | if "origin_info" in bit_fn: | 
|  | continue | 
|  | verbose and print("Checking %s" % bit_fn) | 
|  | parsedb.run(bit_fn, fnout=None, strict=True, verbose=verbose) | 
|  | files += 1 | 
|  | print("segbits_*.db: %d okay" % files) | 
|  |  | 
|  | files = 0 | 
|  | for bit_fn in glob.glob('%s/mask_*.db' % db_root): | 
|  | verbose and print("Checking %s" % bit_fn) | 
|  | parsedb.run(bit_fn, fnout=None, strict=True, verbose=verbose) | 
|  | files += 1 | 
|  | print("mask_*.db: %d okay" % files) | 
|  |  | 
|  |  | 
|  | def check_tile_overlap(db, verbose=False): | 
|  | ''' | 
|  | Verifies that no two tiles use the same bit | 
|  |  | 
|  | Assume .db files are individually valid | 
|  | Create a mask for all the bits the tile type uses | 
|  | For each tile, create bitmasks over the entire bitstream for current part | 
|  | Throw an exception if two tiles share an address | 
|  | ''' | 
|  | mall = dict() | 
|  | tiles_type_done = dict() | 
|  | tile_segbits = dict() | 
|  | grid = db.grid() | 
|  | tiles_checked = 0 | 
|  |  | 
|  | for tile_name in grid.tiles(): | 
|  | tile_info = grid.gridinfo_at_tilename(tile_name) | 
|  | tile_type = tile_info.tile_type | 
|  | tile_bits = tile_info.bits | 
|  |  | 
|  | if tile_type not in tiles_type_done: | 
|  | segbits = db.get_tile_segbits(tile_type).segbits | 
|  | tile_segbits[tile_type] = segbits | 
|  |  | 
|  | # If segbits has zero length the tile_type is marked True in order to be skipped | 
|  | if len(segbits) == 0: | 
|  | tiles_type_done[tile_type] = True | 
|  | else: | 
|  | tiles_type_done[tile_type] = False | 
|  |  | 
|  | if tiles_type_done[tile_type]: | 
|  | continue | 
|  |  | 
|  | mtile = make_tile_mask(tile_segbits[tile_type], tile_name, tile_bits) | 
|  | verbose and print( | 
|  | "Checking %s, type %s, bits: %s" % | 
|  | (tile_name, tile_type, len(mtile))) | 
|  | if len(mtile) == 0: | 
|  | continue | 
|  |  | 
|  | collisions = set() | 
|  | for bits in mtile.keys(): | 
|  | if bits in mall.keys(): | 
|  | collisions.add(bits) | 
|  |  | 
|  | if collisions: | 
|  | print("ERROR: %s collisions" % len(collisions)) | 
|  | for ck in sorted(collisions): | 
|  | addr, bitaddr = ck | 
|  | word, bit = util.addr_bit2word(bitaddr) | 
|  | print( | 
|  | "  %s: had %s, got %s" % | 
|  | (util.addr2str(addr, word, bit), mall[ck], mtile[ck])) | 
|  | raise ValueError("%s collisions" % len(collisions)) | 
|  | mall.update(mtile) | 
|  | tiles_checked += 1 | 
|  | print("Checked %s tiles, %s bits" % (tiles_checked, len(mall))) | 
|  |  | 
|  |  | 
|  | def run(db_root, part, verbose=False): | 
|  | # Start by running a basic check on db files | 
|  | print("Checking individual .db...") | 
|  | parsedb_all(db_root, verbose=verbose) | 
|  |  | 
|  | # Now load and verify tile consistency | 
|  | db = prjxraydb.Database(db_root, part) | 
|  | db._read_tilegrid() | 
|  | ''' | 
|  | these don't load properly without .json files | 
|  | See: https://github.com/SymbiFlow/prjxray/issues/303 | 
|  | db._read_tile_types() | 
|  | print(db.tile_types.keys()) | 
|  | ''' | 
|  |  | 
|  | verbose and print("") | 
|  |  | 
|  | print("Checking aggregate dir...") | 
|  | check_tile_overlap(db, verbose=verbose) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | import argparse | 
|  |  | 
|  | parser = argparse.ArgumentParser( | 
|  | description="Parse a db repository, checking for consistency") | 
|  |  | 
|  | util.db_root_arg(parser) | 
|  | util.part_arg(parser) | 
|  | parser.add_argument('--verbose', action='store_true', help='') | 
|  | args = parser.parse_args() | 
|  |  | 
|  | run(args.db_root, args.part, verbose=args.verbose) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |