#!/usr/bin/env python3 ''' Take raw .bits files and decode them to higher level functionality This output is intended for debugging and not directly related to FASM ''' import sys, os, json, re from prjxray import bitstream from prjxray import db as prjxraydb class NoDB(Exception): pass # cache segbitsdb = dict() # int and sites are loaded together so that bit coverage can be checked together # however, as currently written, each segment is essentially printed twice def process_db(db, tile_type, process, verbose): print(db.get_tile_types()) 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(line) def get_database(db, tile_type, verbose=False): tags = list() if tile_type in segbitsdb: return segbitsdb[tile_type] def process(l): tags.append(l.split()) process_db(db, tile_type, process, verbose=verbose) if len(tags) == 0: raise NoDB(tile_type) segbitsdb[tile_type] = tags return tags def mk_segbits(seginfo, bitdata): baseframe = int(seginfo["baseaddr"][0], 16) basewordidx = int(seginfo["baseaddr"][1]) numframes = int(seginfo["frames"]) numwords = int(seginfo["words"]) segbits = set() for frame in range(baseframe, baseframe + numframes): if frame not in bitdata: continue for wordidx in range(basewordidx, basewordidx + numwords): if wordidx not in bitdata[frame]: continue for bitidx in bitdata[frame][wordidx]: segbits.add( "%02d_%02d" % (frame - baseframe, 32 * (wordidx - basewordidx) + bitidx)) return segbits def print_unknown_bits(segments, bitdata): ''' Print bits not covered by known tiles ''' # Index all known locations # seggrames[address] = set() # where set contains word numbers segframes = dict() for segname, segdata in segments.items(): framebase = int(segdata["baseaddr"][0], 16) for i in range(segdata["frames"]): words = segframes.setdefault(framebase + i, set()) for j in range(segdata["baseaddr"][1], segdata["baseaddr"][1] + segdata["words"]): words.add(j) # print uncovered locations print('Non-database bits:') for frame in sorted(bitdata.keys()): for wordidx in sorted(bitdata[frame].keys()): if frame in segframes and wordidx in segframes[frame]: continue for bitidx in sorted(bitdata[frame][wordidx]): print("bit_%08x_%03d_%02d" % (frame, wordidx, bitidx)) def tagmatch(entry, segbits): for bit in entry[1:]: if bit[0] != "!" and bit not in segbits: return False if bit[0] == "!" and bit[1:] in segbits: return False return True def tag_matched(entry, segbits): for bit in entry[1:]: if bit[0] != "!": segbits.remove(bit) decode_warnings = set() def seg_decode(flag_decode_emit, db, seginfo, segbits, verbose=False): segtags = set() # already failed? if seginfo["type"] in decode_warnings: return segtags try: for entry in get_database(db, seginfo["type"], verbose=verbose): if not tagmatch(entry, segbits): continue tag_matched(entry, segbits) if flag_decode_emit: segtags.add(entry[0]) except NoDB: verbose and print( "WARNING: failed to load DB for %s" % seginfo["type"]) decode_warnings.add(seginfo["type"]) return segtags def handle_segment( db, segname, segments, bitdata, flag_decode_emit, flag_decode_omit, omit_empty_segs, verbose=False): assert segname # only print bitstream tiles if segname not in segments: return seginfo = segments[segname] segbits = mk_segbits(seginfo, bitdata) if flag_decode_emit or flag_decode_omit: segtags = seg_decode( flag_decode_emit, db, seginfo, segbits, verbose=verbose) else: segtags = set() # Found something to print? if not (not omit_empty_segs or len(segbits) > 0 or len(segtags) > 0): return print() print("seg %s" % (segname, )) for bit in sorted(segbits): print("bit %s" % bit) for tag in sorted(segtags): print("tag %s" % tag) def mk_segments(tiles): segments = {} for tile_name, tile in tiles.items(): bits = tile.get('bits', None) if not bits: continue for block_name, block in bits.items(): segname = mksegment(tile_name, block_name) segments[segname] = { 'baseaddr': [ block['baseaddr'], block['offset'], ], 'type': tile['type'], 'frames': block['frames'], 'words': block['words'], 'tile_name': tile_name, 'block_name': block_name, } return segments def mk_grid(db_root): with open(os.path.join(db_root, "tilegrid.json"), "r") as f: tiles = json.load(f) '''Load tilegrid, flattening all blocks into one dictionary''' # TODO: Migrate to new tilegrid format via library. return tiles, mk_segments(tiles) def mksegment(tile_name, block_name): '''Create a segment name''' return '%s:%s' % (tile_name, block_name) def tile_segnames(tiles): ret = [] for tile_name, tile in tiles.items(): if 'bits' not in tile: continue for block_name in tile['bits'].keys(): ret.append(mksegment(tile_name, block_name)) return ret def run( db_root, bits_file, segnames, omit_empty_segs=False, flag_unknown_bits=False, flag_decode_emit=False, flag_decode_omit=False, verbose=False): tiles, segments = mk_grid(db_root) db = prjxraydb.Database(db_root) bitdata = bitstream.load_bitdata2(open(bits_file, "r")) if flag_unknown_bits: print_unknown_bits(segments, bitdata) # Default: print all if segnames: for i, segname in enumerate(segnames): # Default to common tile config area if tile given without explicit block if ':' not in segname: segnames[i] = mksegment(segname, 'CLB_IO_CLK') else: segnames = sorted(tile_segnames(tiles)) print('Segments: %u' % len(segnames)) # XXX: previously this was sorted by address, not name # revisit? for segname in segnames: handle_segment( db, segname, segments, bitdata, flag_decode_emit, flag_decode_omit, omit_empty_segs, verbose=verbose) def main(): import argparse # XXX: tool still works, but not well # need to eliminate segments entirely parser = argparse.ArgumentParser( description='XXX: does not print all data?') database_dir = os.getenv("XRAY_DATABASE_DIR") database = os.getenv("XRAY_DATABASE") db_root_kwargs = {} if database_dir is None or database is None: db_root_kwargs['required'] = True else: db_root_kwargs['required'] = False db_root_kwargs['default'] = os.path.join(database_dir, database) parser.add_argument('--db_root', help="Database root.", **db_root_kwargs) parser.add_argument('--verbose', action='store_true', help='') parser.add_argument( '-z', action='store_true', help="do not print a 'seg' header for empty segments") parser.add_argument( '-b', action='store_true', help='print bits outside of known segments') parser.add_argument( '-d', action='store_true', help='decode known segment bits and write them as tags') # XXX: possibly broken, or we have missing DB data parser.add_argument( '-D', action='store_true', help='decode known segment bits and omit them in the output') parser.add_argument('bits_file', help='') parser.add_argument( 'segnames', nargs='*', help='List of tile or tile:block to print') args = parser.parse_args() run( args.db_root, args.bits_file, args.segnames, args.z, args.b, args.d, args.D, args.verbose) if __name__ == '__main__': main()