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