OpenFPGA/openfpga_flow/scripts/run_fpga_flow.py

1065 lines
41 KiB
Python

# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Script Name : run_fpga_flow.py
# Description : This script designed to run different flows supported by
# OpensFPGA project.
# Args : python3 run_fpga_flow.py --help
# Author : Ganesh Gore
# Email : ganesh.gore@utah.edu
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
import os
import sys
import shutil
import time
import traceback
from datetime import timedelta
import shlex
import glob
import json
import argparse
from configparser import ConfigParser, ExtendedInterpolation
import logging
from envyaml import EnvYAML
import glob
import subprocess
import threading
from string import Template
import re
import xml.etree.ElementTree as ET
from importlib import util
if util.find_spec("humanize"):
import humanize
if sys.version_info[0] < 3:
raise Exception("run_fpga_task script must be using Python 3")
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Initialise general paths for the script
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Copy directory where flow file exist
flow_script_dir = os.path.dirname(os.path.abspath(__file__))
# Find OpenFPGA base directory
openfpga_base_dir = os.path.abspath(os.path.join(flow_script_dir, os.pardir, os.pardir))
# Copy directory from where script is laucnhed
# [req to resolve relative paths provided while launching script]
launch_dir = os.getcwd()
# Path section to append in configuration file to interpolate path
task_script_dir = os.path.dirname(os.path.abspath(__file__))
script_env_vars = {
"PATH": {
"OPENFPGA_FLOW_PATH": task_script_dir,
"ARCH_PATH": os.path.join("${PATH:OPENFPGA_PATH}", "arch"),
"OPENFPGA_SHELLSCRIPT_PATH": os.path.join("${PATH:OPENFPGA_PATH}", "OpenFPGAShellScripts"),
"BENCH_PATH": os.path.join("${PATH:OPENFPGA_PATH}", "benchmarks"),
"TECH_PATH": os.path.join("${PATH:OPENFPGA_PATH}", "tech"),
"SPICENETLIST_PATH": os.path.join("${PATH:OPENFPGA_PATH}", "SpiceNetlists"),
"VERILOG_PATH": os.path.join("${PATH:OPENFPGA_PATH}", "VerilogNetlists"),
"OPENFPGA_PATH": os.path.abspath(os.path.join(task_script_dir, os.pardir, os.pardir)),
}
}
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Reading command-line argument
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Helper function to provide better alignment to help print
def formatter(prog):
return argparse.HelpFormatter(prog, max_help_position=60)
parser = argparse.ArgumentParser(formatter_class=formatter)
# Mandatory arguments
parser.add_argument("arch_file", type=str)
parser.add_argument("benchmark_files", type=str, nargs="+")
# parser.add_argument('extraArgs', nargs=argparse.REMAINDER)
parser.add_argument("otherthings", nargs="*")
# Optional arguments
parser.add_argument("--top_module", type=str, default="top")
parser.add_argument("--fpga_flow", type=str, default="yosys_vpr")
parser.add_argument("--flow_config", type=str, help="CAD tools path overrides default setting")
parser.add_argument(
"--run_dir",
type=str,
default=os.path.join(openfpga_base_dir, "tmp"),
help="Directory to store intermidiate file & final results",
)
parser.add_argument(
"--default_tool_path",
type=str,
default=os.path.join(flow_script_dir, os.pardir, "misc", "fpgaflow_default_tool_path.conf"),
help="The configuraton file contains paths to tools as well as keywords to be extracted from logs",
)
parser.add_argument(
"--openfpga_shell_template",
type=str,
default=os.path.join("openfpga_flow", "openfpga_shell_scripts", "example_script.openfpga"),
help="Sample openfpga shell script",
)
parser.add_argument("--openfpga_arch_file", type=str, help="Openfpga architecture file for shell")
parser.add_argument(
"--arch_variable_file", type=str, default=None, help="Openfpga architecture file for shell"
)
# parser.add_argument('--openfpga_sim_setting_file', type=str,
# help="Openfpga simulation file for shell")
# parser.add_argument('--external_fabric_key_file', type=str,
# help="Key file for shell")
parser.add_argument(
"--yosys_tmpl",
type=str,
default=None,
help="Alternate yosys template, generates top_module.blif",
)
parser.add_argument(
"--ys_rewrite_tmpl",
type=str,
default=None,
help="Alternate yosys template, to rewrite verilog netlist",
)
parser.add_argument("--verific", action="store_true", help="Run yosys with verific enabled")
parser.add_argument("--disp", action="store_true", help="Open display while running VPR")
parser.add_argument("--debug", action="store_true", help="Run script in debug mode")
# Blif_VPR Only flow arguments
parser.add_argument("--activity_file", type=str, help="Activity file used while running yosys flow")
parser.add_argument(
"--base_verilog",
type=str,
help="Original Verilog file to run verification in " + "blif_VPR flow",
)
# ACE2 and power estimation related arguments
parser.add_argument("--K", type=int, help="LUT Size, if not specified extracted from arch file")
parser.add_argument("--power", action="store_true")
parser.add_argument("--power_tech", type=str, help="Power tech xml file for power calculation")
parser.add_argument("--ace_d", type=float, help="Specify the default signal density of PIs in ACE2")
parser.add_argument(
"--ace_p", type=float, help="Specify the default signal probablity of PIs in ACE2"
)
parser.add_argument("--black_box_ace", action="store_true")
# VPR Options
parser.add_argument("--min_route_chan_width", type=float, help="Turn on min_route_chan_width")
parser.add_argument(
"--max_route_width_retry",
type=int,
default=100,
help="Maximum iterations to perform to reroute",
)
parser.add_argument("--fix_route_chan_width", type=int, help="Turn on fix_route_chan_width")
parser.add_argument(
"--vpr_timing_pack_off", action="store_true", help="Turn off the timing-driven pack for vpr"
)
parser.add_argument(
"--vpr_place_clb_pin_remap", action="store_true", help="Turn on place_clb_pin_remap in VPR"
)
parser.add_argument(
"--vpr_max_router_iteration", type=int, help="Specify the max router iteration in VPR"
)
parser.add_argument(
"--vpr_route_breadthfirst",
action="store_true",
help="Use the breadth-first routing algorithm of VPR",
)
parser.add_argument(
"--vpr_use_tileable_route_chan_width",
action="store_true",
help="Turn on the conversion to " + "tileable_route_chan_width in VPR",
)
# VPR - FPGA-X2P Extension
X2PParse = parser.add_argument_group("VPR-FPGA-X2P Extension")
X2PParse.add_argument(
"--vpr_fpga_x2p_rename_illegal_port",
action="store_true",
help="Rename illegal ports option of VPR FPGA SPICE",
)
X2PParse.add_argument(
"--vpr_fpga_x2p_signal_density_weight",
type=float,
help="Specify the signal_density_weight of VPR FPGA SPICE",
)
X2PParse.add_argument(
"--vpr_fpga_x2p_sim_window_size",
type=float,
help="specify the sim_window_size of VPR FPGA SPICE",
)
X2PParse.add_argument(
"--vpr_fpga_x2p_compact_routing_hierarchy",
action="store_true",
help="Compact_routing_hierarchy",
)
X2PParse.add_argument(
"--vpr_fpga_x2p_duplicate_grid_pin", action="store_true", help="Added duplicated grid pin"
)
# VPR - FPGA-SPICE Extension
SPParse = parser.add_argument_group("FPGA-SPICE Extension")
SPParse.add_argument("--vpr_fpga_spice", action="store_true", help="Print SPICE netlists in VPR")
SPParse.add_argument(
"--vpr_fpga_spice_sim_mt_num", type=int, help="Specify the option sim_mt_num of VPR FPGA SPICE"
)
SPParse.add_argument(
"--vpr_fpga_spice_print_component_tb",
action="store_true",
help="Output component-level testbench",
)
SPParse.add_argument(
"--vpr_fpga_spice_print_grid_tb", action="store_true", help="Output grid-level testbench"
)
SPParse.add_argument(
"--vpr_fpga_spice_print_top_testbench",
action="store_true",
help="Output full-chip-level testbench",
)
SPParse.add_argument(
"--vpr_fpga_spice_leakage_only",
action="store_true",
help="Turn on leakage_only mode in VPR FPGA SPICE",
)
SPParse.add_argument(
"--vpr_fpga_spice_parasitic_net_estimation_off",
action="store_true",
help="Turn off parasitic_net_estimation in VPR FPGA SPICE",
)
SPParse.add_argument(
"--vpr_fpga_spice_testbench_load_extraction_off",
action="store_true",
help="Turn off testbench_load_extraction in VPR FPGA SPICE",
)
SPParse.add_argument("--vpr_fpga_spice_simulator_path", type=str, help="Specify simulator path")
# VPR - FPGA-Verilog Extension
VeriPar = parser.add_argument_group("FPGA-Verilog Extension")
VeriPar.add_argument(
"--vpr_fpga_verilog", action="store_true", help="Generator verilog of VPR FPGA SPICE"
)
VeriPar.add_argument(
"--vpr_fpga_verilog_dir", type=str, help="path to store generated verilog files"
)
VeriPar.add_argument(
"--vpr_fpga_verilog_include_timing",
action="store_true",
help="Print delay specification in Verilog files",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_include_signal_init",
action="store_true",
help="Print signal initialization in Verilog files",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_autocheck_top_testbench",
action="store_true",
help="Print autochecked top-level " + "testbench for Verilog Generator of VPR FPGA SPICE",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_formal_verification_top_netlist",
action="store_true",
help="Print formal top Verilog files",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_include_icarus_simulator",
action="store_true",
help="dd syntax and definition" + " required to use Icarus Verilog simulator",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_user_defined_template", action="store_true", help="Unknown parameter"
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_report_timing_tcl",
action="store_true",
help="Generate tcl script useful " + "for timing report generation",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_report_timing_rpt_path",
type=str,
help="Specify path for report timing results",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_sdc_pnr",
action="store_true",
help="Generate sdc file to constraint Hardware P&R",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_sdc_analysis", action="store_true", help="Generate sdc file to do STA"
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_top_tb",
action="store_true",
help="Print top-level testbench for Verilog Generator " + "of VPR FPGA SPICE",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_input_blif_tb",
action="store_true",
help="Print testbench" + "for input blif file in Verilog Generator",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_print_simulation_ini",
action="store_true",
help="Create simulation INI file",
)
VeriPar.add_argument(
"--vpr_fpga_verilog_explicit_mapping", action="store_true", help="Explicit Mapping"
)
# VPR - FPGA-Bitstream Extension
BSparse = parser.add_argument_group("FPGA-Bitstream Extension")
BSparse.add_argument(
"--vpr_fpga_bitstream_generator", action="store_true", help="Generate FPGA-SPICE bitstream"
)
# Regression test option
RegParse = parser.add_argument_group("Regression Test Extension")
RegParse.add_argument(
"--end_flow_with_test", action="store_true", help="Run verification test at the end"
)
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Global varaibles declaration
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Setting up print and logging system
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format="%(levelname)s - %(message)s")
logger = logging.getLogger("OpenFPGA_Flow_Logs")
# variable to store script_configuration and cad tool paths
config, cad_tools = None, None
ExecTime = {}
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Main program starts here
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
def main():
logger.debug("Script Launched in " + os.getcwd())
check_required_file(args.default_tool_path)
read_script_config(args.default_tool_path)
validate_command_line_arguments()
prepare_run_directory(args.run_dir)
if args.fpga_flow == "yosys_vpr":
logger.info('Running "yosys_vpr" Flow')
run_yosys_with_abc()
# TODO Make it optional if activity file is provided
if args.power:
run_ace2()
run_pro_blif_3arg()
else:
# Make a copy of the blif file to be compatible with vpr flow
shutil.copy(args.top_module + "_yosys_out.blif", args.top_module + ".blif")
# Always Generate the post-synthesis verilog files
run_rewrite_verilog()
if args.fpga_flow == "vpr_blif":
collect_files_for_vpr()
if args.fpga_flow == "yosys":
run_yosys_with_abc()
if not (args.fpga_flow == "yosys"):
logger.info("Running OpenFPGA Shell Engine ")
run_openfpga_shell()
if args.end_flow_with_test:
run_netlists_verification()
ExecTime["End"] = time.time()
def timestr(x):
return (
humanize.naturaldelta(timedelta(seconds=x))
if "humanize" in sys.modules
else str(int(x)) + " Sec "
)
if args.fpga_flow == "yosys":
TimeInfo = "Openfpga_flow completed, " + "Total Time Taken %s " % timestr(
ExecTime["End"] - ExecTime["Start"]
)
else:
TimeInfo = (
"Openfpga_flow completed, "
+ "Total Time Taken %s " % timestr(ExecTime["End"] - ExecTime["Start"])
+ "VPR Time %s " % timestr(ExecTime["VPREnd"] - ExecTime["VPRStart"])
)
TimeInfo += (
"Verification Time %s "
% timestr(ExecTime["VerificationEnd"] - ExecTime["VerificationStart"])
if args.end_flow_with_test
else ""
)
logger.info(TimeInfo)
exit()
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Subroutines starts here
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
def check_required_file(default_tool_path):
"""Function ensure existace of all required files for the script"""
files_dict = {
"CAD TOOL PATH": default_tool_path,
}
for filename, filepath in files_dict.items():
if not os.path.isfile(filepath):
clean_up_and_exit("Not able to locate default file " + filename + " under " + filepath)
def read_script_config(default_tool_path):
"""This fucntion reads default CAD tools path from configuration file"""
global config, cad_tools
config = ConfigParser(interpolation=ExtendedInterpolation())
config.read_dict(script_env_vars)
default_cad_tool_conf = default_tool_path
config.read_file(open(default_cad_tool_conf))
if args.flow_config:
config.read_file(open(args.flow_config))
if not "CAD_TOOLS_PATH" in config.sections():
clean_up_and_exit("Missing CAD_TOOLS_PATH in openfpga_flow config")
cad_tools = config["CAD_TOOLS_PATH"]
if args.arch_variable_file:
_, file_extension = os.path.splitext(args.arch_variable_file)
if file_extension in [".yml", ".yaml"]:
script_env_vars["PATH"].update(
EnvYAML(args.arch_variable_file, include_environment=False)
)
if file_extension in [
".json",
]:
with open(args.arch_variable_file, "r") as fp:
script_env_vars["PATH"].update(json.load(fp))
def validate_command_line_arguments():
"""
This function validate the command line arguments
FLOW_SCRIPT_CONFIG->valid_flows :
Key is used to validate if the request flow is supported by the script
CMD_ARGUMENT_DEPENDANCY :
Validates the dependencies of the command arguments
Checks the following file exists and replaces them with an absolute path
- All architecture files
- Benchmark files
- Power tech files
- Run directory
- Activity file
- Base verilog file
"""
logger.info("Validating command line arguments")
if args.debug:
logger.info("Setting loggger in debug mode")
logger.setLevel(logging.DEBUG)
# Check if flow supported
if not args.fpga_flow in config.get("FLOW_SCRIPT_CONFIG", "valid_flows"):
clean_up_and_exit("%s Flow not supported" % args.fpga_flow)
# Check if argument list is consistant
for eacharg, dependent in config.items("CMD_ARGUMENT_DEPENDANCY"):
if getattr(args, eacharg, None):
dependent = dependent.split(",")
for eachdep in dependent:
if not any([getattr(args, i, 0) for i in eachdep.split("|")]):
clean_up_and_exit(
"'%s' argument depends on (%s) arguments"
% (eacharg, ", ".join(dependent).replace("|", " or "))
)
# Check if architecrue files exists
args.arch_file = os.path.abspath(args.arch_file)
if not os.path.isfile(args.arch_file):
clean_up_and_exit("VPR architecture file not found. -%s" % args.arch_file)
args.openfpga_arch_file = os.path.abspath(args.openfpga_arch_file)
if not os.path.isfile(args.openfpga_arch_file):
clean_up_and_exit("OpenFPGA architecture file not found. -%s" % args.openfpga_arch_file)
# Filter provided benchmark files
for index, everyinput in enumerate(args.benchmark_files):
args.benchmark_files[index] = os.path.abspath(everyinput)
if os.path.isdir(args.benchmark_files[index]):
logger.warning("Skipping directory in bench %s" % everyinput)
logger.warning(
"Directory is not support in benchmark list" + "use wildcard pattern to add files"
)
continue
for everyfile in glob.glob(args.benchmark_files[index]):
if not os.path.isfile(everyfile):
clean_up_and_exit("Failed to copy benchmark file -%s" % args.arch_file)
# Filter provided powertech files
if args.power_tech:
args.power_tech = os.path.abspath(args.power_tech)
if not os.path.isfile(args.power_tech):
clean_up_and_exit("Power Tech file not found. -%s" % args.power_tech)
# Expand run directory to absolute path
args.run_dir = os.path.abspath(args.run_dir)
if args.power:
if args.activity_file:
args.activity_file = os.path.abspath(args.activity_file)
if args.base_verilog:
args.base_verilog = os.path.abspath(args.base_verilog)
def prepare_run_directory(run_dir):
"""
Prepares run directory to run
1. Change current directory to run_dir
2. Copy architecture XML file to run_dir
3. Copy circuit files to run_dir
"""
logger.info("Run directory : %s" % run_dir)
if os.path.isdir(run_dir):
no_of_files = len(next(os.walk(run_dir))[2])
shutil.rmtree(run_dir)
os.makedirs(run_dir)
# Clean run_dir is created change working directory
os.chdir(run_dir)
# Create arch dir in run_dir and copy flattened architecture file
os.mkdir("arch")
tmpl = Template(open(args.arch_file, encoding="utf-8").read())
arch_filename = os.path.basename(args.arch_file)
args.arch_file = os.path.join(run_dir, "arch", arch_filename)
with open(args.arch_file, "w", encoding="utf-8") as archfile:
archfile.write(tmpl.safe_substitute(script_env_vars["PATH"]))
if args.openfpga_arch_file:
tmpl = Template(open(args.openfpga_arch_file, encoding="utf-8").read())
arch_filename = os.path.basename(args.openfpga_arch_file)
args.openfpga_arch_file = os.path.join(run_dir, "arch", arch_filename)
with open(args.openfpga_arch_file, "w", encoding="utf-8") as archfile:
archfile.write(tmpl.safe_substitute(script_env_vars["PATH"]))
# Sanitize provided openshell template, if provided
if args.openfpga_shell_template:
if not os.path.isfile(args.openfpga_shell_template or ""):
logger.error("Openfpga shell file - %s" % args.openfpga_shell_template)
clean_up_and_exit(
"Provided openfpga_shell_template"
+ f" {args.openfpga_shell_template} file not found"
)
else:
shutil.copy(args.openfpga_shell_template, args.top_module + "_template.openfpga")
# Create benchmark dir in run_dir and copy flattern architecture file
os.mkdir("benchmark")
try:
for index, eachfile in enumerate(args.benchmark_files):
args.benchmark_files[index] = shutil.copy2(
eachfile, os.path.join(os.curdir, "benchmark")
)
except:
logger.exception("Failed to copy all benchmark file to run_dir")
def clean_up_and_exit(msg, clean=False):
logger.error("Current working directory : " + os.getcwd())
logger.error(msg)
logger.error("Exiting . . . . . .")
exit(1)
def create_yosys_params():
tree = ET.parse(args.arch_file)
root = tree.getroot()
try:
lut_size = max(
[
int(pb_type.find("input").get("num_pins"))
for pb_type in root.iter("pb_type")
if pb_type.get("class") == "lut"
]
)
logger.info("Extracted lut_size size from arch XML = %s", lut_size)
logger.info("Running Yosys with lut_size = %s", lut_size)
except:
logger.exception("Failed to extract lut_size from XML file")
clean_up_and_exit("")
args.K = lut_size
# Yosys script parameter mapping
ys_params = script_env_vars["PATH"]
for indx in range(0, len(OpenFPGAArgs), 2):
tmpVar = OpenFPGAArgs[indx][2:].upper()
ys_params[tmpVar] = OpenFPGAArgs[indx + 1]
if not args.verific:
ys_params["VERILOG_FILES"] = " ".join(
[shlex.quote(eachfile) for eachfile in args.benchmark_files]
)
if not "READ_VERILOG_OPTIONS" in ys_params:
ys_params["READ_VERILOG_OPTIONS"] = ""
else:
if "ADD_INCLUDE_DIR" not in ys_params:
ys_params["ADD_INCLUDE_DIR"] = ""
if "ADD_LIBRARY_DIR" not in ys_params:
ys_params["ADD_LIBRARY_DIR"] = ""
if "ADD_BLACKBOX_MODULES" not in ys_params:
ys_params["ADD_BLACKBOX_MODULES"] = ""
if "READ_HDL_FILE" not in ys_params:
ys_params["READ_HDL_FILE"] = ""
if "READ_LIBRARY" not in ys_params:
ys_params["READ_LIBRARY"] = ""
if "VERIFIC_VERILOG_STANDARD" not in ys_params:
ys_params["VERIFIC_VERILOG_STANDARD"] = "-vlog2k"
if "VERIFIC_SYSTEMVERILOG_STANDARD" not in ys_params:
ys_params["VERIFIC_SYSTEMVERILOG_STANDARD"] = "-sv"
if "VERIFIC_VHDL_STANDARD" not in ys_params:
ys_params["VERIFIC_VHDL_STANDARD"] = "-vhdl"
ext_to_standard_map = {
".v": ys_params["VERIFIC_VERILOG_STANDARD"],
".vh": ys_params["VERIFIC_VERILOG_STANDARD"],
".verilog": ys_params["VERIFIC_VERILOG_STANDARD"],
".vlg": ys_params["VERIFIC_VERILOG_STANDARD"],
".sv": ys_params["VERIFIC_SYSTEMVERILOG_STANDARD"],
".svh": ys_params["VERIFIC_SYSTEMVERILOG_STANDARD"],
".vhd": ys_params["VERIFIC_VHDL_STANDARD"],
".vhdl": ys_params["VERIFIC_VHDL_STANDARD"],
}
lib_files = []
include_dirs = set([os.path.dirname(eachfile) for eachfile in args.benchmark_files])
if "VERIFIC_INCLUDE_DIR" in ys_params:
include_dirs.update(ys_params["VERIFIC_INCLUDE_DIR"].split(","))
if include_dirs and not ys_params["ADD_INCLUDE_DIR"]:
ys_params["ADD_INCLUDE_DIR"] = "\n".join(
["verific -vlog-incdir " + shlex.quote(eachdir) for eachdir in include_dirs]
)
if "VERIFIC_LIBRARY_DIR" in ys_params:
ys_params["ADD_LIBRARY_DIR"] = "\n".join(
[
"verific -vlog-libdir " + shlex.quote(eachdir)
for eachdir in ys_params["VERIFIC_LIBRARY_DIR"].split(",")
]
)
try:
for param, value in ys_params.items():
if param.startswith("VERIFIC_READ_LIB_NAME"):
index = param[len("VERIFIC_READ_LIB_NAME") :]
src_param = "VERIFIC_READ_LIB_SRC" + index
if src_param in ys_params:
src_files = []
for name in ys_params[src_param].split(","):
for eachfile in args.benchmark_files:
if name.strip() in eachfile:
src_files.append(eachfile)
break
if not src_files:
clean_up_and_exit("Failed to locate verific library files")
lib_files.extend(src_files)
filename, file_extension = os.path.splitext(src_files[0])
ys_params["READ_LIBRARY"] += " ".join(
["verific -work", ys_params[param], ext_to_standard_map[file_extension]]
+ [shlex.quote(eachfile) for eachfile in src_files]
+ ["\n"]
)
standard_to_sources = {}
for eachfile in args.benchmark_files:
if eachfile in lib_files:
continue
filename, file_extension = os.path.splitext(eachfile)
if ext_to_standard_map[file_extension] in standard_to_sources:
standard_to_sources[ext_to_standard_map[file_extension]].append(eachfile)
else:
standard_to_sources[ext_to_standard_map[file_extension]] = [eachfile]
for standard, sources in standard_to_sources.items():
ys_params["READ_HDL_FILE"] += " ".join(
[
"verific",
(
"-L " + ys_params["VERIFIC_SEARCH_LIB"]
if "VERIFIC_SEARCH_LIB" in ys_params
else ""
),
standard,
" ".join([shlex.quote(src) for src in sources]),
"\n",
]
)
except:
logger.exception("Failed to determine design file type")
clean_up_and_exit("")
if "YOSYS_CELL_SIM_VERILOG" in ys_params:
ys_params["READ_HDL_FILE"] += " ".join(
[
"verific",
ys_params["VERIFIC_VERILOG_STANDARD"],
ys_params["YOSYS_CELL_SIM_VERILOG"],
"\n",
]
)
if "YOSYS_CELL_SIM_SYSTEMVERILOG" in ys_params:
ys_params["READ_HDL_FILE"] += " ".join(
[
"verific",
ys_params["VERIFIC_SYSTEMVERILOG_STANDARD"],
ys_params["YOSYS_CELL_SIM_SYSTEMVERILOG"],
"\n",
]
)
if "YOSYS_CELL_SIM_VHDL" in ys_params:
ys_params["READ_HDL_FILE"] += " ".join(
[
"verific",
ys_params["VERIFIC_VHDL_STANDARD"],
ys_params["YOSYS_CELL_SIM_VHDL"],
"\n",
]
)
if "YOSYS_BLACKBOX_MODULES" in ys_params:
ys_params["ADD_BLACKBOX_MODULES"] = "blackbox " + " ".join(
["\\" + mod for mod in ys_params["YOSYS_BLACKBOX_MODULES"].split(",")]
)
ys_params["TOP_MODULE"] = args.top_module
ys_params["LUT_SIZE"] = lut_size
ys_params["OUTPUT_BLIF"] = args.top_module + "_yosys_out.blif"
ys_params["OUTPUT_VERILOG"] = args.top_module + "_output_verilog.v"
return ys_params
def run_yosys_with_abc():
"""
Execute yosys with ABC and optional blackbox support
"""
ys_params = create_yosys_params()
yosys_template = (
args.yosys_tmpl
if args.yosys_tmpl
else os.path.join(cad_tools["misc_dir"], "ys_tmpl_yosys_vpr_flow.ys")
)
tmpl = Template(open(yosys_template, encoding="utf-8").read())
with open("yosys.ys", "w") as archfile:
archfile.write(tmpl.safe_substitute(ys_params))
run_command("Run yosys", "yosys_output.log", [cad_tools["yosys_path"], "yosys.ys"])
def run_odin2():
pass
def run_abc_vtr():
pass
def run_abc_for_standarad():
pass
def run_ace2():
if args.black_box_ace:
with open(args.top_module + "_yosys_out.blif", "r") as fp:
blif_lines = fp.readlines()
with open(args.top_module + "_bb.blif", "w") as fp:
for eachline in blif_lines:
if ".names" in eachline:
input_nets = eachline.split()[1:]
if len(input_nets) - 1 > args.K:
logger.error("One module in blif have more inputs" + " than K value")
# Map CEll to each logic in blif
map_nets = (input_nets[:-1] + ["unconn"] * args.K)[: args.K]
map_nets = [
"I[%d]=%s" % (indx, eachnet) for indx, eachnet in enumerate(map_nets)
]
map_nets += ["O[0]=%s\n" % input_nets[-1]]
fp.write(".subckt CELL ")
fp.write(" ".join(map_nets))
else:
fp.write(eachline)
declar_input = " ".join(["I[%d]" % i for i in range(args.K)])
model_tmpl = (
"\n"
+ ".model CELL\n"
+ ".inputs "
+ declar_input
+ " \n"
+ ".outputs O[0]\n"
+ ".blackbox\n"
+ ".end\n"
)
fp.write(model_tmpl)
# Prepare ACE run command
command = [
"-b",
args.top_module + ("_bb.blif" if args.black_box_ace else "_yosys_out.blif"),
"-o",
args.top_module + "_ace_out.act",
"-n",
args.top_module + "_ace_out.blif",
"-c",
"clk",
]
command += ["-d", "%.4f" % args.ace_d] if args.ace_d else [""]
command += ["-p", "%.4f" % args.ace_d] if args.ace_p else [""]
try:
filename = args.top_module + "_ace2_output.txt"
with open(filename, "w+") as output:
process = subprocess.run(
[cad_tools["ace_path"]] + command,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
)
output.write(process.stdout)
if process.returncode:
logger.info("ACE2 failed with returncode %d", process.returncode)
raise subprocess.CalledProcessError(0, command)
except:
logger.exception("Failed to run ACE2")
clean_up_and_exit("")
logger.info("ACE2 output is written in file %s" % filename)
def run_pro_blif_3arg():
command = [
"-i",
args.top_module + "_ace_out.blif",
"-o",
args.top_module + ".blif",
"-initial_blif",
args.top_module + "_yosys_out.blif",
]
try:
filename = args.top_module + "_blif_3args_output.txt"
with open(filename, "w+") as output:
process = subprocess.run(
["perl", cad_tools["pro_blif_path"]] + command,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
)
output.write(process.stdout)
if process.returncode:
logger.info("blif_3args script failed with returncode %d", process.returncode)
except:
logger.exception("Failed to run blif_3args")
clean_up_and_exit("")
logger.info("blif_3args output is written in file %s" % filename)
def collect_files_for_vpr():
# Sanitize provided Benchmark option
if len(args.benchmark_files) > 1:
logger.error("Expecting Single Benchmark Blif file.")
if not os.path.isfile(args.benchmark_files[0] or ""):
clean_up_and_exit("Provided Blif file not found")
shutil.copy(args.benchmark_files[0], args.top_module + ".blif")
# Sanitize provided Activity file option
if args.power:
if not os.path.isfile(args.activity_file or ""):
logger.error("Activity File - %s" % args.activity_file)
clean_up_and_exit("Provided activity file not found")
shutil.copy(args.activity_file, args.top_module + "_ace_out.act")
else:
if os.path.isfile(args.activity_file):
shutil.copy(args.activity_file, args.top_module + "_ace_out.act")
# Sanitize provided Benchmark option
if not os.path.isfile(args.base_verilog or ""):
logger.error("Base Verilog File - %s" % args.base_verilog)
clean_up_and_exit("Provided base_verilog file not found")
shutil.copy(args.base_verilog, args.top_module + "_output_verilog.v")
def run_openfpga_shell():
ExecTime["VPRStart"] = time.time()
# bench_blif, fixed_chan_width, logfile, route_only=False
tmpl = Template(open(args.top_module + "_template.openfpga", encoding="utf-8").read())
path_variables = script_env_vars["PATH"]
path_variables["TOP_MODULE"] = args.top_module
path_variables["VPR_ARCH_FILE"] = args.arch_file
path_variables["OPENFPGA_ARCH_FILE"] = args.openfpga_arch_file
path_variables["VPR_TESTBENCH_BLIF"] = args.top_module + ".blif"
path_variables["ACTIVITY_FILE"] = args.top_module + "_ace_out.act"
path_variables["REFERENCE_VERILOG_TESTBENCH"] = args.top_module + "_output_verilog.v"
for indx in range(0, len(OpenFPGAArgs), 2):
tmpVar = OpenFPGAArgs[indx][2:].upper()
path_variables[tmpVar] = OpenFPGAArgs[indx + 1]
with open(args.top_module + "_run.openfpga", "w", encoding="utf-8") as archfile:
archfile.write(tmpl.safe_substitute(path_variables))
command = [cad_tools["openfpga_shell_path"], "-batch", "-f", args.top_module + "_run.openfpga"]
run_command("OpenFPGA Shell Run", "openfpgashell.log", command)
ExecTime["VPREnd"] = time.time()
extract_vpr_stats("openfpgashell.log")
def extract_vpr_stats(logfile, r_filename="vpr_stat", parse_section="vpr"):
section = (
"DEFAULT_PARSE_RESULT_POWER" if parse_section == "power" else "DEFAULT_PARSE_RESULT_VPR"
)
vpr_log = open(logfile).read()
resultDict = {}
for name, value in config.items(section):
reg_string, filt_function = value.split(",")
reg_result = re.findall(reg_string[1:-1], vpr_log)
if reg_result:
try:
if "lambda" in filt_function.strip():
eval("ParseFunction = " + filt_function.strip())
extract_val = ParseFunction(reg_result)
elif filt_function.strip() == "int":
extract_val = int(reg_result[-1])
elif filt_function.strip() == "float":
extract_val = float(reg_result[-1])
elif filt_function.strip() == "str":
extract_val = str(reg_result[-1])
elif filt_function.strip() == "scientific":
try:
mult = {
"m": 1e-3,
"u": 1e-6,
"n": 1e-9,
"K": 1e-3,
"M": 1e-6,
"G": 1e-9,
}.get(reg_result[-1][1], 1)
except:
mult = 1
extract_val = float(reg_result[-1][0]) * mult
else:
extract_val = reg_result[-1]
except:
logger.exception("Filter failed")
extract_val = "Filter Failed"
resultDict[name] = extract_val
dummyparser = ConfigParser()
dummyparser.read_dict({"RESULTS": resultDict})
with open(r_filename + ".result", "w") as configfile:
dummyparser.write(configfile)
logger.info("%s result extracted in file %s" % (parse_section, r_filename + ".result"))
def run_rewrite_verilog():
# Rewrite the verilog after optimization
# If there is no template script provided, use a default template
# If there is a template script provided, replace parameters from configuration
if not args.ys_rewrite_tmpl:
script_cmd = [
"read_blif %s" % args.top_module + ".blif",
"write_verilog %s" % args.top_module + "_output_verilog.v",
]
command = [cad_tools["yosys_path"], "-p", "; ".join(script_cmd)]
run_command("Yosys", "yosys_rewrite.log", command)
else:
# Yosys script parameter mapping
ys_rewrite_params = create_yosys_params()
# Split a series of scripts by delim ';'
# And execute the scripts serially
for iteration_idx, curr_rewrite_tmpl in enumerate(args.ys_rewrite_tmpl.split(";")):
tmpl = Template(open(curr_rewrite_tmpl, encoding="utf-8").read())
logger.info("Yosys rewrite iteration: " + str(iteration_idx))
with open("yosys_rewrite_" + str(iteration_idx) + ".ys", "w") as archfile:
archfile.write(tmpl.safe_substitute(ys_rewrite_params))
run_command(
"Run yosys",
"yosys_rewrite_output_" + str(iteration_idx) + ".log",
[cad_tools["yosys_path"], "yosys_rewrite_" + str(iteration_idx) + ".ys"],
)
def run_netlists_verification(exit_if_fail=True):
ExecTime["VerificationStart"] = time.time()
compiled_file = "compiled_" + args.top_module
# include_netlists = args.top_module+"_include_netlists.v"
tb_top_formal = args.top_module + "_top_formal_verification_random_tb"
tb_top_autochecked = args.top_module + "_autocheck_top_tb"
# netlists_path = args.vpr_fpga_verilog_dir_val+"/SRC/"
command = [cad_tools["iverilog_path"]]
command += ["-o", compiled_file]
command += ["./SRC/%s_include_netlists.v" % args.top_module]
command += ["-s"]
if args.vpr_fpga_verilog_formal_verification_top_netlist:
command += [tb_top_formal]
else:
command += [tb_top_autochecked]
# TODO: This is NOT flexible!!! We should consider to make the include directory customizable through options
# Add source directory to the include dir
command += ["-I", "./SRC"]
run_command("iverilog_verification", "iverilog_output.txt", command)
vvp_command = ["vvp", compiled_file]
output = run_command("vvp_verification", "vvp_sim_output.txt", vvp_command)
if "Succeed" in output:
logger.info("VVP Simulation Successful")
else:
logger.error(str(output).split("\n")[-1])
if exit_if_fail:
clean_up_and_exit("Failed to run VVP verification")
ExecTime["VerificationEnd"] = time.time()
def run_command(taskname, logfile, command, exit_if_fail=True):
logger.info("Launching %s " % taskname)
with open(logfile, "w") as output:
try:
output.write(" ".join(command) + "\n")
process = subprocess.run(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True
)
output.write(process.stdout)
output.write(process.stderr)
output.write(str(process.returncode))
if "openfpgashell" in logfile:
filter_openfpga_output(process.stdout)
if process.returncode:
logger.error("%s run failed with returncode %d" % (taskname, process.returncode))
logger.error("command %s" % " ".join(command))
filter_failed_process_output(process.stderr)
if exit_if_fail:
clean_up_and_exit("Failed to run %s task" % taskname)
except Exception:
logger.exception("%s failed to execute" % (taskname))
traceback.print_exc(file=output)
if exit_if_fail:
clean_up_and_exit("Failed to run %s task" % taskname)
logger.info("%s is written in file %s" % (taskname, logfile))
return process.stdout
def filter_openfpga_output(vpr_output):
stdout = iter(vpr_output.split("\n"))
try:
for i in range(50):
if "Version:" in next(stdout):
logger.info("OpenFPGAShell %s %s" % (next(stdout), next(stdout)))
break
except StopIteration:
pass
def filter_failed_process_output(vpr_output):
for line in vpr_output.split("\n"):
elements_to_log = ["error", "what()"]
if any(match in line.lower() for match in elements_to_log):
logger.error("-->>" + line)
if __name__ == "__main__":
ExecTime["Start"] = time.time()
# args = parser.parse_args()
args, OpenFPGAArgs = parser.parse_known_args()
main()