mirror of https://github.com/openXC7/prjxray.git
Merge pull request #220 from mcmasterg/segprint_bram
Segprint: print related tiles
This commit is contained in:
commit
eddf249df1
|
|
@ -1,7 +1,9 @@
|
|||
#!/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
|
||||
|
||||
A segment as currently used is defined as a tile type + a memory region
|
||||
Ex: BRAM_L_X6Y100:CLB_IO_CLK
|
||||
'''
|
||||
|
||||
import sys, os, json, re
|
||||
|
|
@ -20,7 +22,6 @@ 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]
|
||||
|
|
@ -39,7 +40,29 @@ def get_database(db, tile_type, verbose=False):
|
|||
return segbitsdb[tile_type]
|
||||
|
||||
def process(l):
|
||||
tags.append(l.split())
|
||||
# l like: CLBLL_L.SLICEL_X0.AMUX.CY !30_07 !30_11 30_06 30_08
|
||||
# Parse tags to do math when multiple tiles share an address space
|
||||
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:]]
|
||||
|
||||
tags.append(list([name] + tagbits))
|
||||
|
||||
process_db(db, tile_type, process, verbose=verbose)
|
||||
|
||||
|
|
@ -51,22 +74,33 @@ def get_database(db, tile_type, verbose=False):
|
|||
|
||||
|
||||
def mk_segbits(seginfo, bitdata):
|
||||
baseframe = int(seginfo["baseaddr"][0], 16)
|
||||
basewordidx = int(seginfo["baseaddr"][1])
|
||||
numframes = int(seginfo["frames"])
|
||||
numwords = int(seginfo["words"])
|
||||
'''
|
||||
Given a tile memory region (seginfo), return list of bits in that region
|
||||
|
||||
seginfo: mk_segments()s object supplying address range
|
||||
bitdata: all bits in the entire bitstream
|
||||
'''
|
||||
|
||||
segbits = set()
|
||||
for frame in range(baseframe, baseframe + numframes):
|
||||
|
||||
block = seginfo["block"]
|
||||
baseaddr = int(block["baseaddr"], 0)
|
||||
frames = block["frames"]
|
||||
word_offset = block["offset"]
|
||||
words = block["words"]
|
||||
|
||||
for frame in range(baseaddr, baseaddr + frames):
|
||||
if frame not in bitdata:
|
||||
continue
|
||||
for wordidx in range(basewordidx, basewordidx + numwords):
|
||||
for wordidx in range(word_offset, word_offset + words):
|
||||
if wordidx not in bitdata[frame]:
|
||||
continue
|
||||
for bitidx in bitdata[frame][wordidx]:
|
||||
segbits.add(
|
||||
"%02d_%02d" %
|
||||
(frame - baseframe, 32 * (wordidx - basewordidx) + bitidx))
|
||||
frame_addr = frame - baseaddr
|
||||
bit_addr = 32 * (wordidx - word_offset) + bitidx
|
||||
#segbits.add( "%02d_%02d" % (frame_addr, word_addr))
|
||||
segbits.add((frame_addr, bit_addr))
|
||||
|
||||
return segbits
|
||||
|
||||
|
||||
|
|
@ -79,12 +113,13 @@ def print_unknown_bits(segments, bitdata):
|
|||
# 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"]):
|
||||
for segname, segment in segments.items():
|
||||
block = segment["block"]
|
||||
framebase = int(block["baseaddr"][0], 16)
|
||||
for i in range(block["frames"]):
|
||||
words = segframes.setdefault(framebase + i, set())
|
||||
for j in range(segdata["baseaddr"][1],
|
||||
segdata["baseaddr"][1] + segdata["words"]):
|
||||
for j in range(block["baseaddr"][1],
|
||||
block["baseaddr"][1] + block["words"]):
|
||||
words.add(j)
|
||||
|
||||
# print uncovered locations
|
||||
|
|
@ -98,66 +133,165 @@ def print_unknown_bits(segments, bitdata):
|
|||
|
||||
|
||||
def tagmatch(entry, segbits):
|
||||
'''Does tag appear in segbits?'''
|
||||
|
||||
# Entry like "CLBLL_L.SLICEL_X0.AMUX.CY !30_07 !30_11 30_06 30_08".split()
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!" and bit not in segbits:
|
||||
return False
|
||||
if bit[0] == "!" and bit[1:] in segbits:
|
||||
isset, bitaddr = bit
|
||||
|
||||
# Reject if bit polarity is incorrect
|
||||
if bitaddr not in segbits if isset else bitaddr in segbits:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def tag_matched(entry, segbits):
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!":
|
||||
segbits.remove(bit)
|
||||
isset, bitaddr = bit
|
||||
if isset:
|
||||
segbits.remove(bitaddr)
|
||||
|
||||
|
||||
# tile types that failed to decode
|
||||
decode_warnings = set()
|
||||
|
||||
|
||||
def seg_decode(flag_decode_emit, db, seginfo, segbits, verbose=False):
|
||||
def seg_decode(db, seginfo, segbits, segments, verbose=False):
|
||||
'''
|
||||
Remove matched tags from segbits
|
||||
Returns a list of all matched tags
|
||||
'''
|
||||
|
||||
segtags = set()
|
||||
|
||||
# already failed?
|
||||
if seginfo["type"] in decode_warnings:
|
||||
return segtags
|
||||
# Valid addresses for refereced tiles
|
||||
ref_block = seginfo["block"]
|
||||
# ref_frame_as = (int(ref_block["baseaddr"], 0), int(ref_block["baseaddr"], 0) + ref_block["frames"] - 1)
|
||||
ref_frame_as = (0, ref_block["frames"] - 1)
|
||||
ref_bit_as = (
|
||||
32 * ref_block["offset"],
|
||||
32 * (ref_block["offset"] + ref_block["words"]) - 1)
|
||||
|
||||
def process(cmp_seginfo, ref_tile_name):
|
||||
tile_type = cmp_seginfo["tile"]["type"]
|
||||
|
||||
# already failed?
|
||||
if tile_type in decode_warnings:
|
||||
return
|
||||
|
||||
try:
|
||||
entries = get_database(db, tile_type, verbose=verbose)
|
||||
except NoDB:
|
||||
verbose and print("WARNING: failed to load DB for %s" % tile_type)
|
||||
assert tile_type != 'BRAM_L'
|
||||
decode_warnings.add(tile_type)
|
||||
return
|
||||
|
||||
cmp_block = cmp_seginfo["block"]
|
||||
ref_frame_delta = int(cmp_block["baseaddr"], 0) - int(
|
||||
ref_block["baseaddr"], 0)
|
||||
ref_bit_delta = 32 * (cmp_block["offset"] - ref_block["offset"])
|
||||
|
||||
def adjust_entry_addr(entry):
|
||||
'''Return bits that apply to this tile at the correct address'''
|
||||
tagname = entry[0]
|
||||
bits = entry[1:]
|
||||
if len(bits) == 0:
|
||||
return None
|
||||
ret = [tagname]
|
||||
|
||||
def adjust_entry():
|
||||
'''Adjust entry in another tile address space to be in our reference tile address space'''
|
||||
for isset, old_bitaddr, in bits:
|
||||
old_frame_addr, old_bit_addr = old_bitaddr
|
||||
new_frame_addr = old_frame_addr + ref_frame_delta
|
||||
if not (ref_frame_as[0] <= new_frame_addr <=
|
||||
ref_frame_as[1]):
|
||||
verbose and print(
|
||||
"out frame range: %d <= %d <= %d" %
|
||||
(ref_frame_as[0], new_frame_addr, ref_frame_as[1]))
|
||||
return False
|
||||
|
||||
new_bit_addr = old_bit_addr + ref_bit_delta
|
||||
# Verify in range of original tile
|
||||
# This can happen if a smaller tile references a larger tile
|
||||
if not (ref_bit_as[0] <= new_bit_addr <= ref_bit_as[1]):
|
||||
verbose and print(
|
||||
"out bit range: %d <= %d <= %d" %
|
||||
(ref_bit_as[0], new_bit_addr, ref_bit_as[1]))
|
||||
return False
|
||||
ret.append((isset, (new_frame_addr, new_bit_addr)))
|
||||
verbose and print(
|
||||
"ent %02d_%02d => %02d_%02d" % (
|
||||
old_frame_addr, old_bit_addr, new_frame_addr,
|
||||
new_bit_addr))
|
||||
return True
|
||||
|
||||
if not adjust_entry():
|
||||
return None
|
||||
return ret
|
||||
|
||||
for entry in entries:
|
||||
if ref_tile_name:
|
||||
entry = adjust_entry_addr(entry)
|
||||
if entry is None:
|
||||
continue
|
||||
verbose and print('adjusted entry', entry)
|
||||
|
||||
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"])
|
||||
tagname = entry[0]
|
||||
# Prefix matches not from this tile
|
||||
if ref_tile_name:
|
||||
segtags.add('%s:%s' % (ref_tile_name, tagname))
|
||||
else:
|
||||
segtags.add(tagname)
|
||||
|
||||
# Reference tile
|
||||
process(seginfo, None)
|
||||
# Tiles that share our address space
|
||||
for (ref_tile_name, cmp_block_name) in seginfo['segtiles']:
|
||||
process(
|
||||
segments[mksegment(ref_tile_name, cmp_block_name)], ref_tile_name)
|
||||
|
||||
return segtags
|
||||
|
||||
|
||||
def print_seg(segname, segbits, segtags, decode_emit):
|
||||
'''Print segment like used by segmaker/segmatch'''
|
||||
|
||||
print("seg %s" % (segname, ))
|
||||
|
||||
# Bits that weren't decoded
|
||||
for bit in sorted(segbits):
|
||||
print("bit %02d_%02d" % bit)
|
||||
|
||||
if decode_emit:
|
||||
for tag in sorted(segtags):
|
||||
print("tag %s" % tag)
|
||||
|
||||
|
||||
def handle_segment(
|
||||
db,
|
||||
segname,
|
||||
segments,
|
||||
bitdata,
|
||||
flag_decode_emit,
|
||||
flag_decode_omit,
|
||||
decode_emit,
|
||||
decode_omit,
|
||||
omit_empty_segs,
|
||||
segments,
|
||||
verbose=False):
|
||||
|
||||
assert segname
|
||||
seginfo = segments[segname]
|
||||
|
||||
# only print bitstream tiles
|
||||
if segname not in segments:
|
||||
return
|
||||
seginfo = segments[segname]
|
||||
#if segname not in segments:
|
||||
# return
|
||||
|
||||
segbits = mk_segbits(seginfo, bitdata)
|
||||
|
||||
if flag_decode_emit or flag_decode_omit:
|
||||
segtags = seg_decode(
|
||||
flag_decode_emit, db, seginfo, segbits, verbose=verbose)
|
||||
if decode_emit or decode_omit:
|
||||
segtags = seg_decode(db, seginfo, segbits, segments, verbose=verbose)
|
||||
else:
|
||||
segtags = set()
|
||||
|
||||
|
|
@ -166,52 +300,94 @@ def handle_segment(
|
|||
return
|
||||
|
||||
print()
|
||||
print("seg %s" % (segname, ))
|
||||
print_seg(segname, segbits, segtags, decode_emit)
|
||||
|
||||
for bit in sorted(segbits):
|
||||
print("bit %s" % bit)
|
||||
|
||||
for tag in sorted(segtags):
|
||||
print("tag %s" % tag)
|
||||
def overlap(a, b):
|
||||
return a[0] <= b[0] <= a[1] or b[0] <= a[0] <= b[1]
|
||||
|
||||
|
||||
def mk_segtiles(tiles):
|
||||
'''
|
||||
Return a dictionary of tile_name:tiles
|
||||
Where tiles is a list of tiles that are in our address space
|
||||
|
||||
Assumption: tiles in the same minor address region have the same base address and number frames
|
||||
|
||||
As DB is written, not all have the same number of frames
|
||||
Ex: CLBLM_R_X7Y108 36 frames, INT_R_X7Y108 28 frames
|
||||
We could check for this, but don't think its worth the effort
|
||||
Maybe this should be corrected in the DB?
|
||||
'''
|
||||
|
||||
segtiles = {}
|
||||
|
||||
# Group by base address
|
||||
baseaddrs = {}
|
||||
for tile_name, tile in tiles.items():
|
||||
for block_name, block in tile['bits'].items():
|
||||
baseaddrs.setdefault(block["baseaddr"], []).append(
|
||||
(block["offset"], tile_name, block, block_name))
|
||||
|
||||
for baseaddr, values in baseaddrs.items():
|
||||
'''
|
||||
There are only 256 addresses per minor address
|
||||
Just do a set brute force search for now?
|
||||
Maybe too slow with the number of tiles
|
||||
|
||||
Around 50 IP blocks max per minor address
|
||||
'''
|
||||
|
||||
# Sort by block offset
|
||||
values = sorted(values)
|
||||
|
||||
for refi, (_ref_block_offset, ref_tile_name, ref_block,
|
||||
ref_block_name) in enumerate(values):
|
||||
seglets = segtiles.setdefault(ref_tile_name, [])
|
||||
ref_as = (
|
||||
ref_block["offset"],
|
||||
ref_block["offset"] + ref_block["height"] - 1)
|
||||
|
||||
for cmpi in range(refi + 1, len(values)):
|
||||
(_cmp_block_offset, cmp_tile_name, cmp_block,
|
||||
cmp_block_name) = values[cmpi]
|
||||
cmp_as = (
|
||||
cmp_block["offset"],
|
||||
cmp_block["offset"] + cmp_block["height"] - 1)
|
||||
|
||||
if overlap(ref_as, cmp_as):
|
||||
seglets.append((cmp_tile_name, cmp_block_name))
|
||||
# sorting => first non-intersection means no future will intersect
|
||||
else:
|
||||
break
|
||||
|
||||
return segtiles
|
||||
|
||||
|
||||
def mk_segments(tiles):
|
||||
segments = {}
|
||||
segtiles = mk_segtiles(tiles)
|
||||
|
||||
for tile_name, tile in tiles.items():
|
||||
bits = tile.get('bits', None)
|
||||
if not bits:
|
||||
continue
|
||||
for block_name, block in bits.items():
|
||||
for block_name, block in tile['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': tile,
|
||||
'tile_name': tile_name,
|
||||
'block': block,
|
||||
'block_name': block_name,
|
||||
'segtiles': segtiles[tile_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):
|
||||
'''Create a list of all (tile_name, block_name) from input tiles'''
|
||||
ret = []
|
||||
for tile_name, tile in tiles.items():
|
||||
if 'bits' not in tile:
|
||||
|
|
@ -222,6 +398,13 @@ def tile_segnames(tiles):
|
|||
return ret
|
||||
|
||||
|
||||
def load_tiles(db_root):
|
||||
# TODO: Migrate to new tilegrid format via library.
|
||||
with open("%s/tilegrid.json" % (db_root), "r") as f:
|
||||
tiles = json.load(f)
|
||||
return tiles
|
||||
|
||||
|
||||
def run(
|
||||
db_root,
|
||||
bits_file,
|
||||
|
|
@ -231,9 +414,9 @@ def run(
|
|||
flag_decode_emit=False,
|
||||
flag_decode_omit=False,
|
||||
verbose=False):
|
||||
tiles, segments = mk_grid(db_root)
|
||||
db = prjxraydb.Database(db_root)
|
||||
|
||||
tiles = load_tiles(db_root)
|
||||
segments = mk_segments(tiles)
|
||||
bitdata = bitstream.load_bitdata2(open(bits_file, "r"))
|
||||
|
||||
if flag_unknown_bits:
|
||||
|
|
@ -255,11 +438,11 @@ def run(
|
|||
handle_segment(
|
||||
db,
|
||||
segname,
|
||||
segments,
|
||||
bitdata,
|
||||
flag_decode_emit,
|
||||
flag_decode_omit,
|
||||
omit_empty_segs,
|
||||
segments,
|
||||
verbose=verbose)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue