| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| # | |
| # Copyright (C) 2020-2022 F4PGA Authors. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # https://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # Python entrypoints to the shell wrappers moved from arch-defs | |
| from sys import argv as sys_argv, stdout, stderr | |
| from os import environ | |
| from pathlib import Path | |
| from shutil import which | |
| from subprocess import check_call | |
| from argparse import ArgumentParser, RawDescriptionHelpFormatter | |
| from f4pga.context import FPGA_FAM, F4PGA_SHARE_DIR | |
| python3 = which("python3") | |
| ROOT = Path(__file__).resolve().parent | |
| isQuickLogic = FPGA_FAM != "xc7" | |
| SH_SUBDIR = "quicklogic" if isQuickLogic else FPGA_FAM | |
| if not isQuickLogic: | |
| from f4pga.utils.xc7.create_ioplace import main as xc7_create_ioplace | |
| from f4pga.utils.xc7.create_place_constraints import main as xc7_create_place_constraints | |
| # Helper functions | |
| def p_run_sh_script(script, env=environ): | |
| stdout.flush() | |
| stderr.flush() | |
| check_call([str(script)] + sys_argv[1:], env=env) | |
| def p_run_bash_cmds(cmds, env=environ): | |
| stdout.flush() | |
| stderr.flush() | |
| check_call(cmds, env=env, shell=True, executable="/bin/bash") | |
| def p_run_pym(module, env=environ): | |
| stdout.flush() | |
| stderr.flush() | |
| check_call([python3, "-m", module] + sys_argv[1:], env=env) | |
| def p_vpr_env_from_args(log_suffix=None): | |
| vpr_options = environ.get("VPR_OPTIONS") | |
| if vpr_options is not None: | |
| vpr_options = p_args_str2list(vpr_options) | |
| env = environ.copy() | |
| env.update( | |
| p_parse_vpr_args( | |
| vpr_options=vpr_options, | |
| log_suffix=log_suffix, | |
| isQuickLogic=isQuickLogic, | |
| ) | |
| ) | |
| return env | |
| def p_args_str2list(args): | |
| return [arg for arg in args.strip().split() if arg != ""] | |
| def p_vpr_run(args, env=environ): | |
| print("[F4PGA] Running (deprecated) vpr run") | |
| arg_arch_def = env.get("ARCH_DEF") | |
| if arg_arch_def is None: | |
| raise (Exception("[F4PGA] vpr run: envvar ARCH_DEF cannot be unset/empty!")) | |
| arg_eblif = env.get("EBLIF") | |
| if arg_eblif is None: | |
| raise (Exception("[F4PGA] vpr run: envvar EBLIF cannot be unset/empty!")) | |
| arg_vpr_options = env.get("VPR_OPTIONS") | |
| if arg_vpr_options is None: | |
| raise (Exception("[F4PGA] vpr run: envvar VPR_OPTIONS cannot be unset/empty!")) | |
| arg_device_name = env.get("DEVICE_NAME") | |
| if arg_device_name is None: | |
| raise (Exception("[F4PGA] vpr run: envvar DEVICE_NAME cannot be unset/empty!")) | |
| arg_rr_graph = env.get("RR_GRAPH") | |
| if arg_rr_graph is None: | |
| raise (Exception("[F4PGA] vpr run: envvar RR_GRAPH cannot be unset/empty!")) | |
| arg_lookahead = env.get("LOOKAHEAD") | |
| if arg_lookahead is None: | |
| raise (Exception("[F4PGA] vpr run: envvar LOOKAHEAD cannot be unset/empty!")) | |
| arg_place_delay = env.get("PLACE_DELAY") | |
| if arg_place_delay is None: | |
| raise (Exception("[F4PGA] vpr run: envvar PLACE_DELAY cannot be unset/empty!")) | |
| sdc = env.get("SDC") | |
| if sdc == "": | |
| sdc = None | |
| check_call( | |
| [which("vpr"), arg_arch_def, arg_eblif] | |
| + p_args_str2list(arg_vpr_options) | |
| + [ | |
| "--device", | |
| arg_device_name, | |
| "--read_rr_graph", | |
| arg_rr_graph, | |
| "--read_router_lookahead", | |
| arg_lookahead, | |
| "--read_placement_delay_lookup", | |
| arg_place_delay, | |
| ] | |
| + (["--sdc_file", sdc] if sdc is not None else []) | |
| + args, | |
| ) | |
| def p_parse_vpr_args(vpr_options=None, log_suffix=None, isQuickLogic=False): | |
| if isQuickLogic: | |
| return p_parse_vpr_args_quicklogic(vpr_options, log_suffix) | |
| else: | |
| return p_parse_vpr_args_xc7(vpr_options, log_suffix) | |
| def p_parse_vpr_args_xc7(vpr_options=None, log_suffix=None): | |
| parser = ArgumentParser(description=__doc__, formatter_class=RawDescriptionHelpFormatter) | |
| parser.add_argument("--device", "-d", required=False, type=str, help="") | |
| parser.add_argument("--eblif", "-e", required=True, type=str, help="") | |
| parser.add_argument("--pcf", "-p", required=False, type=str, help="") | |
| parser.add_argument("--net", "-n", required=False, type=str, help="") | |
| parser.add_argument("--part", "-P", required=False, type=str, help="") | |
| parser.add_argument("--sdc", "-s", required=False, type=str, help="") | |
| parser.add_argument("additional_vpr_options", nargs="*", type=str, help="") | |
| args = parser.parse_args() | |
| device = args.device | |
| if device is None and args.part is not None: | |
| parts_dir = F4PGA_SHARE_DIR | |
| # Try to find device name. | |
| # Accept only when exactly one is found. | |
| # PART_DIRS=(${F4PGA_SHARE_DIR}/arch/*/${PART}) | |
| # if [ ${#PART_DIRS[@]} -eq 1 ]; then DEVICE=$(basename $(dirname "${PART_DIRS[0]}")); fi | |
| if device is None: | |
| raise Exception("Please provide device name") | |
| noisy_warnings = "" if log_suffix is None else f"noisy_warnings-{device}_{log_suffix}.log" | |
| if vpr_options is None: | |
| print("Using default VPR options") | |
| vpr_options = [ | |
| "--max_router_iterations", | |
| "500", | |
| "--routing_failure_predictor", | |
| "off", | |
| "--router_high_fanout_threshold", | |
| "-1", | |
| "--constant_net_method", | |
| "route", | |
| "--route_chan_width", | |
| "500", | |
| "--router_heap", | |
| "bucket", | |
| "--clock_modeling", | |
| "route", | |
| "--place_delta_delay_matrix_calculation_method", | |
| "dijkstra", | |
| "--place_delay_model", | |
| "delta", | |
| "--router_lookahead", | |
| "extended_map", | |
| "--check_route", | |
| "quick", | |
| "--strict_checks", | |
| "off", | |
| "--allow_dangling_combinational_nodes", | |
| "on", | |
| "--disable_errors", | |
| "check_unbuffered_edges:check_route", | |
| "--congested_routing_iteration_threshold", | |
| "0.8", | |
| "--incremental_reroute_delay_ripup", | |
| "off", | |
| "--base_cost_type", | |
| "delay_normalized_length_bounded", | |
| "--bb_factor", | |
| "10", | |
| "--acc_fac", | |
| "0.7", | |
| "--astar_fac", | |
| "1.8", | |
| "--initial_pres_fac", | |
| "2.828", | |
| "--pres_fac_mult", | |
| "1.2", | |
| "--check_rr_graph", | |
| "off", | |
| "--suppress_warnings", | |
| noisy_warnings | |
| + ",sum_pin_class:check_unbuffered_edges:load_rr_indexed_data_T_values:check_rr_node:trans_per_R:check_route:set_rr_graph_tool_comment:calculate_average_switch", | |
| ] | |
| arch_dir = F4PGA_SHARE_DIR / "arch" / device | |
| envvars = { | |
| "DEVICE": device, | |
| "DEVICE_NAME": device.replace("_", "-"), | |
| "EBLIF": args.eblif, | |
| "VPR_OPTIONS": " ".join(vpr_options + args.additional_vpr_options), | |
| "ARCH_DIR": str(arch_dir), | |
| "ARCH_DEF": str(arch_dir / "arch.timing.xml"), | |
| "RR_GRAPH": str(arch_dir / f"rr_graph_{device}.rr_graph.real.bin"), | |
| "RR_GRAPH_XML": str(arch_dir / f"rr_graph_{device}.rr_graph.real.xml"), | |
| "PLACE_DELAY": str(arch_dir / f"rr_graph_{device}.place_delay.bin"), | |
| "LOOKAHEAD": str(arch_dir / f"rr_graph_{device}.lookahead.bin"), | |
| } | |
| if args.pcf is not None: | |
| envvars["PCF"] = args.pcf | |
| if args.net is not None: | |
| envvars["NET"] = args.net | |
| if args.part is not None: | |
| envvars["PART"] = args.part | |
| if args.sdc is not None: | |
| envvars["SDC"] = args.sdc | |
| if log_suffix is not None: | |
| envvars["OUT_NOISY_WARNINGS"] = noisy_warnings | |
| return envvars | |
| def p_parse_vpr_args_quicklogic(vpr_options=None, log_suffix=None): | |
| parser = ArgumentParser(description=__doc__, formatter_class=RawDescriptionHelpFormatter) | |
| parser.add_argument("--device", "-d", required=True, type=str, help="") | |
| parser.add_argument("--family", "-f", required=True, type=str, help="") | |
| parser.add_argument("--eblif", "-e", required=True, type=str, help="") | |
| parser.add_argument("--pcf", "-p", required=False, type=str, help="") | |
| parser.add_argument("--net", "-n", required=False, type=str, help="") | |
| parser.add_argument("--part", "-P", required=False, type=str, help="") | |
| parser.add_argument("--json", "-j", required=False, type=str, help="") | |
| parser.add_argument("--sdc", "-s", required=False, type=str, help="") | |
| parser.add_argument("--top", "-t", required=False, type=str, help="") | |
| parser.add_argument("--corner", "-c", required=False, type=str, help="") | |
| args = parser.parse_args() | |
| if vpr_options is None: | |
| print("Using default VPR options") | |
| vpr_options = [ | |
| "--max_router_iterations", | |
| "500", | |
| "--routing_failure_predictor", | |
| "off", | |
| "--router_high_fanout_threshold", | |
| "-1", | |
| "--constant_net_method", | |
| "route", | |
| ] | |
| noisy_warnings = "" if log_suffix is None else f"noisy_warnings-{args.device}_{log_suffix}.log" | |
| vpr_options.extend( | |
| [ | |
| "--place_delay_model", | |
| "delta_override", | |
| "--router_lookahead", | |
| "extended_map", | |
| "--allow_dangling_combinational_nodes", | |
| "on", | |
| ] | |
| + ( | |
| [ | |
| "--route_chan_width", | |
| "10", | |
| "--clock_modeling", | |
| "ideal", | |
| "--place_delta_delay_matrix_calculation_method", | |
| "dijkstra", | |
| "--absorb_buffer_luts", | |
| "off", | |
| ] | |
| if args.device == "qlf_k4n8_qlf_k4n8" | |
| else [ | |
| "--route_chan_width", | |
| "100", | |
| "--clock_modeling", | |
| "route", | |
| "--check_route", | |
| "quick", | |
| "--strict_checks", | |
| "off", | |
| "--disable_errors", | |
| "check_unbuffered_edges:check_route", | |
| "--congested_routing_iteration_threshold", | |
| "0.8", | |
| "--incremental_reroute_delay_ripup", | |
| "off", | |
| "--base_cost_type", | |
| "delay_normalized_length_bounded", | |
| "--bb_factor", | |
| "10", | |
| "--initial_pres_fac", | |
| "4.0", | |
| "--check_rr_graph", | |
| "off", | |
| "--pack_high_fanout_threshold", | |
| "PB-LOGIC:18", | |
| "--suppress_warnings", | |
| noisy_warnings | |
| + ",sum_pin_class:check_unbuffered_edges:load_rr_indexed_data_T_values:check_rr_node:trans_per_R:check_route:set_rr_graph_tool_comment", | |
| ] | |
| ) | |
| ) | |
| device_2 = None | |
| if args.device == "qlf_k4n8_qlf_k4n8": | |
| device_1 = f"qlf_k4n8-qlf_k4n8_umc22_{args.corner}" | |
| elif args.device == "qlf_k6n10_qlf_k6n10": | |
| device_1 = "qlf_k6n10-qlf_k6n10_gf12" | |
| else: | |
| device_1 = args.device | |
| device_2 = "wlcsp" | |
| if device_2 is None: | |
| device_2 = device_1 | |
| device_arch = f"{device_1}_{device_2}" | |
| arch_dir = F4PGA_SHARE_DIR / "arch" / device_arch | |
| rr_graph = arch_dir / f"{device_1}.rr_graph.bin" | |
| # qlf* devices use different naming scheme than pp3* ones. | |
| if not rr_graph.exists(): | |
| rr_graph = arch_dir / f"rr_graph_{device_arch}.rr_graph.real.bin" | |
| envvars = { | |
| "DEVICE": args.device, | |
| "FAMILY": args.family, | |
| "EBLIF": args.eblif, | |
| "VPR_OPTIONS": " ".join(vpr_options), | |
| "ARCH_DIR": str(arch_dir), | |
| "ARCH_DEF": str(arch_dir / f"arch_{device_arch}.xml"), | |
| "RR_GRAPH": str(rr_graph), | |
| "PLACE_DELAY": str(arch_dir / f"rr_graph_{device_arch}.place_delay.bin"), | |
| "LOOKAHEAD": str(arch_dir / f"rr_graph_{device_arch}.lookahead.bin"), | |
| "DEVICE_NAME": device_1, | |
| } | |
| if args.pcf is not None: | |
| envvars["PCF"] = args.pcf | |
| if args.net is not None: | |
| envvars["NET"] = args.net | |
| if args.json is not None: | |
| envvars["JSON"] = args.json | |
| if args.sdc is not None: | |
| envvars["SDC"] = args.sdc | |
| if args.top is not None: | |
| envvars["TOP"] = args.top | |
| if args.sdc is not None: | |
| envvars["CORNER"] = args.corner | |
| if log_suffix is not None: | |
| envvars["OUT_NOISY_WARNINGS"] = noisy_warnings | |
| return envvars | |
| # Entrypoints | |
| def generate_constraints(): | |
| print("[F4PGA] Running (deprecated) generate constraints") | |
| if isQuickLogic: | |
| (pcf, eblif, net, part, device, arch_def, corner) = sys_argv[1:8] | |
| place_file_prefix = Path(eblif).stem | |
| scripts_dir = F4PGA_SHARE_DIR / "scripts" | |
| archs_dir = F4PGA_SHARE_DIR / "arch" | |
| p_run_bash_cmds( | |
| f""" | |
| set -e | |
| if [[ '{device}' =~ ^(qlf_.*)$ ]]; then | |
| if [[ '{device}' =~ ^(qlf_k4n8_qlf_k4n8)$ ]];then | |
| DEVICE_PATH='qlf_k4n8-qlf_k4n8_umc22_{corner}' | |
| PINMAPXML="pinmap_qlf_k4n8_umc22.xml" | |
| elif [[ '{device}' =~ ^(qlf_k6n10_qlf_k6n10)$ ]];then | |
| DEVICE_PATH="qlf_k6n10-qlf_k6n10_gf12" | |
| PINMAPXML="pinmap_qlf_k6n10_gf12.xml" | |
| else | |
| echo "ERROR: Unknown qlf device '{device}'" | |
| exit -1 | |
| fi | |
| '{python3}' -m f4pga.utils.quicklogic.qlf_k4n8.create_ioplace \ | |
| --pcf '{pcf}' \ | |
| --blif '{eblif}' \ | |
| --pinmap_xml '{archs_dir}'/"${{DEVICE_PATH}}_${{DEVICE_PATH}}/${{PINMAPXML}}" \ | |
| --csv_file '{part}' \ | |
| --net '{net}' \ | |
| > '{place_file_prefix}_io.place' | |
| elif [[ '{device}' =~ ^(ql-.*)$ ]]; then | |
| if ! [[ '{part}' =~ ^(PU64|WR42|PD64|WD30)$ ]]; then | |
| PINMAPCSV="pinmap_PD64.csv" | |
| CLKMAPCSV="clkmap_PD64.csv" | |
| else | |
| PINMAPCSV='pinmap_{part}.csv' | |
| CLKMAPCSV='clkmap_{part}.csv' | |
| fi | |
| echo "PINMAP FILE : $PINMAPCSV" | |
| echo "CLKMAP FILE : $CLKMAPCSV" | |
| DEVICE_PATH='{device}_wlcsp' | |
| PINMAP='{archs_dir}'/"${{DEVICE_PATH}}/${{PINMAPCSV}}" | |
| '{python3}' -m f4pga.utils.quicklogic.pp3.create_ioplace \ | |
| --pcf '{pcf}' \ | |
| --blif '{eblif}' \ | |
| --map "$PINMAP" \ | |
| --net '{net}' \ | |
| > '{place_file_prefix}_io.place' | |
| '{python3}' -m f4pga.utils.quicklogic.pp3.create_place_constraints \ | |
| --blif '{eblif}' \ | |
| --map '{archs_dir}'/"${{DEVICE_PATH}}/${{CLKMAPCSV}}" \ | |
| -i '{place_file_prefix}_io.place' \ | |
| > '{place_file_prefix}_constraints.place' | |
| # EOS-S3 IOMUX configuration | |
| if [[ '{device}' =~ ^(ql-eos-s3)$ ]]; then | |
| """ | |
| + "\n".join( | |
| [ | |
| f""" | |
| '{python3}' -m f4pga.utils.quicklogic.pp3.eos-s3.iomux_config \ | |
| --eblif '{eblif}' \ | |
| --pcf '{pcf}' \ | |
| --map "$PINMAP" \ | |
| --output-format={fmt[0]} \ | |
| > '{place_file_prefix}_iomux.{fmt[1]}' | |
| """ | |
| for fmt in [["jlink", "jlink"], ["openocd", "openocd"], ["binary", "bin"]] | |
| ] | |
| ) | |
| + f""" | |
| fi | |
| else | |
| echo "FIXME: Unsupported device '{device}'" | |
| exit -1 | |
| fi | |
| """ | |
| ) | |
| else: | |
| (eblif, net, part, device, arch_def) = sys_argv[1:6] | |
| ioplace_file = f"{Path(eblif).stem}.ioplace" | |
| xc7_create_ioplace( | |
| blif=eblif, | |
| map=f"{F4PGA_SHARE_DIR}/arch/{device}/{part}/pinmap.csv", | |
| net=net, | |
| output=ioplace_file, | |
| pcf=Path(sys_argv[6]).open("r") if len(sys_argv) > 6 else None, | |
| ) | |
| xc7_create_place_constraints( | |
| blif=eblif, | |
| net=net, | |
| arch=arch_def, | |
| part=part, | |
| vpr_grid_map=f"{F4PGA_SHARE_DIR}/arch/{device}/vpr_grid_map.csv", | |
| input=ioplace_file, | |
| output="constraints.place", | |
| ) | |
| def pack(): | |
| print("[F4PGA] Running (deprecated) pack") | |
| extra_args = ["--write_block_usage", "block_usage.json"] if isQuickLogic else [] | |
| p_vpr_run(["--pack"] + extra_args, env=p_vpr_env_from_args("pack")) | |
| Path("vpr_stdout.log").rename("pack.log") | |
| def place(): | |
| print("[F4PGA] Running (deprecated) place") | |
| place_cmds = """ | |
| if [ -z $NET ]; then echo "Please provide net file name"; exit 1; fi | |
| """ | |
| if isQuickLogic: | |
| place_cmds += """ | |
| if [ -z $PCF ]; then echo "Please provide pcf file name"; exit 1; fi | |
| PROJECT=$(basename -- "$EBLIF") | |
| PROJECT="${PROJECT%.*}" | |
| VPR_PLACE_FILE="${PROJECT}_constraints.place" | |
| if [ -s $PCF ]; then | |
| echo "Generating constraints ..." | |
| python3 -m f4pga.wrappers.sh.generate_constraints $PCF $EBLIF $NET $PART $DEVICE $ARCH_DEF $CORNER | |
| if [ ! -f ${VPR_PLACE_FILE} ]; then VPR_PLACE_FILE="${PROJECT}_io.place"; fi | |
| else | |
| # Make a dummy empty constraint file | |
| touch ${VPR_PLACE_FILE} | |
| fi | |
| """ | |
| else: | |
| place_cmds += """ | |
| echo "Generating constrains ..." | |
| python3 -m f4pga.wrappers.sh.generate_constraints $EBLIF $NET $PART $DEVICE $ARCH_DEF $PCF | |
| VPR_PLACE_FILE='constraints.place' | |
| """ | |
| place_cmds += 'python3 -m f4pga.wrappers.sh.vpr_run --fix_clusters "${VPR_PLACE_FILE}" --place' | |
| p_run_bash_cmds(place_cmds, env=p_vpr_env_from_args("place")) | |
| Path("vpr_stdout.log").rename("place.log") | |
| def route(): | |
| print("[F4PGA] Running (deprecated) route") | |
| extra_args = ["--write_timing_summary", "timing_summary.json"] if isQuickLogic else [] | |
| p_vpr_run(["--route"] + extra_args, env=p_vpr_env_from_args("pack")) | |
| Path("vpr_stdout.log").rename("route.log") | |
| def synth(): | |
| print("[F4PGA] Running (deprecated) synth") | |
| env = environ.copy() | |
| if environ.get("F4PGA_SHARE_DIR") is None: | |
| env["F4PGA_SHARE_DIR"] = str(F4PGA_SHARE_DIR) | |
| env["UTILS_PATH"] = str(F4PGA_SHARE_DIR / "scripts") | |
| if isQuickLogic: | |
| key = None | |
| for item in ["-F", "--family"]: | |
| if item in sys_argv: | |
| key = item | |
| break | |
| if key is None: | |
| raise Exception("Please specify device family") | |
| family = sys_argv[sys_argv.index(key) + 1] | |
| env.update( | |
| { | |
| "FAMILY": family, | |
| "TECHMAP_PATH": str(F4PGA_SHARE_DIR / "techmaps" / family), | |
| } | |
| ) | |
| else: | |
| env["TECHMAP_PATH"] = str(F4PGA_SHARE_DIR / "techmaps/xc7_vpr/techmap") | |
| p_run_sh_script(ROOT / SH_SUBDIR / "synth.f4pga.sh", env=env) | |
| def write_fasm(genfasm_extra_args=None): | |
| print("[F4PGA] Running (deprecated) write fasm") | |
| p_run_bash_cmds( | |
| f""" | |
| '{which('genfasm')}' \ | |
| ${{ARCH_DEF}} ${{EBLIF}} --device ${{DEVICE_NAME}} \ | |
| ${{VPR_OPTIONS}} \ | |
| --read_rr_graph ${{RR_GRAPH}} {' '.join(genfasm_extra_args) if genfasm_extra_args is not None else ''} | |
| """ | |
| + """ | |
| TOP="${EBLIF%.*}" | |
| FASM_EXTRA="${TOP}_fasm_extra.fasm" | |
| if [ -f $FASM_EXTRA ]; then | |
| echo "writing final fasm (extra: $FASM_EXTRA)" | |
| cat $FASM_EXTRA >> ${TOP}.fasm | |
| fi | |
| """, | |
| env=p_vpr_env_from_args("fasm"), | |
| ) | |
| Path("vpr_stdout.log").rename("fasm.log") | |
| # Xilinx only | |
| def write_bitstream(): | |
| print("[F4PGA] Running (deprecated) write bitstream") | |
| p_run_bash_cmds( | |
| """ | |
| set -e | |
| echo "Writing bitstream ..." | |
| FRM2BIT="" | |
| if [ ! -z ${FRAMES2BIT} ]; then FRM2BIT="--frm2bit ${FRAMES2BIT}"; fi | |
| """ | |
| + f""" | |
| eval set -- $( | |
| getopt \ | |
| --options=d:f:b:p: \ | |
| --longoptions=device:,fasm:,bit:,part: \ | |
| --name $0 -- {' '.join(sys_argv[1:])} | |
| ) | |
| """ | |
| + """ | |
| DEVICE="" | |
| FASM="" | |
| BIT="" | |
| PART=xc7a35tcpg236-1 | |
| while true; do | |
| case "$1" in | |
| -d|--device) DEVICE=$2; shift 2; ;; | |
| -p|--part) PART=$2; shift 2; ;; | |
| -f|--fasm) FASM=$2; shift 2; ;; | |
| -b|--bit) BIT=$2; shift 2; ;; | |
| --) break ;; | |
| esac | |
| done | |
| DATABASE_DIR=${DATABASE_DIR:-$(prjxray-config)} | |
| if [ -z $DEVICE ]; then | |
| # Try to find device name. Accept only when exactly one is found | |
| PART_DIRS=(${DATABASE_DIR}/*/${PART}) | |
| if [ ${#PART_DIRS[@]} -eq 1 ]; then | |
| DEVICE=$(basename $(dirname "${PART_DIRS[0]}")) | |
| else | |
| echo "Please provide device name" | |
| exit 1 | |
| fi | |
| fi | |
| DBROOT=`realpath ${DATABASE_DIR}/${DEVICE}` | |
| if [ -z $FASM ]; then echo "Please provide fasm file name"; exit 1; fi | |
| if [ -z $BIT ]; then echo "Please provide bit file name"; exit 1; fi | |
| xcfasm \ | |
| --db-root ${DBROOT} \ | |
| --part ${PART} \ | |
| --part_file ${DBROOT}/${PART}/part.yaml \ | |
| --sparse \ | |
| --emit_pudc_b_pullup \ | |
| --fn_in ${FASM} \ | |
| --bit_out ${BIT} ${FRM2BIT} | |
| """ | |
| ) | |
| # QuickLogic only | |
| def analysis(): | |
| print("[F4PGA] Running (deprecated) analysis") | |
| p_vpr_run( | |
| [ | |
| "--analysis", | |
| "--gen_post_synthesis_netlist", | |
| "on", | |
| "--gen_post_implementation_merged_netlist", | |
| "on", | |
| "--post_synth_netlist_unconn_inputs", | |
| "nets", | |
| "--post_synth_netlist_unconn_outputs", | |
| "nets", | |
| "--verify_file_digests", | |
| "off", | |
| ], | |
| env=p_vpr_env_from_args("analysis"), | |
| ) | |
| Path("vpr_stdout.log").rename("analysis.log") | |
| def repack(): | |
| print("[F4PGA] Running (deprecated) repack") | |
| p_run_bash_cmds( | |
| """ | |
| DESIGN=${EBLIF/.eblif/} | |
| [ ! -z "${JSON}" ] && JSON_ARGS="--json-constraints ${JSON}" || JSON_ARGS= | |
| [ ! -z "${PCF_PATH}" ] && PCF_ARGS="--pcf-constraints ${PCF_PATH}" || PCF_ARGS= | |
| """ | |
| + f""" | |
| PYTHONPATH='{F4PGA_SHARE_DIR}/scripts':$PYTHONPATH \ | |
| '{python3}' -m f4pga.utils.quicklogic.repacker.repack \ | |
| --vpr-arch ${{ARCH_DEF}} \ | |
| --repacking-rules ${{ARCH_DIR}}/${{DEVICE_NAME}}.repacking_rules.json \ | |
| $JSON_ARGS \ | |
| $PCF_ARGS \ | |
| --eblif-in ${{DESIGN}}.eblif \ | |
| --net-in ${{DESIGN}}.net \ | |
| --place-in ${{DESIGN}}.place \ | |
| --eblif-out ${{DESIGN}}.repacked.eblif \ | |
| --net-out ${{DESIGN}}.repacked.net \ | |
| --place-out ${{DESIGN}}.repacked.place \ | |
| --absorb_buffer_luts on \ | |
| > repack.log 2>&1 | |
| """, | |
| env=p_vpr_env_from_args(), | |
| ) | |
| def generate_bitstream(): | |
| print("[F4PGA] Running (deprecated) generate_bitstream") | |
| parser = ArgumentParser(description=__doc__, formatter_class=RawDescriptionHelpFormatter) | |
| parser.add_argument("--device", "-d", required=True, type=str, help="") | |
| parser.add_argument("--fasm", "-f", required=True, type=str, help="") | |
| parser.add_argument("--bit", "-b", required=True, type=str, help="") | |
| parser.add_argument("--format", "-r", required=False, type=str, help="") | |
| args = parser.parse_args() | |
| if not Path(args.fasm).exists(): | |
| raise Exception(f"File <{args.fasm}> does not exist!") | |
| fmt = "4byte" if args.format is None else args.format | |
| db_root = F4PGA_SHARE_DIR / "fasm_database" / args.device | |
| if "qlf_k4n8" in args.device: | |
| p_run_bash_cmds( | |
| f"'{which('qlf_fasm')}' --db-root '{db_root}' --format '{fmt}' --assemble '{args.fasm}' '{args.bit}'" | |
| ) | |
| elif args.device in ["ql-eos-s3", "ql-pp3e"]: | |
| p_run_bash_cmds(f"qlfasm --dev-type '{args.device}' '{args.fasm}' '{args.bit}'") | |
| else: | |
| raise Exception(f"[bitstream generation] Unsupported device '{args.device}'!") | |
| def generate_libfile(): | |
| print("[F4PGA] Running (deprecated) generate_libfile") | |
| (part, device, corner) = sys_argv[1:4] | |
| p_run_bash_cmds( | |
| f""" | |
| set -e | |
| if [[ '{device}' =~ ^(qlf_k4n8_qlf_k4n8)$ ]];then | |
| DEVICE_1="qlf_k4n8-qlf_k4n8_umc22_{corner}" | |
| PINMAPXML="pinmap_qlf_k4n8_umc22.xml" | |
| INTERFACEXML="interface-mapping_24x24.xml" | |
| DEV="qlf_k4n8_umc22" | |
| else | |
| DEVICE_1={device} | |
| fi | |
| ARCH_DIR='{F4PGA_SHARE_DIR}/arch/'"${{DEVICE_1}}_${{DEVICE_1}}" | |
| """ | |
| + """ | |
| PINMAP_XML=${ARCH_DIR}/${PINMAPXML} | |
| """ | |
| + f""" | |
| '{python3}' -m f4pga.utils.quicklogic.create_lib \ | |
| -n "${{DEV}}_0P72_SSM40" \ | |
| -m fpga_top \ | |
| -c '{part}' \ | |
| -x "${{ARCH_DIR}}/lib/${{INTERFACEXML}}" \ | |
| -l "${{DEV}}_0P72_SSM40.lib" \ | |
| -t "${{ARCH_DIR}}/lib" | |
| """ | |
| ) | |
| def ql(): | |
| print("[F4PGA] Running (deprecated) ql") | |
| env = environ.copy() | |
| if environ.get("F4PGA_SHARE_DIR") is None: | |
| env["F4PGA_SHARE_DIR"] = str(F4PGA_SHARE_DIR) | |
| p_run_sh_script(ROOT / "quicklogic/ql.f4pga.sh", env=env) | |
| def fasm2bels(): | |
| print("[F4PGA] Running (deprecated) fasm2bels") | |
| parser = ArgumentParser(description=__doc__, formatter_class=RawDescriptionHelpFormatter) | |
| parser.add_argument("--device", "-d", required=True, type=str, help="") | |
| parser.add_argument("--bit", "-b", required=True, type=str, help="") | |
| parser.add_argument("--part", "-P", required=True, type=str, help="") | |
| parser.add_argument("--pcf", "-p", required=False, type=str, help="") | |
| parser.add_argument("--out-verilog", "-v", required=False, type=str, help="") | |
| parser.add_argument("--out-pcf", "-o", required=False, type=str, help="") | |
| parser.add_argument("--out-qcf", "-q", required=False, type=str, help="") | |
| args = parser.parse_args() | |
| if args.device not in ["ql-eos-s3", "ql-pp3e"]: | |
| raise Exception(f"[fasm2bels] Unsupported device '{args.device}'") | |
| env = environ.copy() | |
| env["DEVICE"] = args.device | |
| pcf_args = "" if args.pcf is None else f"--input-pcf {args.pcf}" | |
| out_verilog = f"{args.bit}.v" if args.out_verilog is None else args.out_verilog | |
| out_pcf = f"{args.bit}.v.pcf" if args.out_pcf is None else args.out_pcf | |
| out_qcf = f"{args.bit}.v.qcf" if args.out_qcf is None else args.out_qcf | |
| p_run_bash_cmds( | |
| f""" | |
| '{python3}' -m f4pga.utils.quicklogic.pp3.fasm2bels '{args.bit}' \ | |
| --phy-db '{F4PGA_SHARE_DIR}/arch/{args.device}_wlcsp/db_phy.pickle' \ | |
| --device-name "${{DEVICE/ql-/}}" \ | |
| --package-name '{args.part}' \ | |
| --input-type bitstream \ | |
| --output-verilog '{out_verilog}' \ | |
| {pcf_args} \ | |
| --output-pcf '{out_pcf}' \ | |
| --output-qcf '{out_qcf}' | |
| """, | |
| env=env, | |
| ) | |
| def write_bitheader(): | |
| print("[F4PGA] Running (deprecated) write bitheader") | |
| print("Converting bitstream to C Header") | |
| p_run_pym("quicklogic_fasm.bitstream_to_header") | |
| def write_binary(): | |
| print("[F4PGA] Running (deprecated) write binary") | |
| print("Converting bitstream to flashable binary format") | |
| p_run_pym("quicklogic_fasm.bitstream_to_binary") | |
| def write_jlink(): | |
| print("[F4PGA] Running (deprecated) write jlink") | |
| print("Converting bitstream to JLink script") | |
| p_run_pym("quicklogic_fasm.bitstream_to_jlink") | |
| def write_openocd(): | |
| print("[F4PGA] Running (deprecated) write openocd") | |
| print("Converting bitstream to OpenOCD script") | |
| p_run_pym("quicklogic_fasm.bitstream_to_openocd") |