| #!/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 |
| """ Sanity checks FASM output from IOB fuzzer. |
| The IOB fuzzer is fairly complicated, and it's output is hard to verify by |
| inspected. For this reason, check_results.py was written to compare the |
| specimen's generated and their FASM output. The FASM output does pose a |
| chicken and egg issue. The test procedure is a follows: |
| |
| 1. Build the database (e.g. make -j<N> run) |
| 2. Build the database again (e.g. make -j<N> run) |
| 3. Run check_results.py |
| |
| The second time that the database is run, the FASM files in the specimen's |
| will have the bits documented by fuzzer. |
| |
| """ |
| import argparse |
| import os |
| import os.path |
| from prjxray import verilog |
| import json |
| import generate |
| |
| |
| def process_parts(parts): |
| if len(parts) == 0: |
| return |
| |
| if parts[-1] == 'IN_ONLY': |
| yield 'type', ['IBUF', 'IBUFDS'] |
| |
| if len(parts) > 2 and parts[-2] == 'SLEW': |
| yield 'SLEW', verilog.quote(parts[-1]) |
| |
| if parts[0] == 'PULLTYPE': |
| yield 'PULLTYPE', verilog.quote(parts[1]) |
| |
| if len(parts) > 1 and parts[1] == 'IN': |
| yield 'IOSTANDARDS', parts[0].split('_') |
| yield 'IN', True |
| |
| if len(parts) > 1 and parts[1] == 'IN_DIFF': |
| yield 'IOSTANDARDS', parts[0].split('_') |
| yield 'IN_DIFF', True |
| |
| if len(parts) > 1 and parts[1] == 'DRIVE': |
| yield 'IOSTANDARDS', parts[0].split('_') |
| |
| if parts[2] == 'I_FIXED': |
| yield 'DRIVES', [None] |
| else: |
| yield 'DRIVES', parts[2].split('_') |
| |
| |
| def create_sites_from_fasm(fasm_file): |
| sites = {} |
| |
| diff_tiles = set() |
| |
| with open(fasm_file) as f: |
| for l in f: |
| if 'IOB33' not in l: |
| continue |
| |
| parts = l.strip().split('.') |
| tile = parts[0] |
| site = parts[1] |
| |
| if 'OUT_DIFF' == site: |
| diff_tiles.add(tile) |
| continue |
| |
| if (tile, site) not in sites: |
| sites[(tile, site)] = { |
| 'tile': tile, |
| 'site_key': site, |
| } |
| |
| if len(parts) > 3 and 'IN_DIFF' == parts[3]: |
| diff_tiles.add(tile) |
| |
| for key, value in process_parts(parts[2:]): |
| sites[(tile, site)][key] = value |
| |
| for key in sites: |
| if 'type' not in sites[key]: |
| if 'IOSTANDARDS' not in sites[key]: |
| sites[key]['type'] = [None] |
| else: |
| assert 'IOSTANDARDS' in sites[key], sites[key] |
| assert 'DRIVES' in sites[key], sites[key] |
| |
| if 'IN' in sites[key]: |
| sites[key]['type'] = ['IOBUF', 'IOBUF_INTERMDISABLE'] |
| else: |
| sites[key]['type'] = [ |
| "OBUF", |
| "OBUFDS", |
| "OBUFTDS", |
| "OBUFDS_DUAL_BUF", |
| "OBUFTDS_DUAL_BUF", |
| ] |
| |
| return sites, diff_tiles |
| |
| |
| def process_specimen(fasm_file, params_json): |
| sites, diff_tiles = create_sites_from_fasm(fasm_file) |
| |
| with open(params_json) as f: |
| params = json.load(f) |
| |
| count = 0 |
| for p in params['tiles']: |
| tile = p['tile'] |
| for site in p['site'].split(' '): |
| site_y = int(site[site.find('Y') + 1:]) % 2 |
| |
| if generate.skip_broken_tiles(p): |
| continue |
| |
| site_key = 'IOB_Y{}'.format(site_y) |
| |
| if (tile, site_key) not in sites: |
| assert p['type'] is None, p |
| continue |
| |
| site_from_fasm = sites[(tile, site_key)] |
| |
| if site_y == 0 or tile not in diff_tiles: |
| assert p['type'] in site_from_fasm['type'], ( |
| tile, site_key, p['type'], site_from_fasm['type']) |
| else: |
| # Y1 on DIFF tiles is always none. |
| assert p['type'] is None, p |
| |
| if p['type'] is None: |
| continue |
| |
| assert 'PULLTYPE' in p, p |
| assert 'PULLTYPE' in site_from_fasm, site_from_fasm |
| |
| if verilog.unquote(p['PULLTYPE']) == '': |
| # Default is None. |
| pulltype = verilog.quote('NONE') |
| else: |
| pulltype = p['PULLTYPE'] |
| |
| assert pulltype == site_from_fasm['PULLTYPE'], ( |
| tile, site_key, p, site_from_fasm) |
| |
| assert 'IOSTANDARDS' in site_from_fasm, (tile, site) |
| |
| iostandard = verilog.unquote(p['IOSTANDARD']) |
| if iostandard.startswith('DIFF_'): |
| iostandard = iostandard[5:] |
| |
| assert iostandard in site_from_fasm['IOSTANDARDS'], ( |
| p['IOSTANDARD'], |
| site_from_fasm['IOSTANDARDS'], |
| ) |
| |
| if p['type'] not in ['IBUF', 'IBUFDS']: |
| if verilog.unquote(p['SLEW']) == '': |
| # Default is None. |
| slew = verilog.quote('SLOW') |
| else: |
| slew = p['SLEW'] |
| |
| assert slew == site_from_fasm['SLEW'], ( |
| tile, site_key, p, site_from_fasm) |
| |
| assert 'DRIVES' not in p, p |
| assert 'DRIVES' in site_from_fasm, ( |
| tile, site, p['type'], site_from_fasm) |
| |
| if p['DRIVE'] is None: |
| assert None in site_from_fasm['DRIVES'], ( |
| tile, site_key, p['DRIVE'], site_from_fasm['DRIVES']) |
| elif p['DRIVE'] is '': |
| if None in site_from_fasm['DRIVES']: |
| # IOSTANDARD has not DRIVE setting, ignore |
| pass |
| else: |
| # Check that drive is at default |
| assert 'I12' in site_from_fasm['DRIVES'], ( |
| tile, site_key, p['DRIVE'], |
| site_from_fasm['DRIVES']) |
| else: |
| assert 'I{}'.format( |
| p['DRIVE']) in site_from_fasm['DRIVES'], ( |
| tile, site_key, p['DRIVE'], |
| site_from_fasm['DRIVES']) |
| |
| count += 1 |
| |
| return count |
| |
| |
| def scan_specimens(): |
| for root, dirs, files in os.walk('build'): |
| if os.path.basename(root).startswith('specimen_'): |
| print('Processing', os.path.basename(root)) |
| process_specimen( |
| fasm_file=os.path.join(root, 'design.fasm'), |
| params_json=os.path.join(root, 'params.json')) |
| |
| print('No errors found!') |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description="Verify IOB FASM vs BELs.") |
| |
| parser.add_argument('--fasm') |
| parser.add_argument('--params') |
| |
| args = parser.parse_args() |
| |
| if not args.fasm and not args.params: |
| scan_specimens() |
| else: |
| count = process_specimen(fasm_file=args.fasm, params_json=args.params) |
| print('No errors found in {} IO sites'.format(count)) |
| |
| |
| if __name__ == "__main__": |
| main() |