Utility scripts for viewing/comparing segbit files (.db and .rdb)

Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
This commit is contained in:
Maciej Kurc 2019-07-26 17:00:46 +02:00
parent 8ca5d2a358
commit 078dda081b
2 changed files with 367 additions and 0 deletions

141
utils/maskview.py Executable file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env python3
"""
This tool allows to view segbits using a 2D frame vs. bit plot. It can read
either mask.db files or segbits.db files. Multiple files can be read at once
allowing to identify where a bit comes from.
When a single file is read, a bit is marked as "O". When multiple files are
read, a bit is denoted with a letter "A", "B" and so on depending on index
of file that it belongs to.
Duplicate bits (present in more than one files) are marked with "#"
"""
import sys
import argparse
import re
# =============================================================================
def load_just_bits(file_name):
"""
Read bits from a .db or .rdb file. Ignores tags and bit values.
"""
with open(file_name, "r") as fp:
lines = fp.readlines()
bits = set()
for line in lines:
for word in line.split(" "):
match = re.match("^(!?)([0-9]+)_([0-9]+)$", word)
if match is not None:
frm = int(match.group(2))
bit = int(match.group(3))
bits.add((frm, bit,))
return bits
# =============================================================================
def main():
# Colors for TTY
if sys.stdout.isatty():
bit_colors = [
"\033[39m",
"\033[91m",
"\033[92m",
"\033[93m",
"\033[94m",
"\033[95m",
"\033[96m",
"\033[31m",
"\033[32m",
"\033[33m",
"\033[34m",
"\033[35m",
"\033[36m",
]
colors = {
"NONE": "\033[0m",
"DUPLICATE": "\033[101;97m",
}
# Colors for pipe
else:
bit_colors = [""]
colors = {
"NONE": "",
"DUPLICATE": "",
}
# .........................................................................
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("files", nargs="*", type=str, help="Input files")
args = parser.parse_args()
# Load bits
all_bits = []
for i, f in enumerate(args.files):
bits = load_just_bits(f)
all_bits.append(bits)
cstr = bit_colors[i % len(bit_colors)]
bstr = "O" if len(args.files) == 1 else chr(65+i)
print(cstr + bstr + colors["NONE"] + ": %s #%d" % (f, len(bits)))
print("")
max_frames = max([bit[0] for bits in all_bits for bit in bits]) + 1
max_bits = max([bit[1] for bits in all_bits for bit in bits]) + 1
# Header
for r in range(3):
line = " " * 3
for c in range(max_bits):
bstr = "%03d" % c
line += bstr[r]
print(line)
print("")
# Display bits
for r in range(max_frames):
line = "%2d " % r
for c in range(max_bits):
got_bit = False
bit_str = colors["NONE"] + "-"
for i, bits in enumerate(all_bits):
cstr = bit_colors[i % len(bit_colors)]
bstr = "O" if len(args.files) == 1 else chr(65+i)
if (r, c) in bits:
if not got_bit:
bit_str = cstr + bstr
else:
bit_str = colors["DUPLICATE"] + "#" + colors["NONE"]
got_bit = True
line += bit_str
line += colors["NONE"]
print(line)
# =============================================================================
if __name__ == "__main__":
main()

226
utils/segview.py Executable file
View File

