#!/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 ''' Prints minor address group sizes If any part of an IP block is written, Vivado likes to write the entire block This can be useful to guess the number of frames an IP block uses This was developed with PERFRAMECRC, but could probably be rewritten to work without it ''' import sys import os import time import json import subprocess from io import StringIO BITTOOL = os.getenv('XRAY_BITTOOL') def bit2packets(fn): # capture_output wasn't added until 3.7, I have 3.5 #return subprocess.run([BITTOOL, "list_config_packets", fn], capture_output=True, check=True).stdout p = subprocess.run( [BITTOOL, "list_config_packets", fn], check=True, stdout=subprocess.PIPE) return p.stdout.decode("ascii") def nominor(addr): return addr & ~0x7f def gen_frame_writes(f): ''' look for line triples like this [Write Type=1 Address= 1 Length= 1 Reg="Frame Address"] Data in hex: 1d ''' while True: l = f.readline() if not l: break l = l.strip() # TODO: assert that writes are always per frame CRC if l != '[Write Type=1 Address= 1 Length= 1 Reg="Frame Address"]': continue assert f.readline().strip() == 'Data in hex:' lhex = f.readline().strip() yield int(lhex, 16) def gen_major_writes(fnin): ''' The same address can appear twice Ex: 0x00800000 Moved from counter to set ''' last_addrs = None last_major = None for this_minor in gen_frame_writes(StringIO(bit2packets(fnin))): this_major = nominor(this_minor) if this_major == last_major: # should be no gaps last_addrs.add(this_minor) # addresses may skip back, but they don't seem to skip forward # AssertionError: (0, 2, {0, 1}) # assert (this_major - this_minor) + 1 == len(last_addrs), (this_major, len(last_addrs), last_addrs) assert (this_major - this_minor) + 1 <= len(last_addrs), ( this_major, len(last_addrs), last_addrs) else: if last_major is not None: assert len(last_addrs) <= 0x80 yield last_major, len(last_addrs) # all writes should start at base? assert this_major == this_minor last_major = this_major last_addrs = set([this_minor]) yield last_major, len(last_addrs) def run(fnin, verbose=False): for addr, nframes in gen_major_writes(fnin): print('0x%08X: 0x%02X' % (addr, nframes)) def main(): import argparse parser = argparse.ArgumentParser( description='Print PERFRAMECRC bitstream minor address gropuing') parser.add_argument('fnin', default=None, help='input bitstream') args = parser.parse_args() run(args.fnin, verbose=False) if __name__ == '__main__': main()