#!/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()