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