diff --git a/utils/bits2fasm.py b/utils/bits2fasm.py new file mode 100755 index 00000000..bb0cd52c --- /dev/null +++ b/utils/bits2fasm.py @@ -0,0 +1,241 @@ +#!/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 +However, as of 2018-10-16, the output is being parsed to create FASM, +so be mindful when changing output format + +TODO: +''' + +import sys, os, json, re + + +class NoDB(Exception): + pass + + +def line(s=''): + print(s) + + +def comment(s): + print('# %s' % s) + + +# cache +segbitsdb = dict() + + +# TODO: migrate to library +def get_database(segtype): + if segtype in segbitsdb: + return segbitsdb[segtype] + + main_fn = "%s/%s/segbits_%s.db" % ( + os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), + segtype.lower()) + int_fn = "%s/%s/segbits_int_%s.db" % ( + os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), + segtype[-1].lower()) + + if not os.path.exists(main_fn) or not os.path.exists(int_fn): + raise NoDB(segtype) + + segbitsdb[segtype] = list() + + with open(main_fn, "r") as f: + for line in f: + line = line.split() + segbitsdb[segtype].append(line) + + with open(int_fn, "r") as f: + for line in f: + line = line.split() + segbitsdb[segtype].append(line) + + return segbitsdb[segtype] + + +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 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) + + +def seg_decode(seginfo, segbits): + fasms = set() + + try: + for entry in get_database(seginfo["type"]): + if not tagmatch(entry, segbits): + continue + tag_matched(entry, segbits) + fasms.add('%s.%s 1' % (seginfo['tile_name'], entry[0])) + except NoDB: + print("WARNING: failed to load DB for %s" % seginfo["type"]) + return fasms + + +def handle_segment(segname, grid, bitdata): + + assert segname + + # only print bitstream tiles + if segname not in grid["segments"]: + return + seginfo = grid["segments"][segname] + + segbits = mk_segbits(seginfo, bitdata) + + fasms = seg_decode(seginfo, segbits) + + # Found something to print? + if len(segbits) == 0 and len(fasms) == 0: + return + + line('') + comment("seg %s" % (segname, )) + + for fasm in sorted(fasms): + print(fasm) + + if len(segbits) > 0: + comment('WARNING: %u unknown bits' % len(segbits)) + for bit in sorted(segbits): + comment("bit %s" % bit) + + +def load_bitdata(bits_file): + bitdata = dict() + + with open(bits_file, "r") as f: + for line in f: + line = line.split("_") + frame = int(line[1], 16) + wordidx = int(line[2], 10) + bitidx = int(line[3], 10) + + if frame not in bitdata: + bitdata[frame] = dict() + + if wordidx not in bitdata[frame]: + bitdata[frame][wordidx] = set() + + bitdata[frame][wordidx].add(bitidx) + return bitdata + + +def mk_grid(): + '''Load tilegrid, flattening all blocks into one dictionary''' + + with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"), + os.getenv("XRAY_DATABASE")), "r") as f: + new_grid = json.load(f) + + # TODO: Migrate to new tilegrid format via library. + grid = {'tiles': new_grid, 'segments': {}} + + for tile_name, tile in grid['tiles'].items(): + bits = tile.get('bits', None) + if not bits: + continue + for block_name, block in bits.items(): + segname = mksegment(tile_name, block_name) + grid['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 grid + + +def mksegment(tile_name, block_name): + '''Create a segment name''' + return '%s:%s' % (tile_name, block_name) + + +def tile_segnames(grid): + ret = [] + for tile_name, tile in grid['tiles'].items(): + for block_name in tile['bits'].keys(): + ret.append(mksegment(tile_name, block_name)) + return ret + + +def run(bits_file, segnames, verbose=False): + grid = mk_grid() + + bitdata = load_bitdata(bits_file) + + # 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(grid)) + + comment('Segments: %u' % len(segnames)) + + # XXX: previously this was sorted by address, not name + # revisit? + for segname in segnames: + handle_segment(segname, grid, bitdata) + + +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?') + + parser.add_argument('--verbose', action='store_true', help='') + 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.bits_file, args.segnames, verbose=args.verbose) + + +if __name__ == '__main__': + main() diff --git a/utils/environment.sh b/utils/environment.sh index a8e20cf5..b90cc09a 100644 --- a/utils/environment.sh +++ b/utils/environment.sh @@ -4,6 +4,7 @@ while [ -h "$XRAY_ENV_PATH" ]; do # resolve $XRAY_ENV_PATH until the file is no XRAY_ENV_PATH="$(readlink "$XRAY_ENV_PATH")" [[ $XRAY_ENV_PATH != /* ]] && XRAY_ENV_PATH="$XRAY_UTILS_DIR/$XRAY_ENV_PATH" # if $XRAY_ENV_PATH was a relative symlink, we need to resolve it relative to the path where the symlink file was located done +export PYTHONPATH="${XRAY_DIR}:$PYTHONPATH" export XRAY_UTILS_DIR="$( cd -P "$( dirname "$XRAY_ENV_PATH" )" && pwd )" export XRAY_DIR="$( dirname "$XRAY_UTILS_DIR" )" @@ -21,3 +22,6 @@ export XRAY_DBCHECK="bash ${XRAY_UTILS_DIR}/dbcheck.sh" export XRAY_MASKMERGE="bash ${XRAY_UTILS_DIR}/maskmerge.sh" export XRAY_SEGMATCH="${XRAY_TOOLS_DIR}/segmatch" export XRAY_SEGPRINT="python3 ${XRAY_UTILS_DIR}/segprint.py" +export XRAY_BITS2FASM="python3 ${XRAY_UTILS_DIR}/bits2fasm.py" + + diff --git a/utils/segprint.py b/utils/segprint.py index d5d4d6be..5c9f6029 100755 --- a/utils/segprint.py +++ b/utils/segprint.py @@ -19,6 +19,7 @@ class NoDB(Exception): segbitsdb = dict() +# TODO: migrate to library def get_database(segtype): if segtype in segbitsdb: return segbitsdb[segtype] @@ -134,23 +135,33 @@ def print_unknown_bits(grid, bitdata): print("bit_%08x_%03d_%02d" % (frame, wordidx, bitidx)) -def seg_decode(flag_decode_emit, seginfo, segbits, segtags): +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) + + +def seg_decode(flag_decode_emit, seginfo, segbits): + segtags = set() try: for entry in get_database(seginfo["type"]): - match_entry = True - for bit in entry[1:]: - if bit[0] != "!" and bit not in segbits: - match_entry = False - if bit[0] == "!" and bit[1:] in segbits: - match_entry = False - if match_entry: - for bit in entry[1:]: - if bit[0] != "!": - segbits.remove(bit) - if flag_decode_emit: - segtags.add(entry[0]) + if not tagmatch(entry, segbits): + continue + tag_matched(entry, segbits) + if flag_decode_emit: + segtags.add(entry[0]) except NoDB: print("WARNING: failed to load DB for %s" % seginfo["type"]) + return segtags def handle_segment( @@ -177,16 +188,19 @@ def handle_segment( return seginfo = grid["segments"][segname] - segtags = set() segbits = mk_segbits(seginfo, bitdata) if flag_decode_emit or flag_decode_omit: - seg_decode(flag_decode_emit, seginfo, segbits, segtags) + segtags = seg_decode(flag_decode_emit, seginfo, segbits) + else: + segtags = set() # Found something to print? - if not omit_empty_segs or len(segbits) > 0 or len(segtags) > 0: - print() - print("seg %s" % (segname, )) + 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) @@ -315,10 +329,11 @@ def main(): action='store_true', help='decode known segment bits and omit them in the output') parser.add_argument('bits_file', help='') - parser.add_argument('tiles', nargs='*', help='') + parser.add_argument( + 'segnames', nargs='*', help='List of tile or tile:block to print') args = parser.parse_args() - run(args.bits_file, args.tiles, args.z, args.b, args.d, args.D) + run(args.bits_file, args.segnames, args.z, args.b, args.d, args.D) if __name__ == '__main__': diff --git a/utils/segprint2fasm.py b/utils/segprint2fasm.py deleted file mode 100755 index 6620c6d9..00000000 --- a/utils/segprint2fasm.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python3 - -import os -import re -import sys -import json - -enumdb = dict() - - -def get_enums(segtype): - if segtype in enumdb: - return enumdb[segtype] - - enumdb[segtype] = {} - - def process(l): - l = l.strip() - - # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 - parts = line.split() - name = parts[0] - bit_vals = parts[1:] - - # Assumption - # only 1 bit => non-enumerated value - enumdb[segtype][name] = len(bit_vals) != 1 - - with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"), - os.getenv("XRAY_DATABASE"), segtype), - "r") as f: - for line in f: - process(line) - - with open("%s/%s/segbits_int_%s.db" % - (os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), - segtype[-1]), "r") as f: - for line in f: - process(line) - - return enumdb[segtype] - - -def isenum(segtype, tag): - return get_enums(segtype)[tag] - - -def tag2fasm(grid, seg, tag): - '''Given tilegrid, segment name and tag, return fasm directive''' - segj = grid['segments'][seg] - - m = re.match(r'([A-Za-z0-9_]+)[.](.*)', tag) - tile_type = m.group(1) - tag_post = m.group(2) - - # Find associated tile - for tile in segj['tiles']: - if grid['tiles'][tile]['type'] == tile_type: - break - else: - raise Exception("Couldn't find tile type %s" % tile_type) - - if not isenum(segj['type'], tag): - return '%s.%s 1' % (tile, tag_post) - else: - # Make the selection an argument of the configruation - m = re.match(r'(.*)[.]([A-Za-z0-9_]+)', tag_post) - which = m.group(1) - value = m.group(2) - return '%s.%s %s' % (tile, which, value) - - -def run(f_in, f_out, sparse=False): - with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"), - os.getenv("XRAY_DATABASE")), "r") as f: - new_grid = json.load(f) - - # TODO: Migrate to new tilegrid format via library. - grid = {'tiles': new_grid, 'segments': {}} - - for tilename, tile in grid['tiles'].items(): - if 'segment' in tile: - segment = tile['segment'] - - if segment not in grid['segments']: - grid['segments'][segment] = { - 'baseaddr': ( - tile['baseaddr'], - tile['offset'], - ), - 'type': tile['segment_type'], - 'frames': tile['frames'], - 'words': tile['words'], - 'tiles': [tilename] - } - else: - assert grid['segments'][segment]['baseaddr'] == ( - tile['baseaddr'], - tile['offset'], - ) - assert grid['segments'][segment]['type'] == tile[ - 'segment_type'] - assert grid['segments'][segment]['frames'] == tile['frames'] - assert grid['segments'][segment]['words'] == tile['words'] - - grid['segments'][segment]['tiles'].append(tilename) - - seg = None - for l in f_in: - l = l.strip() - if not l: - continue - # seg SEG_CLBLM_L_X10Y102 - # tag CLBLM_L.SLICEM_X0.ALUT.INIT[00] - m = re.match('(seg|tag) (.*)', l) - if not m: - raise Exception("Invalid line %s" % l) - type = m.group(1) - if type == 'seg': - seg = m.group(2) - elif type == 'tag': - f_out.write(tag2fasm(grid, seg, m.group(2)) + '\n') - else: - raise Exception("Invalid type %s" % type) - - -if __name__ == '__main__': - import argparse - - parser = argparse.ArgumentParser( - description='Convert segprint -d output to .fasm file (FPGA assembly)') - - parser.add_argument( - 'fn_in', default='/dev/stdin', nargs='?', help='Input segment file') - parser.add_argument( - 'fn_out', default='/dev/stdout', nargs='?', help='Output .fasm file') - - args = parser.parse_args() - run(open(args.fn_in, 'r'), open(args.fn_out, 'w'))