blob: 8031fdc8d73d0028264fa9b6ab450c63e1329c3d [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017-2020 The Project X-Ray Authors.
#
# Use of this source code is governed by a ISC-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/ISC
#
# SPDX-License-Identifier: ISC
'''
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
from prjxray.util import OpenSafeFile
# =============================================================================
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 OpenSafeFile(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()