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