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