mirror of https://github.com/openXC7/prjxray.git
240 lines
5.8 KiB
Python
Executable File
240 lines
5.8 KiB
Python
Executable File
#!/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()
|