@ -0,0 +1,226 @@
#!/usr/bin/env python3
'''
This script allows to load a number of .db or .rdb files and display bits in
a nice visualization.
When more than one files are loaded, a difference between them is shown.
Differring bits are highlighted.
'''
import argparse
import re
import sys
import itertools
# =============================================================================
def tagmap(tag):
"""
Maps a specific tag name to its generic name
"""
tag = tag.replace("CLBLL_L", "CLB")
tag = tag.replace("CLBLL_M", "CLB")
tag = tag.replace("CLBLM_L", "CLB")
tag = tag.replace("CLBLM_M", "CLB")
tag = tag.replace("SLICEL", "SLICE")
tag = tag.replace("SLICEM", "SLICE")
tag = tag.replace("LIOB33", "IOB33")
tag = tag.replace("RIOB33", "IOB33")
tag = tag.replace("LIOI3", "IOI3")
tag = tag.replace("RIOI3", "IOI3")
# TODO: Add other tag mappings
return tag
def parse_bit(bit):
"""
Decodes string describing a bit. Returns a tuple (frame, bit, value)
"""
match = re.match("^(!?)([0-9]+)_([0-9]+)$", bit)
assert match != None, bit
val = int(match.group(1) != "!")
frm = int(match.group(2))
bit = int(match.group(3))
return frm, bit, val
def load_and_sort_segbits(file_name, tagmap = lambda tag: tag):
"""
Loads a segbit file (.db or .rdb). Skips bits containing '<' or '>'
"""
# Load segbits
segbits = {}
with open(file_name, "r") as fp:
lines = fp.readlines()
# Parse lines
for line in lines:
line = line.strip()
fields = line.split()
if len(fields) < 2:
print("Malformed line: '%s'" % line)
continue
# Map name
feature = tagmap(fields[0])
# Decode bits
bits = []
for bit in fields[1:]:
if "<" in bit or ">" in bit:
continue
bits.append(parse_bit(bit))
# Sort bits
bits.sort(key=lambda bit: (bit[0], bit[1],))
segbits[feature] = bits
return segbits
# =============================================================================
def make_header_lines(all_bits):
"""
Formats header lines
"""
lines = []
# Bit names
bit_names = ["%d_%d" % (b[0], b[1]) for b in all_bits]
bit_len = 6
for i in range(bit_len):
line = ""
for j in range(len(all_bits)):
bstr = bit_names[j].ljust(bit_len).replace("_", "|")
line += bstr[i]
lines.append(line)
return lines
def make_data_lines(all_tags, all_bits, segbits):
"""
Formats data lines
"""
lines = []
def map_f(b):
if b < 0: return "0"
if b > 0: return "1"
return "-"
# Bit data
for tag in all_tags:
if tag in segbits.keys():
lines.append("".join(map(map_f, segbits[tag])))
else:
lines.append(" " * len(all_bits))
return lines
def main():
# Colors for TTY
if sys.stdout.isatty():
colors = {
"NONE": "\033[0m",
"DIFF": "\033[1m",
}
# Colors for pipe
else:
colors = {
"NONE": "",
"DIFF": "",
}
# ........................................................................
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("files", nargs="*", type=str, help="Input files")
args = parser.parse_args()
# Load segbits
all_segbits = []
for f in args.files:
all_segbits.append(load_and_sort_segbits(f, tagmap))
# List of all features and all bits
all_tags = set()
all_bits = set()
for segbits in all_segbits:
all_tags |= set(segbits.keys())
for bits in segbits.values():
all_bits |= set([(b[0], b[1]) for b in bits])
all_tags = sorted(list(all_tags))
all_bits = sorted(list(all_bits))
# Convert bit lists to bit vectors
for segbits in all_segbits:
for tag in segbits.keys():
vec = list([0]*len(all_bits))
for i, bit in enumerate(all_bits):
if (bit[0], bit[1], 0) in segbits[tag]:
vec[i] = -1
if (bit[0], bit[1], 1) in segbits[tag]:
vec[i] = +1
segbits[tag] = vec
# Make header and data lines
header_lines = make_header_lines(all_bits)
data_lines = [make_data_lines(all_tags, all_bits, segbits) for segbits in all_segbits]
# Display
max_tag_len = max([len(tag) for tag in all_tags])
for l in header_lines:
line = " " * max_tag_len + " "
for i in range(len(all_segbits)):
line += l + " "
print(line)
data_len = len(all_bits)
for i, tag in enumerate(all_tags):
line = tag.ljust(max_tag_len) + " "
diff = bytearray(data_len)
for l1, l2 in itertools.combinations(data_lines, 2):
for j in range(data_len):
if l1[i][j] != l2[i][j]:
diff[j] = 1
for l in data_lines:
for j in range(data_len):
if diff[j]:
line += colors["DIFF"] + l[i][j] + colors["NONE"]
else:
line += l[i][j]
line += " "
print(line)
# =============================================================================
if __name__ == "__main__":
main()