#!/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 performs the analysis of disassembled bitstream and design information. It correlates presence/absence of particular fasm features in the bitstream with presence/absence of a particular IOB type. It also checks for features that are set for unused IOBs that are in the same bank as the used ones. The list of available fasm features is read from the prjxray database. The analysis result is written to a CSV file. """ import os import argparse import json import random from collections import defaultdict from prjxray import util import fasm # ============================================================================= def load_iob_segbits(): """ Loads IOB segbits from the database to get fasm feature list. This function assumes: - LIOB33/RIOB33 have the same features. - IOB_Y0 and IOB_Y1 have the same features (at least those of interest). """ features = [] fname = os.path.join(util.get_db_root(), "segbits_liob33.db") with open(fname, "r") as fp: for line in fp: line = line.strip() if len(line) == 0: continue # Parse the line fields = line.split(maxsplit=1) feature = fields[0] bits = fields[1] # Parse the feature parts = feature.split(".", maxsplit=3) if len(parts) >= 3 and parts[1] == "IOB_Y0": features.append(".".join(parts[2:])) return features def correlate_features(features, tile, site, set_features): """ Correlate each feature with the fasm disassembly for given tile/site. Given a set of all possible fasm features (for an IOB) in the first argument, checks whether they are set or cleared for the given tile+site in a design. The parameter set_features contains fasm dissassembly of the design. Returns a list of tuples with feature names and whether they are set or not. """ result = [] for feature in features: full_feature = "{}.{}.{}".format(tile, site, feature) if full_feature in set_features: result.append(( feature, True, )) else: result.append(( feature, False, )) return result def run(): """ Main. """ # Parse arguments parser = argparse.ArgumentParser() parser.add_argument( "--design", type=str, required=True, help="Design JSON file") parser.add_argument( "--fasm", type=str, required=True, help="Decoded fasm file") parser.add_argument( "-o", type=str, default="results.csv", help="Output CSV file") parser.add_argument("-j", type=str, default=None, help="Output JSON file") args = parser.parse_args() # Load IOB features features = load_iob_segbits() # Load the design data with open(args.design, "r") as fp: design = json.load(fp) # Load disassembled fasm fasm_tuples = fasm.parse_fasm_filename(args.fasm) set_features = fasm.fasm_tuple_to_string(fasm_tuples).split("\n") # Correlate features for given IOB types results = [] for region in design: result = dict(region["iosettings"]) for l in ["input", "output", "inout", "unused_sites"]: # TODO: Check if this is true eg. for all unused sites, not just # one random site. tile, site = random.choice(region[l]).split(".") matches = correlate_features(features, tile, site, set_features) result[l] = matches results.append(result) # Save results if args.j: with open(args.j, "w") as fp: json.dump(results, fp, indent=2, sort_keys=True) # Save results to CSV with open(args.o, "w") as fp: csv_data = defaultdict(lambda: {}) # Collect data for result in results: iostandard = result["iostandard"] drive = result["drive"] slew = result["slew"] if drive is None: drive = "_FIXED" iosettings = "{}.I{}.{}".format(iostandard, drive, slew) is_diff = "DIFF" in iostandard for feature in sorted(features): I = [f[1] for f in result["input"] if f[0] == feature and f[1]] O = [ f[1] for f in result["output"] if f[0] == feature and f[1] ] T = [f[1] for f in result["inout"] if f[0] == feature and f[1]] U = [ f[1] for f in result["unused_sites"] if f[0] == feature and f[1] ] s = "".join( [ "I" if len(I) > 0 else "", "O" if len(O) > 0 else "", "T" if len(T) > 0 else "", "U" if len(U) > 0 else "", ]) csv_data[iosettings][feature] = s # Write header line = ["iosettings"] + sorted(features) fp.write(",".join(line) + "\n") # Write data for iosettings in sorted(csv_data.keys()): data = csv_data[iosettings] line = [iosettings ] + [data[feature] for feature in sorted(features)] fp.write(",".join(line) + "\n") if __name__ == "__main__": run()