roi_harness: Fix variable names Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/minitests/roi_harness/fasm2bit.sh b/minitests/roi_harness/fasm2bit.sh index ee1d389..3b5e5ff 100755 --- a/minitests/roi_harness/fasm2bit.sh +++ b/minitests/roi_harness/fasm2bit.sh
@@ -1,6 +1,6 @@ # Example pre-req # ./runme.sh -# XRAY_ROIV=roi_inv.v XRAY_FIXED_XDC=out_xc7a35tcpg236-1_BASYS3-SWBUT_roi_basev/fixed_noclk.xdc ./runme.sh +# URAY_ROIV=roi_inv.v URAY_FIXED_XDC=out_xc7a35tcpg236-1_BASYS3-SWBUT_roi_basev/fixed_noclk.xdc ./runme.sh set -ex @@ -27,14 +27,14 @@ echo "Harness .bit: $bit_in" echo "Out .bit: $bit_out" -${XRAY_FASM2FRAMES} --sparse $fasm_in roi_partial.frm +${URAY_FASM2FRAMES} --sparse $fasm_in roi_partial.frm -${XRAY_TOOLS_DIR}/xc7patch \ - --part_name ${XRAY_PART} \ - --part_file ${XRAY_PART_YAML} \ +${URAY_TOOLS_DIR}/xc7patch \ + --part_name ${URAY_PART} \ + --part_file ${URAY_PART_YAML} \ --bitstream_file $bit_in \ --frm_file roi_partial.frm \ --output_file $bit_out -#openocd -f $XRAY_DIR/utils/openocd/board-digilent-basys3.cfg -c "init; pld load 0 $bit_out; exit" +#openocd -f $URAY_DIR/utils/openocd/board-digilent-basys3.cfg -c "init; pld load 0 $bit_out; exit"
diff --git a/utils/fasm2frames.py b/utils/fasm2frames.py new file mode 100755 index 0000000..25cd6bc --- /dev/null +++ b/utils/fasm2frames.py
@@ -0,0 +1,306 @@ +#!/usr/bin/env python3 + +from __future__ import print_function + +import fasm +import argparse +import json +import os +import os.path +import csv + +from collections import defaultdict + +from utils import fasm_assembler, util +from utils.db import Database +from utils.roi import Roi + +import sys + + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + +class FASMSyntaxError(SyntaxError): + pass + + +def dump_frames_verbose(frames): + print() + print("Frames: %d" % len(frames)) + for addr in sorted(frames.keys()): + words = frames[addr] + print( + '0x%08X ' % addr + ', '.join(['0x%08X' % w for w in words]) + + '...') + + +def dump_frames_sparse(frames): + print() + print("Frames: %d" % len(frames)) + for addr in sorted(frames.keys()): + words = frames[addr] + + # Skip frames without filled words + for w in words: + if w: + break + else: + continue + + print('Frame @ 0x%08X' % addr) + for i, w in enumerate(words): + if w: + print(' % 3d: 0x%08X' % (i, w)) + + +def dump_frm(f, frames): + '''Write a .frm file given a list of frames, each containing a list of 101 32 bit words''' + for addr in sorted(frames.keys()): + words = frames[addr] + f.write( + '0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n') + + +def find_pudc_b(db): + """ Find PUDC_B pin func in grid, and return the tile and site prefix. + + The PUDC_B pin is a special 7-series pin that controls unused pin pullup. + + If the PUDC_B is unused, it is configured as an input with a PULLUP. + + """ + grid = db.grid() + + pudc_b_tile_site = None + for tile in grid.tiles(): + gridinfo = grid.gridinfo_at_tilename(tile) + + for site, pin_function in gridinfo.pin_functions.items(): + if 'PUDC_B' in pin_function: + assert pudc_b_tile_site == None, ( + pudc_b_tile_site, (tile, site)) + iob_y = int(site[-1]) % 2 + + pudc_b_tile_site = (tile, 'IOB_Y{}'.format(iob_y)) + + return pudc_b_tile_site + + +def get_iob_sites(db, tile_name): + """ + Yields prjxray site names for given IOB tile name + """ + grid = db.grid() + gridinfo = grid.gridinfo_at_tilename(tile_name) + + for site in gridinfo.sites: + site_y = int(site[-1]) % 2 + yield "IOB_Y{}".format(site_y) + + +def run( + db_root, + part, + filename_in, + f_out, + sparse=False, + roi=None, + debug=False, + emit_pudc_b_pullup=False): + db = Database(db_root, part) + assembler = fasm_assembler.FasmAssembler(db) + + set_features = set() + + def feature_callback(feature): + set_features.add(feature) + + assembler.set_feature_callback(feature_callback) + + # Build mapping of tile to IO bank + tile_to_bank = {} + bank_to_tile = defaultdict(lambda: set()) + + if part is not None: + with open(os.path.join(db_root, part, "package_pins.csv"), "r") as fp: + reader = csv.DictReader(fp) + package_pins = [l for l in reader] + + with open(os.path.join(db_root, part, "part.json"), "r") as fp: + part_data = json.load(fp) + + for bank, loc in part_data["iobanks"].items(): + tile = "HCLK_IOI3_" + loc + bank_to_tile[bank].add(tile) + tile_to_bank[tile] = bank + + for pin in package_pins: + bank_to_tile[pin["bank"]].add(pin["tile"]) + tile_to_bank[pin["tile"]] = pin["bank"] + + if emit_pudc_b_pullup: + pudc_b_in_use = False + pudc_b_tile_site = find_pudc_b(db) + + def check_for_pudc_b(set_feature): + feature_callback(set_feature) + parts = set_feature.feature.split('.') + + if parts[0] == pudc_b_tile_site[0] and parts[ + 1] == pudc_b_tile_site[1]: + nonlocal pudc_b_in_use + pudc_b_in_use = True + + if pudc_b_tile_site is not None: + assembler.set_feature_callback(check_for_pudc_b) + + extra_features = [] + if roi: + with open(roi) as f: + roi_j = json.load(f) + x1 = roi_j['info']['GRID_X_MIN'] + x2 = roi_j['info']['GRID_X_MAX'] + y1 = roi_j['info']['GRID_Y_MIN'] + y2 = roi_j['info']['GRID_Y_MAX'] + + assembler.mark_roi_frames(Roi(db=db, x1=x1, x2=x2, y1=y1, y2=y2)) + + if 'required_features' in roi_j: + extra_features = list( + fasm.parse_fasm_string('\n'.join(roi_j['required_features']))) + + # Get required extra features for the part + required_features = db.get_required_fasm_features(part) + extra_features += list( + fasm.parse_fasm_string('\n'.join(required_features))) + + assembler.parse_fasm_filename(filename_in, extra_features=extra_features) + + if emit_pudc_b_pullup and not pudc_b_in_use and pudc_b_tile_site is not None: + # Enable IN-only and PULLUP on PUDC_B IOB. + # + # TODO: The following FASM string only works on Artix 50T and Zynq 10 + # fabrics. It is known to be wrong for the K70T fabric, but it is + # unclear how to know which IOSTANDARD to use. + missing_features = [] + for line in fasm.parse_fasm_string(""" +{tile}.{site}.LVCMOS12_LVCMOS15_LVCMOS18_LVCMOS25_LVCMOS33_LVTTL_SSTL135.IN_ONLY +{tile}.{site}.LVCMOS25_LVCMOS33_LVTTL.IN +{tile}.{site}.PULLTYPE.PULLUP +""".format( + tile=pudc_b_tile_site[0], + site=pudc_b_tile_site[1], + )): + assembler.add_fasm_line(line, missing_features) + + if missing_features: + raise fasm_assembler.FasmLookupError('\n'.join(missing_features)) + + if part is not None: + # Make a set of all used IOB tiles and sites. Look for the "STEPDOWN" + # feature. If one is set for an IOB then set it for all other IOBs of + # the same bank. + stepdown_tags = defaultdict(lambda: set()) + stepdown_banks = set() + used_iob_sites = set() + + for set_feature in set_features: + if set_feature.value == 0: + continue + + feature = set_feature.feature + parts = feature.split(".") + if len(parts) >= 3: + tile, site, tag = feature.split(".", maxsplit=2) + if "IOB33" in tile: + used_iob_sites.add(( + tile, + site, + )) + + # Store STEPDOWN related tags. + if "STEPDOWN" in tag: + bank = tile_to_bank[tile] + stepdown_banks.add(bank) + stepdown_tags[bank].add(tag) + + # Set the feature for unused IOBs, loop over all banks which were + # observed to have the STEPDOWN feature set. + missing_features = [] + + for bank in stepdown_banks: + for tile in bank_to_tile[bank]: + + # This is an IOB33 tile. Set the STEPDOWN feature in it but + # only if it is unused. + if "IOB33" in tile: + for site in get_iob_sites(db, tile): + + if (tile, site) in used_iob_sites: + continue + + for tag in stepdown_tags[bank]: + feature = "{}.{}.{}".format(tile, site, tag) + for line in fasm.parse_fasm_string(feature): + assembler.add_fasm_line(line, missing_features) + + # This is a HCLK_IOI3 tile, set the stepdown feature for it + # too. + if "HCLK_IOI3" in tile: + feature = "{}.STEPDOWN".format(tile) + for line in fasm.parse_fasm_string(feature): + assembler.add_fasm_line(line, missing_features) + + if missing_features: + raise fasm_assembler.FasmLookupError('\n'.join(missing_features)) + + frames = assembler.get_frames(sparse=sparse) + + if debug: + dump_frames_sparse(frames) + + dump_frm(f_out, frames) + + +def main(): + parser = argparse.ArgumentParser( + description= + 'Convert FPGA configuration description ("FPGA assembly") into binary frame equivalent' + ) + + util.db_root_arg(parser) + util.part_arg(parser) + parser.add_argument( + '--sparse', action='store_true', help="Don't zero fill all frames") + parser.add_argument( + '--roi', + help="ROI design.json file defining which tiles are within the ROI.") + parser.add_argument( + '--emit_pudc_b_pullup', + help="Emit an IBUF and PULLUP on the PUDC_B pin if unused", + action='store_true') + parser.add_argument( + '--debug', action='store_true', help="Print debug dump") + parser.add_argument('fn_in', help='Input FPGA assembly (.fasm) file') + parser.add_argument( + 'fn_out', + default='/dev/stdout', + nargs='?', + help='Output FPGA frame (.frm) file') + + args = parser.parse_args() + run( + db_root=args.db_root, + part=args.part, + filename_in=args.fn_in, + f_out=open(args.fn_out, 'w'), + sparse=args.sparse, + roi=args.roi, + debug=args.debug, + emit_pudc_b_pullup=args.emit_pudc_b_pullup) + + +if __name__ == '__main__': + main()