diff --git a/.gitignore b/.gitignore index fc6dbf3a5..dade84a47 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,5 @@ fpga_flow/csv_rpts tmp/ build/ -message.txt \ No newline at end of file +message.txt +deploy_key \ No newline at end of file diff --git a/.travis/after_failure.sh b/.travis/after_failure.sh index 95f8bd80c..fbbbda957 100755 --- a/.travis/after_failure.sh +++ b/.travis/after_failure.sh @@ -9,4 +9,7 @@ travis_fold end after_failure.1 start_section "failure.tail" "${RED}Failure output...${NC}" tail -n 1000 output.log +echo "Failed uploading files to LNIS Server" +scp -qCr $TRAVIS_BUILD_DIR/openfpga_flow/tasks/ u1249762@lab1-1.eng.utah.edu:/var/tmp/travis_bc/$TRAVIS_JOB_ID/ +scp output.log u1249762@lab1-1.eng.utah.edu:/var/tmp/travis_bc/$TRAVIS_JOB_ID/ end_section "failure.tail" diff --git a/deploy_key.enc b/deploy_key.enc new file mode 100644 index 000000000..df15a29c9 Binary files /dev/null and b/deploy_key.enc differ diff --git a/openfpga.sh b/openfpga.sh new file mode 100755 index 000000000..e44b22f82 --- /dev/null +++ b/openfpga.sh @@ -0,0 +1,74 @@ +#!/bin/bash +#title : openfpga.sh +#description : This script provides shortcut commands +# for several simple operations in OpenFPGA project +#author : Ganesh Gore +#============================================================================== + +export OPENFPGA_PATH="$(pwd)" +export OPENFPGA_SCRIPT_PATH="$(pwd)/openfpga_flow/scripts" +export OPENFPGA_TASK_PATH="$(pwd)/openfpga_flow/tasks" +if [ -z $PYTHON_EXEC ]; then export PYTHON_EXEC="python3"; fi + +# This function checks the path and +# raises warning if the command is not executing +# inside current OpendFPGA folder +check_execution_path (){ + if [[ $1 != *"${OPENFPGA_PATH}"* ]]; then + echo -e "\e[33mCommand is not executed from configured OPNEFPGA directory\e[0m" + fi +} + +run-task () { + $PYTHON_EXEC $OPENFPGA_SCRIPT_PATH/run_fpga_task.py "$@" +} + +run-flow () { + $PYTHON_EXEC $OPENFPGA_SCRIPT_PATH/run_fpga_flow.py "$@" +} + +# lists all the configure task in task directory +list-tasks () { + check_execution_path "$(pwd)" + ls -tdalh ${OPENFPGA_TASK_PATH}/* | awk '{printf("%-4s | %s %-3s | ", $5, $6, $7) ;system("basename " $9)}' +} + +# Switch directory to root of OpenFPGA +goto-root () { + cd $OPENFPGA_PATH +} + +# Changes directory to task directory [goto_task ] +goto-task () { + if [ -z $1 ]; then + echo "requires task name goto_task " + return + fi + goto_path=$OPENFPGA_TASK_PATH/$1 + run_num="" + if [ ! -d $goto_path ]; then echo "Task directory not found"; return; fi + if [[ "$2" =~ '^[0-9]+$' ]] ; then + if ! [[ $2 == '0' ]] ; then run_num="$(printf run%03d $2)"; else run_num="latest"; fi + if [ ! -d "$goto_path/$run_num" ]; then run_num="latest"; fi + fi + if [ ! -d $goto_path/$run_num ]; then + echo "\e[33mTask run directory not found -" $goto_path/$run_num "\e[0m" + else + echo "Switching current dirctory to" $goto_path/$run_num + cd $goto_path/$run_num + fi +} + +# Clears enviroment variables and fucntions +unset-openfpga (){ + unset -v OPENFPGA_PATH + unset -f list-tasks run-task run-flow goto-task goto-root >/dev/null 2>&1 +} + +# Allow autocompletion of task +if [[ $(ps -p $$ -oargs=) == *"zsh"* ]]; then + autoload -U +X bashcompinit; bashcompinit; +fi +TaskList=$(ls -tdalh ${OPENFPGA_TASK_PATH}/* | awk '{system("basename " $9)}' | awk '{printf("%s ",$1)}') +complete -W "${TaskList}" goto-task +complete -W "${TaskList}" run-task \ No newline at end of file diff --git a/openfpga_flow/misc/modelsim_proc.tcl b/openfpga_flow/misc/modelsim_proc.tcl index cede56af4..69972b764 100644 --- a/openfpga_flow/misc/modelsim_proc.tcl +++ b/openfpga_flow/misc/modelsim_proc.tcl @@ -47,7 +47,7 @@ proc top_create_new_project {projectname verilog_files modelsim_path simtime uni #Start the simulation vsim $projectname.$top_tb -voptargs=+acc #Add the waves - add_waves top_tb + add_waves $top_tb #run the simulation runsim $simtime $unit #Fit the window view diff --git a/openfpga_flow/scripts/run_modelsim.py b/openfpga_flow/scripts/run_modelsim.py index 0ddbe118c..173ff8494 100644 --- a/openfpga_flow/scripts/run_modelsim.py +++ b/openfpga_flow/scripts/run_modelsim.py @@ -1,16 +1,20 @@ from string import Template import sys import os -import pprint +import re +import glob +import time +import threading +from datetime import timedelta import argparse import subprocess import logging -from pprint import pprint -from configparser import ConfigParser +from configparser import ConfigParser, ExtendedInterpolation # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = # Configure logging system # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +FILE_LOG_FORMAT = '%(levelname)s (%(threadName)10s) - %(message)s' logging.basicConfig(level=logging.INFO, stream=sys.stdout, format='%(levelname)s (%(threadName)10s) - %(message)s') logger = logging.getLogger('Modelsim_run_log') @@ -20,7 +24,13 @@ logger = logging.getLogger('Modelsim_run_log') # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = parser = argparse.ArgumentParser() parser.add_argument('files', nargs='+', - help="Pass SimulationDeckInfo generated by OpenFPGA flow") + help="Pass SimulationDeckInfo generated by OpenFPGA flow" + + " or pass taskname ") +parser.add_argument('--maxthreads', type=int, default=2, + help="Number of fpga_flow threads to run default = 2," + + "Typically <= Number of processors on the system") +parser.add_argument('--debug', action="store_true", + help="Run script in debug mode") parser.add_argument('--modelsim_proc_tmpl', type=str, help="Modelsim proc template file") parser.add_argument('--modelsim_runsim_tmpl', type=str, @@ -36,9 +46,32 @@ parser.add_argument('--modelsim_ini', type=str, help="Skip any confirmation") parser.add_argument('--skip_prompt', action='store_true', help='Skip any confirmation') +parser.add_argument('--ini_filename', type=str, + default="simulation_deck_info.ini", + help='default INI filename in in fun dir') args = parser.parse_args() -# Consider default formality script template +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Read script configuration file +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +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"), + "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))}}) +config = ConfigParser(interpolation=ExtendedInterpolation()) +config.read_dict(script_env_vars) +config.read_file(open(os.path.join(task_script_dir, 'run_fpga_task.conf'))) +gc = config["GENERAL CONFIGURATION"] + +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Load default templates for modelsim +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = task_script_dir = os.path.dirname(os.path.abspath(__file__)) if not args.modelsim_proc_tmpl: args.modelsim_proc_tmpl = os.path.join(task_script_dir, os.pardir, @@ -52,7 +85,60 @@ args.modelsim_runsim_tmpl = os.path.abspath(args.modelsim_runsim_tmpl) def main(): - for eachFile in args.files: + if os.path.isfile(args.files[0]): + create_tcl_script(args.files) + else: + # Check if task directory exists and consistent + taskname = args.files[0] + task_run = "latest" + if len(args.files) > 1: + task_run = f"run{int(args.files[1]):03}" + + temp_dir = os.path.join(gc["task_dir"], taskname) + if not os.path.isdir(temp_dir): + clean_up_and_exit("Task directory [%s] not found" % temp_dir) + temp_dir = os.path.join(gc["task_dir"], taskname, task_run) + if not os.path.isdir(temp_dir): + clean_up_and_exit("Task run directory [%s] not found" % temp_dir) + + # = = = = = = = Create a current script log file handler = = = = + logfile_path = os.path.join(gc["task_dir"], + taskname, task_run, "modelsim_run.log") + logfilefh = logging.FileHandler(logfile_path, "w") + logfilefh.setFormatter(logging.Formatter(FILE_LOG_FORMAT)) + logger.addHandler(logfilefh) + logger.info("Created log file at %s" % logfile_path) + # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + + # = = = = Read Task log file and extract run directory = = = + logfile = os.path.join(gc["task_dir"], taskname, task_run, "*_out.log") + logfiles = glob.glob(logfile) + if not len(logfiles): + clean_up_and_exit("No successful run found in [%s]" % temp_dir) + + task_ini_files = [] + for eachfile in logfiles: + with open(eachfile) as fp: + run_dir = [re.findall(r'^INFO.*Run directory : (.*)$', line) + for line in open(eachfile)] + run_dir = filter(bool, run_dir) + for each_run in run_dir: + INIfile = os.path.join(each_run[0], args.ini_filename) + if os.path.isfile(INIfile): + task_ini_files.append(INIfile) + logger.info(f"Found {len(task_ini_files)} INI files") + create_tcl_script(task_ini_files) + + +def clean_up_and_exit(msg): + logger.error(msg) + logger.error("Exiting . . . . . .") + exit(1) + + +def create_tcl_script(files): + runsim_files = [] + for eachFile in files: eachFile = os.path.abspath(eachFile) pDir = os.path.dirname(eachFile) os.chdir(pDir) @@ -107,42 +193,78 @@ def main(): with open(proc_filename, 'w', encoding='utf-8') as tclout: tclout.write(open(args.modelsim_proc_tmpl, encoding='utf-8').read()) - - # Execute modelsim - if args.run_sim: - os.chdir(args.modelsim_run_dir) - print(args.modelsim_run_dir) - modelsim_run_cmd = ["vsim", "-c", "-do", runsim_filename] - run_command("ModelSim Run", "modelsim_run.log", - modelsim_run_cmd) - else: - logger.info("Created runsim and proc files") - logger.info(f"runsim_filename {runsim_filename}") - logger.info(f"proc_filename {proc_filename}") + runsim_files.append({ + "modelsim_run_dir": args.modelsim_run_dir, + "runsim_filename": runsim_filename, + "status" :False, + "finished" : True + }) + # Execute modelsim + if args.run_sim: + thread_sema = threading.Semaphore(args.maxthreads) + logger.info("Launching %d parallel threads" % args.maxthreads) + thread_list = [] + for thread_no, eachjob in enumerate(runsim_files): + t = threading.Thread(target=run_modelsim_thread, + name=f"Thread_{thread_no:d}", + args=(thread_sema, eachjob, runsim_files)) + t.start() + thread_list.append(t) + for eachthread in thread_list: + eachthread.join() + exit() + else: + logger.info("Created runsim and proc files") + logger.info(f"runsim_filename {runsim_filename}") + logger.info(f"proc_filename {proc_filename}") + from pprint import pprint + pprint(runsim_files) -def run_command(taskname, logfile, command, exit_if_fail=True): - # os.chdir(os.pardir) - logger.info("Launching %s " % taskname) - with open(logfile, 'w+') as output: +def run_modelsim_thread(s, eachJob, job_list): + os.chdir(eachJob["modelsim_run_dir"]) + with s: + thread_name = threading.currentThread().getName() + eachJob["starttime"] = time.time() try: - output.write(os.getcwd() + "\n") - output.write(" ".join(command)+"\n") - process = subprocess.run(command, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - output.write(process.stdout) - if process.returncode: - logger.error("%s run failed with returncode %d" % - (taskname, process.returncode)) - except (Exception, subprocess.CalledProcessError) as e: - logger.exception("failed to execute %s" % taskname) - return None - logger.info("%s is written in file %s" % (taskname, logfile)) - return process.stdout - + logfile = "%s_modelsim.log" % thread_name + with open(logfile, 'w+') as output: + output.write("* "*20 + '\n') + output.write("RunDirectory : %s\n" % os.getcwd()) + command = ["vsim", "-c", "-do", eachJob["runsim_filename"]] + output.write(" ".join(command) + '\n') + output.write("* "*20 + '\n') + logger.info("Running modelsim with [%s]" % " ".join(command)) + process = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + for line in process.stdout: + if "Errors" in line: + logger.info(line.strip()) + sys.stdout.buffer.flush() + output.write(line) + process.wait() + if process.returncode: + raise subprocess.CalledProcessError(0, " ".join(command)) + eachJob["status"] = True + except: + logger.exception("Failed to execute openfpga flow - " + + eachJob["name"]) + if not args.continue_on_fail: + os._exit(1) + eachJob["endtime"] = time.time() + timediff = timedelta(seconds=(eachJob["endtime"]-eachJob["starttime"])) + timestr = humanize.naturaldelta(timediff) if "humanize" in sys.modules \ + else str(timediff) + logger.info("%s Finished with returncode %d, Time Taken %s " % + (thread_name, process.returncode, timestr)) + eachJob["finished"] = True + no_of_finished_job = sum([not eachJ["finished"] for eachJ in job_list]) + logger.info("***** %d runs pending *****" % (no_of_finished_job)) if __name__ == "__main__": + if args.debug: + logger.info("Setting loggger in debug mode") + logger.setLevel(logging.DEBUG) main() diff --git a/run_test.sh b/run_test.sh index 027a1bea4..b234523eb 100644 --- a/run_test.sh +++ b/run_test.sh @@ -18,14 +18,13 @@ # Test popular multi-mode architecture python3 openfpga_flow/scripts/run_fpga_flow.py \ ./openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml \ -./openfpga_flow/benchmarks/Test_Modes/test_modes.blif \ +./openfpga_flow/benchmarks/test_modes/test_modes.blif \ --fpga_flow vpr_blif \ --top_module test_modes \ ---activity_file ./openfpga_flow/benchmarks/Test_Modes/test_modes.act \ ---base_verilog ./openfpga_flow/benchmarks/Test_Modes/test_modes.v \ +--activity_file ./openfpga_flow/benchmarks/test_modes/test_modes.act \ +--base_verilog ./openfpga_flow/benchmarks/test_modes/test_modes.v \ --power \ --power_tech ./openfpga_flow/tech/PTM_45nm/45nm.xml \ -#--fix_route_chan_width 300 \ --min_route_chan_width 1.3 \ --vpr_fpga_verilog \ --vpr_fpga_verilog_dir . \ @@ -42,64 +41,65 @@ python3 openfpga_flow/scripts/run_fpga_flow.py \ --vpr_fpga_verilog_print_sdc_pnr \ --vpr_fpga_verilog_print_sdc_analysis \ --vpr_fpga_x2p_compact_routing_hierarchy \ ---end_flow_with_test +--end_flow_with_test \ +--vpr_fpga_verilog_print_modelsim_autodeck /uusoc/facility/cad_tools/Mentor/modelsim10.7b/modeltech/modelsim.ini -# Test Standard cell MUX2 -python3 openfpga_flow/scripts/run_fpga_flow.py \ -./openfpga_flow/arch/template/k8_N10_sram_chain_FC_template.xml \ -./openfpga_flow/benchmarks/Test_Modes/test_modes.blif \ ---fpga_flow vpr_blif \ ---top_module test_modes \ ---activity_file ./openfpga_flow/benchmarks/Test_Modes/test_modes.act \ ---base_verilog ./openfpga_flow/benchmarks/Test_Modes/test_modes.v \ ---power \ ---power_tech ./openfpga_flow/tech/PTM_45nm/45nm.xml \ -#--fix_route_chan_width 300 \ ---min_route_chan_width 1.3 \ ---vpr_fpga_verilog \ ---vpr_fpga_verilog_dir . \ ---vpr_fpga_x2p_rename_illegal_port \ ---vpr_fpga_verilog_include_icarus_simulator \ ---vpr_fpga_verilog_formal_verification_top_netlist \ ---vpr_fpga_verilog_include_timing \ ---vpr_fpga_verilog_include_signal_init \ ---vpr_fpga_verilog_print_autocheck_top_testbench \ ---debug \ ---vpr_fpga_bitstream_generator \ ---vpr_fpga_verilog_print_user_defined_template \ ---vpr_fpga_verilog_print_report_timing_tcl \ ---vpr_fpga_verilog_print_sdc_pnr \ ---vpr_fpga_verilog_print_sdc_analysis \ ---vpr_fpga_x2p_compact_routing_hierarchy \ ---end_flow_with_test +# # Test Standard cell MUX2 +# python3 openfpga_flow/scripts/run_fpga_flow.py \ +# ./openfpga_flow/arch/template/k8_N10_sram_chain_FC_template.xml \ +# ./openfpga_flow/benchmarks/Test_Modes/test_modes.blif \ +# --fpga_flow vpr_blif \ +# --top_module test_modes \ +# --activity_file ./openfpga_flow/benchmarks/Test_Modes/test_modes.act \ +# --base_verilog ./openfpga_flow/benchmarks/Test_Modes/test_modes.v \ +# --power \ +# --power_tech ./openfpga_flow/tech/PTM_45nm/45nm.xml \ +# #--fix_route_chan_width 300 \ +# --min_route_chan_width 1.3 \ +# --vpr_fpga_verilog \ +# --vpr_fpga_verilog_dir . \ +# --vpr_fpga_x2p_rename_illegal_port \ +# --vpr_fpga_verilog_include_icarus_simulator \ +# --vpr_fpga_verilog_formal_verification_top_netlist \ +# --vpr_fpga_verilog_include_timing \ +# --vpr_fpga_verilog_include_signal_init \ +# --vpr_fpga_verilog_print_autocheck_top_testbench \ +# --debug \ +# --vpr_fpga_bitstream_generator \ +# --vpr_fpga_verilog_print_user_defined_template \ +# --vpr_fpga_verilog_print_report_timing_tcl \ +# --vpr_fpga_verilog_print_sdc_pnr \ +# --vpr_fpga_verilog_print_sdc_analysis \ +# --vpr_fpga_x2p_compact_routing_hierarchy \ +# --end_flow_with_test -# Test local encoder feature -python3 openfpga_flow/scripts/run_fpga_flow.py \ -./openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml \ -./openfpga_flow/benchmarks/Test_Modes/test_modes.blif \ ---fpga_flow vpr_blif \ ---top_module test_modes \ ---activity_file ./openfpga_flow/benchmarks/Test_Modes/test_modes.act \ ---base_verilog ./openfpga_flow/benchmarks/Test_Modes/test_modes.v \ ---power \ ---power_tech ./openfpga_flow/tech/PTM_45nm/45nm.xml \ ---fix_route_chan_width 300 \ ---vpr_fpga_verilog \ ---vpr_fpga_verilog_dir . \ ---vpr_fpga_x2p_rename_illegal_port \ ---vpr_fpga_verilog_include_icarus_simulator \ ---vpr_fpga_verilog_formal_verification_top_netlist \ ---vpr_fpga_verilog_include_timing \ ---vpr_fpga_verilog_include_signal_init \ ---vpr_fpga_verilog_print_autocheck_top_testbench \ ---debug \ ---vpr_fpga_bitstream_generator \ ---vpr_fpga_verilog_print_user_defined_template \ ---vpr_fpga_verilog_print_report_timing_tcl \ ---vpr_fpga_verilog_print_sdc_pnr \ ---vpr_fpga_verilog_print_sdc_analysis \ ---vpr_fpga_x2p_compact_routing_hierarchy \ ---end_flow_with_test +# # Test local encoder feature +# python3 openfpga_flow/scripts/run_fpga_flow.py \ +# ./openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml \ +# ./openfpga_flow/benchmarks/Test_Modes/test_modes.blif \ +# --fpga_flow vpr_blif \ +# --top_module test_modes \ +# --activity_file ./openfpga_flow/benchmarks/Test_Modes/test_modes.act \ +# --base_verilog ./openfpga_flow/benchmarks/Test_Modes/test_modes.v \ +# --power \ +# --power_tech ./openfpga_flow/tech/PTM_45nm/45nm.xml \ +# --fix_route_chan_width 300 \ +# --vpr_fpga_verilog \ +# --vpr_fpga_verilog_dir . \ +# --vpr_fpga_x2p_rename_illegal_port \ +# --vpr_fpga_verilog_include_icarus_simulator \ +# --vpr_fpga_verilog_formal_verification_top_netlist \ +# --vpr_fpga_verilog_include_timing \ +# --vpr_fpga_verilog_include_signal_init \ +# --vpr_fpga_verilog_print_autocheck_top_testbench \ +# --debug \ +# --vpr_fpga_bitstream_generator \ +# --vpr_fpga_verilog_print_user_defined_template \ +# --vpr_fpga_verilog_print_report_timing_tcl \ +# --vpr_fpga_verilog_print_sdc_pnr \ +# --vpr_fpga_verilog_print_sdc_analysis \ +# --vpr_fpga_x2p_compact_routing_hierarchy \ +# --end_flow_with_test # Test tileable routing feature #python3 openfpga_flow/scripts/run_fpga_flow.py \