OpenFPGA/openfpga_flow/scripts/run_fpga_flow.py

918 lines
41 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
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
2019-08-09 17:49:05 -05: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__))
2019-08-31 16:19:34 -05: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"),
2019-08-31 16:19:34 -05:00
"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
def formatter(prog): return argparse.HelpFormatter(prog, max_help_position=60)
parser = argparse.ArgumentParser(formatter_class=formatter)
# Mandatory arguments
2019-08-09 17:49:05 -05:00
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")
2019-08-09 17:49:05 -05:00
parser.add_argument('--fpga_flow', type=str, default="yosys_vpr")
2019-08-31 16:19:34 -05:00
parser.add_argument('--flow_config', type=str,
help="CAD tools path overrides default setting")
parser.add_argument('--run_dir', type=str,
2019-08-09 17:49:05 -05:00
default=os.path.join(openfpga_base_dir, 'tmp'),
help="Directory to store intermidiate file & final results")
parser.add_argument('--openfpga_shell_template', type=str,
2021-02-03 11:34:34 -06:00
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")
2021-10-29 08:34:27 -05:00
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
2019-08-16 14:36:39 -05: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
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')
2019-12-30 13:16:04 -06:00
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")
2019-12-30 13:16:04 -06:00
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")
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
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():
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)
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()
2022-01-06 03:44:11 -06:00
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 "
2022-01-06 03:44:11 -06:00
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)
2019-08-09 17:49:05 -05:00
exit()
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Subroutines starts here
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
def check_required_file():
""" Function ensure existace of all required files for the script """
files_dict = {
"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)
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(
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():
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
'''
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("|")]):
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):
2021-02-03 11:34:34 -06:00
clean_up_and_exit(
"VPR architecture file not found. -%s"%
2021-02-03 11:34:34 -06:00
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"%
2021-02-03 11:34:34 -06:00
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)
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):
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)
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")
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)
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
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
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(
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())
2019-08-09 17:49:05 -05:00
logger.error(msg)
logger.error("Exiting . . . . . .")
exit(1)
2019-08-09 17:49:05 -05: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:
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()
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"] = ""
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 = {
".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 = {}
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():
2021-10-29 08:34:27 -05: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:
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"
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()
yosys_template = args.yosys_tmpl if args.yosys_tmpl else os.path.join(
2019-08-09 17:49:05 -05:00
cad_tools["misc_dir"], "ys_tmpl_yosys_vpr_flow.ys")
tmpl = Template(open(yosys_template, encoding='utf-8').read())
2019-08-09 17:49:05 -05:00
with open("yosys.ys", 'w') as archfile:
archfile.write(tmpl.safe_substitute(ys_params))
2021-02-03 11:34:34 -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:
2019-08-19 20:06:46 -05:00
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 = [
2019-08-19 20:06:46 -05: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:
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)
2019-08-09 17:49:05 -05:00
def run_pro_blif_3arg():
command = [
"-i", args.top_module+"_ace_out.blif",
2019-08-19 20:06:46 -05:00
"-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)
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")
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:
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")
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")
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))
2021-02-03 11:34:34 -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("vpr_stdout.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(",")
match = re.search(reg_string[1:-1], vpr_log)
if match:
try:
if "lambda" in filt_function.strip():
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:
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
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})
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)]
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(";")):
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))
2021-10-29 08:34:27 -05:00
run_command("Run yosys", "yosys_rewrite_output_" + str(iteration_idx) + ".log",
[cad_tools["yosys_path"], "yosys_rewrite_" + str(iteration_idx) + ".ys"])
2021-03-08 01:35:47 -06:00
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]
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)
2021-02-03 11:34:34 -06:00
with open(logfile, 'w') as output:
2019-08-23 00:41:25 -05:00
try:
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:
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):
logger.info("OpenFPGAShell %s %s" %
(next(stdout), next(stdout)))
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()