mirror of https://github.com/openXC7/prjxray.git
Utility scripts for viewing/comparing segbit files (.db and .rdb)
Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
This commit is contained in:
parent
8ca5d2a358
commit
078dda081b
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
||||
Loading…
Reference in New Issue