OpenFPGA/openfpga_flow/scripts/run_fpga_flow.py

1061 lines
40 KiB
Python
Raw Normal View History

# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# 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
2019-11-16 20:10:04 -06:00
# Email : ganesh.gore@utah.edu
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2019-08-09 17:49:05 -05:00
import os
import sys
2019-08-09 17:49:05 -05:00
import shutil
import time
2021-02-03 11:34:34 -06:00
import traceback
from datetime import timedelta
2019-08-09 17:49:05 -05:00
import shlex
import glob
import json
2019-08-09 17:49:05 -05:00
import argparse
from configparser import ConfigParser, ExtendedInterpolation
import logging
from envyaml import EnvYAML
2019-08-09 17:49:05 -05:00
import glob
import subprocess
import threading
from string import Template
import re
2019-08-09 17:49:05 -05:00
import xml.etree.ElementTree as ET
from importlib import util
2022-11-21 16:21:31 -06:00
if util.find_spec("humanize"):
import humanize
2019-08-09 17:49:05 -05:00
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
2019-08-09 17:49:05 -05:00
flow_script_dir = os.path.dirname(os.path.abspath(__file__))
# Find OpenFPGA base directory
2022-11-21 16:21:31 -06:00
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]
2019-08-09 17:49:05 -05:00
launch_dir = os.getcwd()
# Path section to append in configuration file to interpolate path
2019-08-31 22:55:32 -05:00
task_script_dir = os.path.dirname(os.path.abspath(__file__))
2022-11-21 16:21:31 -06:00
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)),
}
}
2019-08-09 17:49:05 -05:00
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Reading command-line argument
2019-08-09 17:49:05 -05:00
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Helper function to provide better alignment to help print
2022-11-21 16:21:31 -06:00
def formatter(prog):
return argparse.HelpFormatter(prog, max_help_position=60)
parser = argparse.ArgumentParser(formatter_class=formatter)
# Mandatory arguments
2022-11-21 16:21:31 -06:00
parser.add_argument("arch_file", type=str)
parser.add_argument("benchmark_files", type=str, nargs="+")
# parser.add_argument('extraArgs', nargs=argparse.REMAINDER)
2022-11-21 16:21:31 -06:00
parser.add_argument("otherthings", nargs="*")
# Optional arguments
2022-11-21 16:21:31 -06:00
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(
"--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")
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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"
)
2019-08-09 17:49:05 -05:00
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Global varaibles declaration
2019-08-09 17:49:05 -05:00
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Setting up print and logging system
2022-11-21 16:21:31 -06:00
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 = {}
2019-08-09 17:49:05 -05:00
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Main program starts here
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2019-08-09 17:49:05 -05:00
def main():
2022-11-21 16:21:31 -06:00
logger.debug("Script Launched in " + os.getcwd())
check_required_file()
read_script_config()
validate_command_line_arguments()
2019-08-09 17:49:05 -05:00
prepare_run_directory(args.run_dir)
2022-11-21 16:21:31 -06:00
if args.fpga_flow == "yosys_vpr":
2019-08-09 17:49:05 -05:00
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
2022-11-21 16:21:31 -06:00
shutil.copy(args.top_module + "_yosys_out.blif", args.top_module + ".blif")
# Always Generate the post-synthesis verilog files
run_rewrite_verilog()
2022-11-21 16:21:31 -06:00
if args.fpga_flow == "vpr_blif":
collect_files_for_vpr()
2022-11-21 16:21:31 -06:00
if args.fpga_flow == "yosys":
2022-01-06 03:44:11 -06:00
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()
2022-11-21 16:21:31 -06:00
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"]
)
2022-01-06 03:44:11 -06:00
else:
2022-11-21 16:21:31 -06:00
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)
2019-08-09 17:49:05 -05:00
exit()
2022-11-21 16:21:31 -06:00
2019-08-09 17:49:05 -05:00
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Subroutines starts here
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
def check_required_file():
""" Function ensure existace of all required files for the script """
files_dict = {
2022-11-21 16:21:31 -06:00
"CAD TOOL PATH": os.path.join(
flow_script_dir, os.pardir, "misc", "fpgaflow_default_tool_path.conf"
),
}
for filename, filepath in files_dict.items():
if not os.path.isfile(filepath):
clean_up_and_exit("Not able to locate default file " + filename)
def read_script_config():
""" This fucntion reads default CAD tools path from configuration file """
global config, cad_tools
config = ConfigParser(interpolation=ExtendedInterpolation())
config.read_dict(script_env_vars)
2022-11-21 16:21:31 -06:00
default_cad_tool_conf = os.path.join(
flow_script_dir, os.pardir, "misc", "fpgaflow_default_tool_path.conf"
)
config.read_file(open(default_cad_tool_conf))
2019-08-31 16:19:34 -05:00
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(
2022-11-21 16:21:31 -06:00
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():
2022-11-21 16:21:31 -06:00
"""
2021-02-03 11:34:34 -06:00
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
2022-11-21 16:21:31 -06:00
"""
2021-10-18 04:13:42 -05:00
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("|")]):
2022-11-21 16:21:31 -06:00
clean_up_and_exit(
"'%s' argument depends on (%s) arguments"
% (eacharg, ", ".join(dependent).replace("|", " or "))
)
2019-08-09 17:49:05 -05:00
2021-02-03 11:34:34 -06:00
# Check if architecrue files exists
2019-08-09 17:49:05 -05:00
args.arch_file = os.path.abspath(args.arch_file)
if not os.path.isfile(args.arch_file):
2022-11-21 16:21:31 -06:00
clean_up_and_exit("VPR architecture file not found. -%s" % args.arch_file)
2021-02-03 11:34:34 -06:00
args.openfpga_arch_file = os.path.abspath(args.openfpga_arch_file)
if not os.path.isfile(args.openfpga_arch_file):
2022-11-21 16:21:31 -06:00
clean_up_and_exit("OpenFPGA architecture file not found. -%s" % args.openfpga_arch_file)
2019-08-09 17:49:05 -05:00
# 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)
2022-11-21 16:21:31 -06:00
logger.warning(
"Directory is not support in benchmark list" + "use wildcard pattern to add files"
)
continue
2019-08-09 17:49:05 -05:00
for everyfile in glob.glob(args.benchmark_files[index]):
if not os.path.isfile(everyfile):
2022-11-21 16:21:31 -06:00
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):
2022-11-21 16:21:31 -06:00
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:
2022-11-21 16:21:31 -06:00
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)
2019-08-09 17:49:05 -05:00
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])
2021-02-03 11:34:34 -06:00
shutil.rmtree(run_dir)
2019-08-09 17:49:05 -05:00
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
2019-08-09 17:49:05 -05:00
os.mkdir("arch")
2022-11-21 16:21:31 -06:00
tmpl = Template(open(args.arch_file, encoding="utf-8").read())
2019-08-09 17:49:05 -05:00
arch_filename = os.path.basename(args.arch_file)
args.arch_file = os.path.join(run_dir, "arch", arch_filename)
2022-11-21 16:21:31 -06:00
with open(args.arch_file, "w", encoding="utf-8") as archfile:
archfile.write(tmpl.safe_substitute(script_env_vars["PATH"]))
2019-08-09 17:49:05 -05:00
2022-11-21 16:21:31 -06:00
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)
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
if args.openfpga_shell_template:
if not os.path.isfile(args.openfpga_shell_template or ""):
2022-11-21 16:21:31 -06:00
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:
2022-11-21 16:21:31 -06:00
shutil.copy(args.openfpga_shell_template, args.top_module + "_template.openfpga")
# Create benchmark dir in run_dir and copy flattern architecture file
2019-08-09 17:49:05 -05:00
os.mkdir("benchmark")
try:
for index, eachfile in enumerate(args.benchmark_files):
args.benchmark_files[index] = shutil.copy2(
2022-11-21 16:21:31 -06:00
eachfile, os.path.join(os.curdir, "benchmark")
)
2019-08-09 17:49:05 -05:00
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())
2019-08-09 17:49:05 -05:00
logger.error(msg)
logger.error("Exiting . . . . . .")
exit(1)
2019-08-09 17:49:05 -05:00
2022-11-21 16:21:31 -06:00
2021-10-29 08:34:27 -05:00
def create_yosys_params():
2019-08-09 17:49:05 -05:00
tree = ET.parse(args.arch_file)
root = tree.getroot()
try:
2022-11-21 16:21:31 -06:00
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"
]
)
2019-08-16 14:36:39 -05:00
logger.info("Extracted lut_size size from arch XML = %s", lut_size)
2019-08-09 17:49:05 -05:00
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
2019-08-09 17:49:05 -05:00
# Yosys script parameter mapping
ys_params = script_env_vars["PATH"]
2021-10-29 08:34:27 -05:00
for indx in range(0, len(OpenFPGAArgs), 2):
tmpVar = OpenFPGAArgs[indx][2:].upper()
2022-11-21 16:21:31 -06:00
ys_params[tmpVar] = OpenFPGAArgs[indx + 1]
2021-10-29 08:34:27 -05:00
if not args.verific:
2022-11-21 16:21:31 -06:00
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"] = ""
2021-10-29 08:34:27 -05:00
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 = {
2022-11-21 16:21:31 -06:00
".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"],
}
2021-10-29 08:34:27 -05:00
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"]:
2022-11-21 16:21:31 -06:00
ys_params["ADD_INCLUDE_DIR"] = "\n".join(
["verific -vlog-incdir " + shlex.quote(eachdir) for eachdir in include_dirs]
)
2021-10-29 08:34:27 -05:00
if "VERIFIC_LIBRARY_DIR" in ys_params:
2022-11-21 16:21:31 -06:00
ys_params["ADD_LIBRARY_DIR"] = "\n".join(
[
"verific -vlog-libdir " + shlex.quote(eachdir)
for eachdir in ys_params["VERIFIC_LIBRARY_DIR"].split(",")
]
)
2021-10-29 08:34:27 -05:00
try:
for param, value in ys_params.items():
if param.startswith("VERIFIC_READ_LIB_NAME"):
2022-11-21 16:21:31 -06:00
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])
2022-11-21 16:21:31 -06:00
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 = {}
2021-10-29 08:34:27 -05:00
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():
2022-11-21 16:21:31 -06:00
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",
]
)
2021-10-29 08:34:27 -05:00
except:
logger.exception("Failed to determine design file type")
clean_up_and_exit("")
if "YOSYS_CELL_SIM_VERILOG" in ys_params:
2022-11-21 16:21:31 -06:00
ys_params["READ_HDL_FILE"] += " ".join(
[
"verific",
ys_params["VERIFIC_VERILOG_STANDARD"],
ys_params["YOSYS_CELL_SIM_VERILOG"],
"\n",
]
)
2021-10-29 08:34:27 -05:00
if "YOSYS_CELL_SIM_SYSTEMVERILOG" in ys_params:
2022-11-21 16:21:31 -06:00
ys_params["READ_HDL_FILE"] += " ".join(
[
"verific",
ys_params["VERIFIC_SYSTEMVERILOG_STANDARD"],
ys_params["YOSYS_CELL_SIM_SYSTEMVERILOG"],
"\n",
]
)
2021-10-29 08:34:27 -05:00
if "YOSYS_CELL_SIM_VHDL" in ys_params:
2022-11-21 16:21:31 -06:00
ys_params["READ_HDL_FILE"] += " ".join(
[
"verific",
ys_params["VERIFIC_VHDL_STANDARD"],
ys_params["YOSYS_CELL_SIM_VHDL"],
"\n",
]
)
2021-10-29 08:34:27 -05:00
if "YOSYS_BLACKBOX_MODULES" in ys_params:
2022-11-21 16:21:31 -06:00
ys_params["ADD_BLACKBOX_MODULES"] = "blackbox " + " ".join(
["\\" + mod for mod in ys_params["YOSYS_BLACKBOX_MODULES"].split(",")]
)
2021-10-29 08:34:27 -05:00
ys_params["TOP_MODULE"] = args.top_module
ys_params["LUT_SIZE"] = lut_size
2022-11-21 16:21:31 -06:00
ys_params["OUTPUT_BLIF"] = args.top_module + "_yosys_out.blif"
ys_params["OUTPUT_VERILOG"] = args.top_module + "_output_verilog.v"
2021-10-29 08:34:27 -05:00
return ys_params
def run_yosys_with_abc():
"""
Execute yosys with ABC and optional blackbox support
"""
ys_params = create_yosys_params()
2022-11-21 16:21:31 -06:00
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))
2021-02-03 11:34:34 -06:00
2022-11-21 16:21:31 -06:00
run_command("Run yosys", "yosys_output.log", [cad_tools["yosys_path"], "yosys.ys"])
2019-08-09 17:49:05 -05:00
def run_odin2():
pass
def run_abc_vtr():
pass
def run_abc_for_standarad():
pass
def run_ace2():
if args.black_box_ace:
2022-11-21 16:21:31 -06:00
with open(args.top_module + "_yosys_out.blif", "r") as fp:
blif_lines = fp.readlines()
2022-11-21 16:21:31 -06:00
with open(args.top_module + "_bb.blif", "w") as fp:
for eachline in blif_lines:
if ".names" in eachline:
input_nets = eachline.split()[1:]
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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)])
2022-11-21 16:21:31 -06:00
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 = [
2022-11-21 16:21:31 -06:00
"-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:
2022-11-21 16:21:31 -06:00
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:
2022-11-21 16:21:31 -06:00
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)
2019-08-09 17:49:05 -05:00
def run_pro_blif_3arg():
command = [
2022-11-21 16:21:31 -06:00
"-i",
args.top_module + "_ace_out.blif",
"-o",
args.top_module + ".blif",
"-initial_blif",
args.top_module + "_yosys_out.blif",
]
try:
2022-11-21 16:21:31 -06:00
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:
2022-11-21 16:21:31 -06:00
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)
2019-08-09 17:49:05 -05:00
def collect_files_for_vpr():
2019-08-25 01:23:39 -05:00
# Sanitize provided Benchmark option
if len(args.benchmark_files) > 1:
2020-10-06 14:09:22 -05:00
logger.error("Expecting Single Benchmark Blif file.")
2019-08-25 01:23:39 -05:00
if not os.path.isfile(args.benchmark_files[0] or ""):
clean_up_and_exit("Provided Blif file not found")
2022-11-21 16:21:31 -06:00
shutil.copy(args.benchmark_files[0], args.top_module + ".blif")
2019-08-25 01:23:39 -05:00
# Sanitize provided Activity file option
if args.power:
2022-11-21 16:21:31 -06:00
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:
2022-11-21 16:21:31 -06:00
if os.path.isfile(args.activity_file):
shutil.copy(args.activity_file, args.top_module + "_ace_out.act")
2019-08-25 01:23:39 -05:00
# 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")
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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
2022-11-21 16:21:31 -06:00
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()
2022-11-21 16:21:31 -06:00
path_variables[tmpVar] = OpenFPGAArgs[indx + 1]
2022-11-21 16:21:31 -06:00
with open(args.top_module + "_run.openfpga", "w", encoding="utf-8") as archfile:
archfile.write(tmpl.safe_substitute(path_variables))
2022-11-21 16:21:31 -06:00
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"):
2022-11-21 16:21:31 -06:00
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(",")
match = re.search(reg_string[1:-1], vpr_log)
if match:
try:
if "lambda" in filt_function.strip():
2022-11-21 16:21:31 -06:00
eval("ParseFunction = " + filt_function.strip())
extract_val = ParseFunction(**match.groups())
elif filt_function.strip() == "int":
extract_val = int(match.group(1))
elif filt_function.strip() == "float":
extract_val = float(match.group(1))
elif filt_function.strip() == "str":
extract_val = str(match.group(1))
elif filt_function.strip() == "scientific":
try:
2022-11-21 16:21:31 -06:00
mult = {
"m": 1e-3,
"u": 1e-6,
"n": 1e-9,
"K": 1e-3,
"M": 1e-6,
"G": 1e-9,
}.get(match.group(2)[0], 1)
except:
mult = 1
2022-11-21 16:21:31 -06:00
extract_val = float(match.group(1)) * mult
else:
2019-08-19 20:06:46 -05:00
extract_val = match.group(1)
except:
logger.exception("Filter failed")
extract_val = "Filter Failed"
resultDict[name] = extract_val
dummyparser = ConfigParser()
2019-08-16 11:59:44 -05:00
dummyparser.read_dict({"RESULTS": resultDict})
2022-11-21 16:21:31 -06:00
with open(r_filename + ".result", "w") as configfile:
dummyparser.write(configfile)
2022-11-21 16:21:31 -06:00
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 = [
2022-11-21 16:21:31 -06:00
"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)]
2021-03-08 01:35:47 -06:00
run_command("Yosys", "yosys_rewrite.log", command)
else:
# Yosys script parameter mapping
2021-10-29 08:34:27 -05:00
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(";")):
2022-11-21 16:21:31 -06:00
tmpl = Template(open(curr_rewrite_tmpl, encoding="utf-8").read())
logger.info("Yosys rewrite iteration: " + str(iteration_idx))
2022-11-21 16:21:31 -06:00
with open("yosys_rewrite_" + str(iteration_idx) + ".ys", "w") as archfile:
archfile.write(tmpl.safe_substitute(ys_rewrite_params))
2022-11-21 16:21:31 -06:00
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()
2022-11-21 16:21:31 -06:00
compiled_file = "compiled_" + args.top_module
# include_netlists = args.top_module+"_include_netlists.v"
2022-11-21 16:21:31 -06:00
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]
2019-08-19 20:06:46 -05:00
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)
2022-11-21 16:21:31 -06:00
with open(logfile, "w") as output:
2019-08-23 00:41:25 -05:00
try:
2022-11-21 16:21:31 -06:00
output.write(" ".join(command) + "\n")
process = subprocess.run(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True
)
output.write(process.stdout)
2021-02-03 11:34:34 -06:00
output.write(process.stderr)
output.write(str(process.returncode))
if "openfpgashell" in logfile:
filter_openfpga_output(process.stdout)
if process.returncode:
2022-11-21 16:21:31 -06:00
logger.error("%s run failed with returncode %d" % (taskname, process.returncode))
2021-02-03 11:34:34 -06:00
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)
2019-08-23 00:41:25 -05:00
if exit_if_fail:
clean_up_and_exit("Failed to run %s task" % taskname)
logger.info("%s is written in file %s" % (taskname, logfile))
2019-08-19 20:06:46 -05:00
return process.stdout
2021-02-03 11:34:34 -06:00
def filter_openfpga_output(vpr_output):
stdout = iter(vpr_output.split("\n"))
try:
for i in range(50):
if "Version:" in next(stdout):
2022-11-21 16:21:31 -06:00
logger.info("OpenFPGAShell %s %s" % (next(stdout), next(stdout)))
2021-02-03 11:34:34 -06:00
break
except StopIteration:
pass
2019-08-23 00:41:25 -05:00
def filter_failed_process_output(vpr_output):
2019-08-16 14:36:39 -05:00
for line in vpr_output.split("\n"):
elements_to_log = ["error", "what()"]
if any(match in line.lower() for match in elements_to_log):
2019-08-16 14:36:39 -05:00
logger.error("-->>" + line)
2019-08-09 17:49:05 -05:00
if __name__ == "__main__":
ExecTime["Start"] = time.time()
# args = parser.parse_args()
args, OpenFPGAArgs = parser.parse_known_args()
2019-08-09 17:49:05 -05:00
main()