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