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