mirror of https://github.com/openXC7/prjxray.git
302 lines
8.1 KiB
Python
Executable File
302 lines
8.1 KiB
Python
Executable File
#!/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():
|
|
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
|
os.getenv("XRAY_DATABASE")), "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(
|
|
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 = prjxraydb.Database()
|
|
|
|
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?')
|
|
|
|
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.bits_file, args.segnames, args.z, args.b, args.d, args.D,
|
|
args.verbose)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|