From 96ee4ac44983837848494c490a599d0fb3deccee Mon Sep 17 00:00:00 2001 From: John McMaster Date: Mon, 10 Dec 2018 14:42:53 -0800 Subject: [PATCH] checkdb: check tile overlap Signed-off-by: John McMaster --- prjxray/util.py | 54 +++++++++++++++++++++ utils/checkdb.py | 116 +++++++++++++++++++++++++++------------------- utils/segprint.py | 14 +----- 3 files changed, 124 insertions(+), 60 deletions(-) diff --git a/prjxray/util.py b/prjxray/util.py index 09377c83..0e8ba60d 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -96,3 +96,57 @@ def parse_db_line(line): # 100_319 assert re.match(r'[!]*[0-9]+_[0-9]+', bit), "Invalid bit: %s" % bit return tag, bits, None + + +def parse_tagbit(x): + # !30_07 + if x[0] == '!': + isset = False + numstr = x[1:] + else: + isset = True + numstr = x + frame, word = numstr.split("_") + # second part forms a tuple refereced in sets + return (isset, (int(frame, 10), int(word, 10))) + + +def gen_tile_bits(db_root, tilej, strict=False, verbose=False): + ''' + For given tile yield + (absolute address, absolute FDRI bit offset, tag) + + For each address space + Find applicable files + For each tag bit in those files, calculate absolute address and bit offsets + + Sample file names: + segbits_clbll_l.db + segbits_int_l.db + segbits_bram_l.block_ram.db + ''' + for block_type, blockj in tilej["bits"].items(): + baseaddr = int(blockj["baseaddr"], 0) + bitbase = 32 * blockj["offset"] + + if block_type == "CLB_IO_CLK": + fn = "%s/segbits_%s.db" % (db_root, tilej["type"].lower()) + else: + fn = "%s/segbits_%s.db.%s" % ( + db_root, tilej["type"].lower(), block_type.lower()) + # tilegrid runs a lot earlier than fuzzers + # may not have been created yet + verbose and print("Check %s: %s" % (fn, os.path.exists(fn))) + if strict: + assert os.path.exists(fn) + elif not os.path.exists(fn): + continue + + with open(fn, "r") as f: + for line in f: + tag, bits, mode = parse_db_line(line) + assert mode is None + for bitstr in bits: + # 31_06 + _bit_inv, (bit_addroff, bit_bitoff) = parse_tagbit(bitstr) + yield (baseaddr + bit_addroff, bitbase + bit_bitoff, tag) diff --git a/utils/checkdb.py b/utils/checkdb.py index 29bae1b4..4a586957 100644 --- a/utils/checkdb.py +++ b/utils/checkdb.py @@ -1,93 +1,114 @@ #!/usr/bin/env python3 - ''' 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 ''' -import sys, re from prjxray import util +from prjxray import db as prjxraydb +import os import parsedb #from prjxray import db as prjxraydb import glob +def make_tile_mask(db_root, tile_name, tilej, strict=False, verbose=False): + ''' + 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 util.gen_tile_bits( + db_root, tilej, strict=strict, verbose=verbose): + 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''' - for bit_fn in glob.glob('%s/segbits_*.db'): + files = 0 + for bit_fn in glob.glob('%s/segbits_*.db' % db_root): + 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) - for bit_fn in glob.glob('%s/mask_*.db'): + 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) - -def process_db(tile_type, process, verbose): - #ttdb = db.get_tile_type(tile_type) - - fns = [ttdb.tile_dbs.segbits, ttdb.tile_dbs.ppips] - verbose and print("process_db(%s): %s" % (tile_type, fns)) - for fn in fns: - if fn: - with open(fn, "r") as f: - for line in f: - process(util.parse_db_line(line)) + files += 1 + print("mask_*.db: %d okay" % files) -def check_seg_overlap(db_root, verbose=False): +def check_tile_overlap(db, db_root, strict=False, 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 ''' - # key: (address, bit index) - # val: sample description of where it came from (there may be multiple, only one) - used = dict() + mall = dict() tiles_checked = 0 - for tile_name, tile in db.tilegrid.items(): - #ttdb = db.get_tile_type(tile["type"]) - # FIXME: check BRAM - bitj = tile["bits"].get("CLB_IO_CLK", None) - if not bitj: + for tile_name, tilej in db.tilegrid.items(): + #for tile_name, tilej in [("CLBLL_L_X12Y138", db.tilegrid["CLBLL_L_X12Y138"])]: + mtile = make_tile_mask( + db_root, tile_name, tilej, strict=strict, verbose=verbose) + verbose and print( + "Checking %s, type %s, bits: %s" % + (tile_name, tilej["type"], len(mtile))) + if len(mtile) == 0: continue - verbose and print("Checking %s, type %s" % (tile_name, tile["type"])) - baseaddr = int(bitj["baseaddr"], 0) - bitbase = 32 * bitj["offset"] - # Create tile mask - tile_used = dict() - def process(lparse): - tag, bits, mode = lparse - assert mode is None - for (bit_addroff, bit_bitoff) in bits: - tile_used[(baseaddr + bit_addroff, bitbase + bit_bitoff)] = "%s.%s" % (tile_name, tag) - process_db(tile["type"], process, verbose=verbose) - - # See if tile mask intersects any existing bits - for (waddr, bitaddr), tile_desc in tile_used.items(): - used_desc = used.get((waddr, bitaddr), None) - if used_desc: - raise ValueError("Collision at %08X:%04X: had %s, got %s" % (waddr, bitaddr, used_desc, tile_desc)) - used[(waddr, bitaddr)] = tile_desc + collisions = set(mall.keys()).intersection(set(mtile.keys())) + if collisions: + print("ERROR: %s collisions" % len(collisions)) + for ck in sorted(collisions): + addr, bitaddr = ck + print( + " %08X_%04X: had %s, got %s" % + (addr, bitaddr, 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(used)) + print("Checked %s tiles, %s bits" % (tiles_checked, len(mall))) -def run(db_root, verbose=False): +def run(db_root, strict=False, 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) 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()) ''' - check_seg_overlap(db_root, verbose=verbose) + verbose and print("") + + print("Checking aggregate dir...") + check_tile_overlap(db, db_root, strict=strict, verbose=verbose) def main(): @@ -97,10 +118,11 @@ def main(): description="Parse a db repository, checking for consistency") util.db_root_arg(parser) + parser.add_argument('--strict', action='store_true', help='') parser.add_argument('--verbose', action='store_true', help='') args = parser.parse_args() - run(args.db_root, verbose=args.verbose) + run(args.db_root, strict=args.strict, verbose=args.verbose) if __name__ == '__main__': diff --git a/utils/segprint.py b/utils/segprint.py index fabad99b..18819b2c 100755 --- a/utils/segprint.py +++ b/utils/segprint.py @@ -46,22 +46,10 @@ def get_database(db, tile_type, verbose=False): parts = l.split() name = parts[0] - def parsetag(x): - # !30_07 - if x[0] == '!': - isset = False - numstr = x[1:] - else: - isset = True - numstr = x - frame, word = numstr.split("_") - # second part forms a tuple refereced in sets - return (isset, (int(frame, 10), int(word, 10))) - if parts[1] == 'always' or parts[1] == 'hint': tagbits = [] else: - tagbits = [parsetag(x) for x in parts[1:]] + tagbits = [util.parse_tagbit(x) for x in parts[1:]] tags.append(list([name] + tagbits))