diff --git a/run_quick_test.pl b/run_quick_test.pl deleted file mode 100755 index d1c1e8c64c7..00000000000 --- a/run_quick_test.pl +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env perl -#------------------------------------------------------------------------# -# Run an experiment on the batch system # -# - runs an experiment script # -# creates seperate directory for each circuit # -# - uses a configuration file for experiment setup # -# # -# Usage: # -# # -# run_experiment.pl [local|condor] # -# # -# local|condor - an optional string that is either local to run # -# locally or condor to run on condor batch system, # -# defaults to condor # -# Options: -machines [fast|slow|both] # -#------------------------------------------------------------------------# - -use strict; -use Cwd; - -system("./run_reg_test.py vtr_reg_basic"); - diff --git a/run_reg_test.pl b/run_reg_test.pl deleted file mode 100755 index f04a22ee224..00000000000 --- a/run_reg_test.pl +++ /dev/null @@ -1,464 +0,0 @@ -#!/usr/bin/env perl -#------------------------------------------------------------------------# -# Run a regression test on the batch system # -# - runs the regression tests given as input parameters # -# - parses and checks results upon completion # -# - tests can be found in /tasks/regression_tests/ # -# # -# Usage: # -# # -# run_reg_test.pl ... # -# # -# Options: # -# -create_golden: Will create/overwrite the golden results with # -# those of the most recent execution # -# quick_test: Will run quick test in top-level directory before # -# running specified regression tests. # -# -display_qor: Will display quality of results of most recent # -# build of specified regression test. # -# # -# Notes: argument is of the format: _reg_ # -# See /tasks/regression_tests for more information. # -# # -# Currently available: # -# - vtr_reg_basic # -# - vtr_reg_strong # -# - vtr_reg_nightly # -# - vtr_reg_weekly # -# # -#------------------------------------------------------------------------# - -use strict; -use Cwd; -use File::Spec; -use List::Util; -use Scalar::Util; - -# Function Prototypes -sub setup_single_test; -sub check_override; -sub run_single_test; -sub parse_single_test; -sub run_quick_test; -sub run_odin_test; - -# Get absolute path of 'vtr_flow' -my $vtr_flow_path = getcwd; -$vtr_flow_path = "$vtr_flow_path/vtr_flow"; - -# Get absolute path of 'regression_tests' -my $reg_test_path = "$vtr_flow_path/tasks/regression_tests"; - -my @tests; -my $token; -my $test_dir; -my $test_name; -my $run_params = ""; -my $parse_params = ""; -# Override variables -my $first = 1; -my $parse_only = 0; -my $create_golden = 0; -my $check_golden = 0; -my $calc_geomean = 0; -my $display_qor = 0; -my $skip_qor = 0; -my $show_failures = 0; -my $num_cpu = 1; -my $script = "run_vtr_flow.py"; -my $long_task_names = 0; - -# Parse Input Arguments -while ( $token = shift(@ARGV) ) { - if ( $token eq "-parse") { - $parse_only = 1; - } elsif ( $token eq "-create_golden" ) { - $create_golden = 1; - } elsif ( $token eq "-check_golden" ) { - $check_golden = 1; - } elsif ( $token eq "-calc_geomean" ) { - $calc_geomean = 1; - } elsif ( $token eq "-display_qor" ) { - $display_qor = 1; - } elsif ( $token eq "-skip_qor" ) { - $skip_qor = 1; - } elsif ( $token eq "-show_failures" ) { - $show_failures = 1; - } elsif ( $token eq "-long_task_names" ) { - $long_task_names = 1; - } elsif ( $token eq "-j" ) { #-j N - $num_cpu = int(shift(@ARGV)); - } elsif ( $token eq "-script" ) { - $script = shift(@ARGV); - } elsif ( $token =~ /^-j(\d+)$/ ) { #-jN - $num_cpu = int($1); - } elsif ($token eq "quick_test") { - run_quick_test(); - } else { - if ($token =~ /(.*)\//) { - $token = $1; - } - push( @tests, $token ); - } -} - -# Remove duplicate tests -@tests = uniq(@tests); - -if ( $#tests == -1) { - die "\n" - . "Incorrect usage. You must specify at least one test to execute.\n" - . "\n" - . "USAGE:\n" - . "run_reg_test.pl ... \n" - . "\n" - . "OPTIONS:\n" - . " -create_golden: Will create/overwrite the golden results with\n" - . " those of the most recent execution\n" - . " -quick_test: Will run quick test in top-level directory before\n" - . " running specified regression tests.\n" - . " -display_qor: Will display quality of results of most recent build \n" - . " of specified regression test.\n" - . "\n" - . "Notes: argument is of the format: _reg_\n" - . "See /tasks/regression_tests for more information.\n" - . "\n" - . "Currently available:\n" - . " - vtr_reg_basic\n" - . " - vtr_reg_strong\n" - . " - vtr_reg_nightly\n" - . " - vtr_reg_weekly\n" - . "\n" - . "If you wish to add your own test, place it in - . /tasks/regression_tests\n" - . "\n"; -} - - -############################################################## -# Run regression tests -############################################################## -my $num_failed_tests = 0; - -if ( $#tests > -1 ) { - - foreach my $test (@tests) { - chomp($test); - my $run_failures = 0; - if($test =~ /^odin_reg/) { - $run_failures = run_odin_test($test); - } else { - #Standard VTR task-based test - - # Set up test - setup_single_test($test); - # Check for user overrides - check_override(); - # Run regression test - $run_failures = run_single_test($test); - $first = 0; - # Parse regression test - parse_single_test(" "); - # Create/Check golden results - if ($create_golden) { - parse_single_test("create", "calculate"); - } elsif ($skip_qor) { - print "\nSkipping QoR checks...\n"; - } else { - my $qor_test_failures = parse_single_test("check", "calculate"); - print "\nTest '$test' had $qor_test_failures qor test failures\n"; - $num_failed_tests += $qor_test_failures; - } - } - - print "\nTest '$test' had $run_failures run failures\n"; - $num_failed_tests += $run_failures; - } - print "\nTest complete\n\n"; -} - -if($num_failed_tests != 0) { - print "Error: $num_failed_tests tests failed!\n" -} else { - print "All tests passed\n" -} - -exit $num_failed_tests; - - -############################################################## -# Subroutines -############################################################## - -sub setup_single_test { - $test_name = shift; - $test_dir = "$reg_test_path/$test_name"; - - # Task list exists. Execute script with list instead. - if ( -e "$test_dir/task_list.txt" ) { - $run_params = "-l $test_dir/task_list.txt"; - $parse_params = "-l $test_dir/task_list.txt "; - } - else { - $run_params = "$test_dir"; - $parse_params = "$test_dir "; - } - - #Enable parallel execution - if ($num_cpu > 1) { - $run_params = "-j " . $num_cpu . " " . $run_params; - } - - if (!$long_task_names) { - $run_params = "-short_task_names " . $run_params; - } - - if ($show_failures) { - $run_params .= " -show_failures"; - } -} - -sub check_override { - - if ($parse_only) { - parse_single_test; - exit "Parsed results"; - } - - if ($check_golden) { - parse_single_test("check"); - exit "Checked results."; - } - - if ($calc_geomean) { - parse_single_test("calculate"); - exit "Calculated results."; - } - - if ($display_qor) { - if (!-e "$test_dir/qor_geomean.txt") { - die "QoR results do not exist ($test_dir/qor_geomean.txt)"; - } - elsif (!-r "$test_dir/qor_geomean.txt") { - die "[ERROR] Failed to open $test_dir/qor_geomean.txt: $!"; - } - else { - print "=" x 121 . "\n"; - print "\t" x 6 . "$test_name QoR Results \n"; - print "=" x 121 . "\n"; - - my @data = ( - "revision" , - "date" , - "total_runtime" , - "total_wirelength" , - "num_clb" , - "min_chan_width" , - "crit_path_delay" - ); - - my %units = ( - "revision" , "" , - "date" , "" , - "total_runtime" , " s" , - "total_wirelength" , " units" , - "num_clb" , " blocks" , - "min_chan_width" , " tracks" , - "crit_path_delay" , " ns" - ); - - my %precision = ( - "revision" , "%s" , - "date" , "%s" , - "total_runtime" , "%.3f", - "total_wirelength" , "%.0f", - "num_clb" , "%.2f", - "min_chan_width" , "%.2f", - "crit_path_delay" , "%.3f" - ); - - open( QOR_FILE, "$test_dir/qor_geomean.txt" ); - my $output = ; - my @first_line = split( /\t/, trim($output) ); - my @backwards = reverse ; - -format STDOUT_TOP = -| @||||||| | @||||||||| | @|||||||||||||| | @||||||||||||||||||| | @|||||||||||| | @||||||||||||||| | @|||||||||||||||| | -@data; -------------------------------------------------------------------------------------------------------------------------- -. -write; - - while( @backwards ) { - my @last_line = split( /\t/, trim( shift(@backwards) ) ); - my @new_last_line; - - foreach my $param (@data) { - - # Get column (index) of each qor metric - my $index = List::Util::first { @first_line[$_] eq $param } 0 .. $#first_line; - - # If index is out-of-bounds or metric cannot be found - if ( $index > @last_line or $index eq "" ) { - push( @new_last_line, sprintf( $precision{$param}, "-" ) . $units{$param} ); - } - # If valid number, add it onto line to be printed with appropriate sig figs. and units - elsif ( Scalar::Util::looks_like_number(@last_line[$index]) ) { - push( @new_last_line, sprintf( $precision{$param}, @last_line[$index] ) . $units{$param} ); - } - # For dates of format dd/mm/yy - elsif ( @last_line[$index] =~ m/^\d{2}\/\d{2}\/\d{2}$/ or @last_line[$index] ne "" ) { - push( @new_last_line, sprintf( $precision{$param}, @last_line[$index] ) . $units{$param} ); - } - else { - push( @new_last_line, sprintf( $precision{$param}, "-" ) . $units{$param} ); - } - } - -format STDOUT = -| @||||||| | @||||||||| | @|||||||||||||| | @||||||||||||||||||| | @|||||||||||| | @||||||||||||||| | @|||||||||||||||| | -@new_last_line; -. -write; - } - - close( QOR_FILE ); - exit "QoR results displayed"; - } - } -} - -sub run_single_test { - my ($test) = @_; - - if ($first) { - print "=============================================\n"; - print " Verilog-to-Routing Regression Testing \n"; - print "=============================================\n"; - $first = 0; - } - - print "\nRunning $test\n"; - print "-------------------------------------------------------------------------------\n"; - print "scripts/run_vtr_task.pl $run_params -script $script \n"; - print "\n"; - chdir("$vtr_flow_path"); - my $test_status = system("scripts/run_vtr_task.pl $run_params -script $script \n"); - chdir(".."); - - #Perl is obtuse, and requires you to manually shift the return value by 8 bits - #to get the real exit code from a call to system(). There must be a better way to do this.... - my $exit_code = $test_status >> 8; - return $exit_code; -} - -sub parse_single_test { - while ( my $golden = shift(@_) ) { - if ( $golden eq "create" ) { - print "\nCreating golden results... \n"; - $parse_params = $parse_params . "-create_golden "; - } - elsif ( $golden eq "check" ) { - print "\nChecking test results... \n"; - $parse_params = $parse_params . "-check_golden "; - } - elsif ( $golden eq "calculate" ) { - print "\nCalculating QoR results... \n"; - $parse_params = $parse_params . "-calc_geomean "; - } - else { - print "\nParsing test results... \n"; - } - } - chdir("$vtr_flow_path"); - print "scripts/parse_vtr_task.pl $parse_params \n"; - my $parse_status = system("scripts/parse_vtr_task.pl $parse_params \n"); - chdir(".."); - - #Perl is obtuse, and requires you to manually shift the return value by 8 bits - #to get the real exit code from a call to system(). There must be a better way to do this.... - my $exit_code = $parse_status >> 8; - return $exit_code; -} - -sub run_quick_test { - - print "=============================================\n"; - print " Test Verilog-to-Routing Infrastructure \n"; - print "=============================================\n\n"; - - system("rm -f quick_test/depth_split.blif"); - system("rm -f quick_test/abc_out.blif"); - system("rm -f quick_test/route.out"); - - - chdir ("quick_test") or die "Failed to change to directory ./quick_test: $!"; - - print "Testing ODIN II: "; - system("../ODIN_II/odin_II.exe -c depth_split.xml > odin_ii_out.txt 2>&1"); - if(-f "depth_split.blif") { - print " [PASSED]\n\n"; - } else { - print " [FAILED]\n\n"; - } - - print "Testing ABC: "; - system("../abc_with_bb_support/abc -c \"read abc_test.blif;sweep;write_hie abc_test.blif abc_out.blif\" > abc_out.txt 2>&1" ); - if(-f "abc_out.blif") { - print " [PASSED]\n\n"; - } else { - print " [FAILED]\n\n"; - } - - print "Testing VPR: "; - system("../vpr/vpr sample_arch.xml vpr_test -blif_file vpr_test.blif -route_file route.out > vpr_out.txt 2>&1"); - if(-f "route.out") { - print " [PASSED]\n\n"; - } else { - print " [FAILED]\n\n"; - } - - chdir (".."); -} - -sub run_odin_test { - $token = shift; - - my $odin_cmd = "ODIN_II/verify_odin.sh --clean -C ODIN_II/regression_test/.library/output_on_error.conf --nb_of_process $num_cpu --test ODIN_II/regression_test/benchmark/"; - my $return_status = 0; - if ( $token eq "odin_reg_micro" ) { - $return_status = system($odin_cmd."suite/light_suite"); - } elsif ( $token eq "odin_reg" ) { - $return_status = system($odin_cmd."task/full"); - } elsif ( $token eq "odin_reg_large" ) { - $return_status = system($odin_cmd."task/large"); - } elsif ( $token eq "odin_reg_operators" ) { - $return_status = system($odin_cmd."task/operators"); - } elsif ( $token eq "odin_reg_arch" ) { - $return_status = system($odin_cmd."task/arch_sweep"); - } elsif ( $token eq "odin_reg_syntax" ) { - $return_status = system($odin_cmd."task/syntax"); - } elsif ( $token eq "odin_reg_full" ) { - $return_status = system($odin_cmd."suite/full_suite"); - } else { - die("Unrecognized odin test $token"); - } - - #Perl is obtuse, and requires you to manually shift the return value by 8 bits - #to get the real exit code from a call to system(). There must be a better way to do this.... - # odin return the number of failure as its exit code - my $exit_code = $return_status >> 8; - return $exit_code; -} - -sub trim($) { - my $string = shift; - $string =~ s/^\s+//; - $string =~ s/\s+$//; - return $string; -} - -sub uniq { - my %seen; - return grep { !$seen{$_}++ } @_; -} diff --git a/vtr_flow/scripts/parse_vtr_flow.pl b/vtr_flow/scripts/parse_vtr_flow.pl deleted file mode 100755 index b96772e249e..00000000000 --- a/vtr_flow/scripts/parse_vtr_flow.pl +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env perl -################################################################################### -# This script is used to extract statistics from a single execution of the VTR flow -# -# Usage: -# parse_vtr_flow.pl [ ...] -# -# Parameters: -# parse_path: Directory path that contains the files that will be -# parsed (vpr.out, odin.out, etc). -# parse_config_file: Path to the Parse Configuration File -# extra_param: Extra key=value pairs to be added to the output -################################################################################### - - -use strict; -use Cwd; -use File::Spec; -use File::Basename; - -sub expand_user_path; - -if ( $#ARGV + 1 < 2 ) { - print "usage: parse_vtr_flow.pl [ ...]\n"; - exit(-1); -} -my $parse_path = expand_user_path(shift(@ARGV)); -my $parse_config_file = expand_user_path(shift(@ARGV)); -my @extra_params = @ARGV; - -if ( !-r $parse_config_file ) { - die "Cannot find parse file ($parse_config_file)\n"; -} - -my @parse_lines = load_file_with_includes($parse_config_file); - -my @parse_data; -my $file_to_parse; -foreach my $line (@parse_lines) { - chomp($line); - - # Ignore comments - if ( $line =~ /^\s*#.*$/ or $line =~ /^\s*$/ ) { next; } - - my @name_file_regexp = split( /;/, $line ); - push(@parse_data, [@name_file_regexp]); -} - -# attributes to parse become headings -for my $extra_param (@extra_params) { - my ($param_name, $param_value) = split('=', $extra_param); - print $param_name . "\t"; -} - -for my $parse_entry (@parse_data) { - print @$parse_entry[0] . "\t"; -} -print "\n"; - -for my $extra_param (@extra_params) { - my ($param_name, $param_value) = split('=', $extra_param); - print $param_value . "\t"; -} -my $count = 0; -for my $parse_entry (@parse_data) { - my $file_to_parse = "@$parse_entry[1]"; - my $file_to_parse_path = - File::Spec->catdir( ${parse_path}, ${file_to_parse} ); - my $default_not_found = "-1"; - if (scalar @$parse_entry > 3) { - $default_not_found = "@$parse_entry[3]"; - } - - $count++; - if ( $file_to_parse =~ /\*/ ) { - my @files = glob($file_to_parse_path); - if ( @files == 1 ) { - $file_to_parse_path = $files[0]; - } - else { - die "Wildcard in filename ($file_to_parse) matched " . @files - . " files. There must be exactly one match.\n"; - } - } - - if ( not -r "$file_to_parse_path" ) { - print $default_not_found; - print "\t"; - } - else { - open( DATA_FILE, "<$file_to_parse_path" ); - my @parse_file_lines = ; - close(DATA_FILE); - - chomp(@parse_file_lines); - - my $regexp = @$parse_entry[2]; - $regexp =~ s/\s+$//; - - my $result = $default_not_found; - for my $line (@parse_file_lines) { - if ( $line =~ m/$regexp/gm ) { - #Match - $result = $1; - - #Note that we keep going even if a match is found, - #so that the recorded value is the *last* match in - #the file - } - } - # tab separation even at end of line to indicate last element - print "$result\t"; - } -} -print "\n"; - -sub expand_user_path { - my $str = shift; - $str =~ s/^~\//$ENV{"HOME"}\//; - return $str; -} - - -sub load_file_with_includes { - my ($filepath) = @_; - - my @lines; - - foreach my $line (load_file_lines($filepath)) { - if ($line =~ m/^\s*%include\s+"(.*)"\s*$/) { - #Assume the included file is in the same direcotry - my $include_filepath = File::Spec->catfile(dirname($filepath), $1); - $include_filepath = Cwd::realpath($include_filepath); - - #Load it's lines, note that this is done recursively to resolve all includes - my @included_file_lines = load_file_with_includes($include_filepath); - push(@lines, "#Starting %include $include_filepath\n"); - push(@lines, @included_file_lines); - push(@lines, "#Finished %include $include_filepath\n"); - } else { - push(@lines, $line); - } - } - - return @lines; -} - -sub load_file_lines { - my ($filepath) = @_; - - open(FILE, "<$filepath" ) or die("Failed to open file $filepath"); - my @lines = ; - close(FILE); - - return @lines; -} diff --git a/vtr_flow/scripts/parse_vtr_task.pl b/vtr_flow/scripts/parse_vtr_task.pl deleted file mode 100755 index 01c566e5f3e..00000000000 --- a/vtr_flow/scripts/parse_vtr_task.pl +++ /dev/null @@ -1,938 +0,0 @@ -#!/usr/bin/env perl - -################################################################################### -# This script is used to extract and verify statistics of one or more VTR tasks. -# -# Usage: -# parse_vtr_task.pl ... [OPTIONS] -# -# Options: -# -l : Used to provide a test file containing a list of tasks -# -create_golden: Will create/overwrite the golden results with those of the -# most recent execution -# -check_golden: Will verify the results of the most recent execution against -# the golden results for each task and report either a -# [Pass] or [Fail] -# -parse_qor: Used for the purposes of parsing quality of results of the -# most recent execution. -# -calc_geomean: Used for the purposes of computing quality of results geomeans -# of the most recent execution. -# -# Authors: Jason Luu and Jeff Goeders -################################################################################### - -use strict; -use Cwd; -use File::Spec; -use File::Copy; -use File::Basename; -use List::Util; -use Math::BigInt; -use POSIX qw/strftime/; - -# Function Prototypes -sub trim; -sub parse_single_task; -sub pretty_print_table; -sub summarize_qor; -sub calc_geomean; -sub check_golden; -sub expand_user_path; -sub get_important_file; -sub parse_single_flow_run; -sub aggregate_single_flow_runs; - -# Get Absolute Path of 'vtr_flow -Cwd::abs_path($0) =~ m/(.*vtr_flow)/; -my $vtr_flow_path = $1; - -my $run_prefix = "run"; - -my $FAILED_PARSE_EXIT_CODE = -1; - -# Parse Input Arguments -my @tasks; -my @task_files; -my $token; -my $create_golden = 0; -my $check_golden = 0; -my $parse_qor = 1; # QoR file is parsed by default; turned off if - # user does not specify QoR parse file in config.txt -my $calc_geomean = 0; # QoR geomeans are not computed by default; -my $override_exp_id = 0; -my $revision; -my $verbose = 0; -my $pretty_print_results = 1; - -while ( $token = shift(@ARGV) ) { - - # Check for a task list file - if ( $token =~ /^-l(.+)$/ ) { - push( @task_files, expand_user_path($1) ); - } - elsif ( $token eq "-l" ) { - push( @task_files, expand_user_path( shift(@ARGV) ) ); - } - elsif ( $token eq "-create_golden" ) { - $create_golden = 1; - } - elsif ( $token eq "-check_golden" ) { - $check_golden = 1; - } - elsif ( $token eq "-parse_qor" ) { - $parse_qor = 1; - } - elsif ( $token eq "-calc_geomean" ) { - $calc_geomean = 1; - } - elsif ( $token eq "-run") { - $override_exp_id = shift(@ARGV); - } - elsif ( $token eq "-revision" ) { - $revision = shift(@ARGV); - } - elsif ( $token eq "-v" ) { - $verbose = 1; - } - elsif ( $token =~ /^-/ ) { - die "Invalid option: $token\n"; - } - # must be a task name - else { - if ( $token =~ /(.*)\/$/ ) { - $token = $1; - } - push( @tasks, $token ); - } -} - -# Read Task Files -foreach (@task_files) { - open( FH, $_ ) or die "$! ($_)\n"; - while () { - push( @tasks, $_ ); - } - close(FH); -} - -my $num_golden_failures = 0; -foreach my $task (@tasks) { - chomp($task); - my $failures = parse_single_task($task); - $num_golden_failures += $failures; -} - -if ($calc_geomean) { - summarize_qor; - calc_geomean; -} - -exit $num_golden_failures; - -sub parse_single_task { - # This routine can operate in a few different ways": - # Parse a result file and compare it to a golen (prior) result. This is the default functionality - # Parse two result files and check equivelence if a second file is provided in the config file. This option disables the comparison to the golden results. - # Optionally, in conjunction with either of the two above options, a qor_parse_file can be provided in the config file and also be compared to the first parsed result file - - - my $task_name = shift; - (my $task_path = $task_name) =~ s/\s+$//; - - # first see if task_name is the task path - if (! -e "$task_path/config/config.txt") { - ($task_path = "$vtr_flow_path/tasks/$task_name") =~ s/\s+$//; - } - - open( CONFIG, "<$task_path/config/config.txt" ) - or die "Failed to open $task_path/config/config.txt: $!\n"; - my @config_data = ; - close(CONFIG); - - my @archs_list; - my @circuits_list; - my @script_params_list; - my $parse_file; - my $qor_parse_file; - my $second_parse_file; - my $counter = 0; - - #determine if this test individual test is compared to the golden values or not. - my $single_check_golden = $check_golden; - - foreach my $line (@config_data) { - - # Ignore comments - if ( $line =~ /^\s*#.*$/ or $line =~ /^\s*$/ ) { next; } - - #Trim off a line-ending comment - $line =~ s/#.*$//; - - my @data = split( /=/, $line ); - my $key = trim( $data[0] ); - my $value = trim( $data[1] ); - - if ( $key eq "circuit_list_add" ) { - push( @circuits_list, $value ); - } elsif ( $key eq "arch_list_add" ) { - push( @archs_list, $value ); - } elsif ( $key eq "script_params_list_add" ) { - push( @script_params_list, $value ); - } elsif ( $key eq "parse_file" ) { - if ($counter eq 1){ - #second time parse file - $second_parse_file = expand_user_path($value); - $counter = 0; - #don't need to check golden for this test, only compare between two files - #If we used the check_golden vairable here, any test following this test would also not compare to golden values. - $single_check_golden = 0; - } - else{ - $parse_file = expand_user_path($value); - $counter = $counter + 1; - } - } elsif ( $key eq "qor_parse_file" ) { - $qor_parse_file = expand_user_path($value); - } - } - - if (!@script_params_list) { - #Add a default empty param if none otherwise specified - #(i.e. only base params) - push(@script_params_list, ""); - } - - # PARSE CONFIG FILE - if ( $parse_file eq "" ) { - die "Task $task_name has no parse file specified.\n"; - } - $parse_file = get_important_file($task_path, $vtr_flow_path, $parse_file); - - if ($second_parse_file){ - $second_parse_file = get_important_file($task_path, $vtr_flow_path, $second_parse_file); - } - - # Get Max Run # - opendir(DIR, $task_path); - my @folders = readdir(DIR); - closedir(DIR); - # QOR PARSE CONFIG FILE - if ( $qor_parse_file eq "" ) { - print "Task $task_name has no QoR parse file specified. Skipping QoR.\n"; - $parse_qor = 0; - $calc_geomean = 0; - } - else { - $qor_parse_file = get_important_file($task_path, $vtr_flow_path, $qor_parse_file); - } - - my $exp_id = 0; - if($override_exp_id != 0) { - #explicitely specified via -run parameter - $exp_id = $override_exp_id; - } else { - # haven't explicitely specified via -run parameter - $exp_id = last_exp_id(${task_path}); - } - - my $run_path = "$task_path/${run_prefix}${exp_id}"; - - my @first_run_result_files; - my @second_run_result_files; - my @qor_run_result_files; - - foreach my $arch (@archs_list) { - foreach my $circuit (@circuits_list) { - foreach my $script_params (@script_params_list) { - my $script_params_dirname = "common"; - if ($script_params ne "") { - $script_params_dirname .= "_" . $script_params; - $script_params_dirname =~ s/ /_/g; - } - - #First run parse - my $work_dir = "$run_path/$arch/$circuit/$script_params_dirname"; - my $output_filepath = "$work_dir/parse_results.txt"; - my $config_prefix = "arch\tcircuit\tscript_params"; - my $config_values = "arch=$arch" . " circuit=$circuit" . " script_params=$script_params_dirname"; - parse_single_flow_run($work_dir, $config_values, $parse_file, $output_filepath); - push(@first_run_result_files, $output_filepath); - - if($second_parse_file){ - my $output_filepath = "$work_dir/parse_results_2.txt"; - parse_single_flow_run($work_dir, $config_values, $second_parse_file, $output_filepath); - push(@second_run_result_files, $output_filepath); - } - - if ($parse_qor) { - my $output_filepath = "$work_dir/qor_results.txt"; - parse_single_flow_run($work_dir, $config_values, $qor_parse_file, $output_filepath); - push(@qor_run_result_files, $output_filepath); - } - } - } - } - - if (@first_run_result_files) { - aggregate_single_flow_runs(\@first_run_result_files, "$run_path/parse_results.txt"); - } - if (@second_run_result_files) { - aggregate_single_flow_runs(\@second_run_result_files, "$run_path/parse_results_2.txt"); - } - if (@qor_run_result_files) { - aggregate_single_flow_runs(\@qor_run_result_files, "$run_path/qor_results.txt"); - } - - if ($create_golden) { - copy( "$run_path/parse_results.txt", "$run_path/../config/golden_results.txt" ); - } - - if ($second_parse_file){ - #don't check with golden results, just check the two files - return check_two_files ( $task_name, $task_path, $run_path, "$run_path/parse_results.txt", "$run_path/parse_results_2.txt", 0); - - } - if ($single_check_golden) { - #Returns 1 if failed - if ($second_parse_file eq ""){ - return check_two_files( $task_name, $task_path, $run_path, "$run_path/parse_results.txt", "$task_path/config/golden_results.txt", $single_check_golden); - } - } - - return 0; #Pass -} - -sub parse_single_flow_run { - my ($run_dir, $config_values, $parse_file, $output_file_path) = @_; - - my $cmd = join(" ", "$vtr_flow_path/scripts/parse_vtr_flow.pl", $run_dir, $parse_file, $config_values, "> $output_file_path"); - - my $ret = system($cmd); - if ($ret != 0) { - print "System command '$cmd' failed\n"; - exit $FAILED_PARSE_EXIT_CODE; - } - pretty_print_table($output_file_path); -} - -sub aggregate_single_flow_runs { - my ($parse_result_files, $output_file_path) = @_; - - my $first = 0; - - open(OUTPUT_FILE, ">$output_file_path" ); - - my $size = @$parse_result_files; - for (my $i=0; $i < $size; $i++) { - my $result_file = @$parse_result_files[$i]; - - open(RESULT_FILE, $result_file); - my @lines = ; - close(RESULT_FILE); - - if ($i == 0) { - #First file add header - print OUTPUT_FILE $lines[0]; - } - - #Data - print OUTPUT_FILE $lines[1]; - - } - close(OUTPUT_FILE); - pretty_print_table($output_file_path); -} - -sub summarize_qor { - - ############################################################## - # Set up output file - ############################################################## - - my $first = 1; - - my $task = @tasks[0]; - (my $task_path = $task) =~ s/\s+$//; - - # first see if task_name is the task path - if (! -e "$task_path/config/config.txt") { - ($task_path = "$vtr_flow_path/tasks/$task") =~ s/\s+$//; - } - - my $output_path = $task_path; - my $exp_id = last_exp_id($task_path); - - if ( ( ( $#tasks + 1 ) > 1 ) | ( -e "$task_path/../task_list.txt" ) ) { - $output_path = "$task_path/../"; - } - if ( !-e "$output_path/task_summary" ) { - mkdir "$output_path/task_summary"; - } - if ( -e "$output_path/task_summary/${run_prefix}${exp_id}_summary.txt" ) { - } - open( OUTPUT_FILE, ">$output_path/task_summary/${run_prefix}${exp_id}_summary.txt" ); - - ############################################################## - # Append contents of QoR files to output file - ############################################################## - - foreach my $task (@tasks) { - chomp($task); - ($task_path = $task) =~ s/\s+$//; - - # first see if task_name is the task path - if (! -e "$task_path/config/config.txt") { - ($task_path = "$vtr_flow_path/tasks/$task") =~ s/\s+$//; - } - $exp_id = last_exp_id($task_path); - (my $run_path = "$task_path/${run_prefix}${exp_id}") =~ s/\s+$//; - - open( RESULTS_FILE, "$run_path/qor_results.txt" ); - my $output = ; - - if ($first) { - print OUTPUT_FILE "task_name\t$output"; - $first = 0; - } - - while ($output = ) { - print OUTPUT_FILE $task . "\t" . $output; - } - close(RESULTS_FILE); - } - close(OUTPUT_FILE); -} - -sub calc_geomean { - - ############################################################## - # Set up output file - ############################################################## - - my $first = 0; - - my $task = @tasks[0]; - (my $task_path = $task) =~ s/\s+$//; - - # first see if task_name is the task path - if (! -e "$task_path/config/config.txt") { - ($task_path = "$vtr_flow_path/tasks/$task") =~ s/\s+$//; - } - my $output_path = $task_path; - my $exp_id = last_exp_id($task_path); - - if ( ( ( $#tasks + 1 ) > 1 ) | ( -e "$task_path/../task_list.txt" ) ) { - ($output_path = "$task_path/../") =~ s/\s+$//; - } - if ( !-e "$output_path/qor_geomean.txt" ) { - open( OUTPUT_FILE, ">$output_path/qor_geomean.txt" ); - $first = 1; - } - else { - open( OUTPUT_FILE, ">>$output_path/qor_geomean.txt" ); - } - - ############################################################## - # Read summary file - ############################################################## - - my $summary_file = "$output_path/task_summary/${run_prefix}${exp_id}_summary.txt"; - - if ( !-r $summary_file ) { - print "[ERROR] Failed to open $summary_file: $!\n"; - return; - } - open( SUMMARY_FILE, "<$summary_file" ); - my @summary_data = ; - close(SUMMARY_FILE); - - my $summary_params = shift @summary_data; - my @summary_params = split( /\t/, trim($summary_params) ); - - if ($first) { - # Hack - remove unwanted labels - my $num = 4; - while ($num) { - shift @summary_params; - --$num; - } - print OUTPUT_FILE "run"; - my @temp = @summary_params; - while ( $#temp >= 0 ) { - my $label = shift @temp; - print OUTPUT_FILE "\t" . "$label"; - } - print OUTPUT_FILE "\t" . "date" . "\t" . "revision"; - $first = 0; - } - else { - } - - print OUTPUT_FILE "\n${exp_id}"; - - ############################################################## - # Compute & write geomean to output file - ############################################################## - - my $index = 4; - my @summary_params = split( /\t/, trim($summary_params) ); - - while ( $#summary_params >= $index ) { - my $geomean = 1; my $num = 0; - foreach my $line (@summary_data) { - my @first_file_line = split( /\t/, $line ); - if ( trim( @first_file_line[$index] ) > 0 ) { - $geomean *= trim( @first_file_line[$index] ); - $num++; - } - } - if ($num) { - $geomean **= 1/$num; - print OUTPUT_FILE "\t" . "${geomean}"; - } - else { - print OUTPUT_FILE "\t" . "-1"; - } - $index++; - } - my $date = strftime( '%D', localtime ); - print OUTPUT_FILE "\t" . "$date" . "\t" . "$revision"; - close(OUTPUT_FILE); -} - -sub max { - my $x = shift; - my $y = shift; - - return ($x < $y) ? $y : $x; -} - -sub pretty_print_table { - my $file_path = shift; - - - #Read the input file - my @file_data; - open(INFILE,"<$file_path"); - while() { - chomp; - push(@file_data, [split /\t/]) - } - - #Determine the maximum column width for pretty formatting - my %col_widths; - for my $row (0 .. $#file_data) { - for my $col (0 .. $#{$file_data[$row]}) { - - my $col_width = length $file_data[$row][$col]; - - #Do we have a valid column width? - if (not exists $col_widths{$col}) { - #Initial width - $col_widths{$col} = $col_width; - } else { - #Max width - $col_widths{$col} = max($col_widths{$col}, $col_width); - } - - } - } - - #Write out in pretty format - open(OUTFILE,">$file_path"); - for my $row (0 .. $#file_data) { - for my $col (0 .. $#{$file_data[$row]}) { - printf OUTFILE "%-*s", $col_widths{$col}, $file_data[$row][$col]; - - if($col != $#{$file_data[$row]}) { - printf OUTFILE "\t"; - } - } - printf OUTFILE "\n"; - } - close(OUTFILE); - -} - -sub last_exp_id { - my $path = shift; - my $num = 0; - my $run_id = ""; - my $run_id_no_pad = ""; - do { - ++$num; - $run_id = sprintf("%03d", $num); - $run_id_no_pad = sprintf("%d", $num); - } while ( -e "$path/${run_prefix}${run_id}" or -e "$path/${run_prefix}${run_id_no_pad}"); - --$num; - $run_id = sprintf("%03d", $num); - $run_id_no_pad = sprintf("%d", $num); - - if( -e "$path/${run_prefix}${run_id}" ) { - return $run_id; - } elsif (-e "$path/${run_prefix}${run_id_no_pad}") { - return $run_id_no_pad; - } - - die("Unknown experiment id"); -} - -sub check_two_files { - my $task_name = shift; - my $task_path = shift; - my $run_path = shift; - my $first_test_file_dir = shift; - my $second_test_file_dir = shift; - my $is_golden = shift; - - #Did this check pass? - my $failed = 0; - - print "$task_name..."; - print "\n" if $verbose; - - # Code to check the results of the two files - (my $test_file_1 = "$first_test_file_dir") =~ s/\s+$//; - (my $test_file_2 = "$second_test_file_dir") =~ s/s+$//; - - my $pass_req_file; - open( CONFIG_FILE, "$task_path/config/config.txt" ); - my $lines = do { local $/; ; }; - close(CONFIG_FILE); - - # Search config file - if ( $lines =~ /^\s*pass_requirements_file\s*=\s*(\S+)\s*$/m ) { - my $pass_req_filename = $1; - - if ($pass_req_filename !~ /^\s*%/) { #Not blank - - # Search for pass requirement file - $pass_req_filename = expand_user_path($pass_req_filename); - if ( -e "$task_path/config/$pass_req_filename" ) { - $pass_req_file = "$task_path/config/$pass_req_filename"; - } elsif ( -e "$vtr_flow_path/parse/pass_requirements/$pass_req_filename" ) { - $pass_req_file = - "$vtr_flow_path/parse/pass_requirements/$pass_req_filename"; - } elsif ( -e $pass_req_filename ) { - $pass_req_file = $pass_req_filename; - } - - if ( -e $pass_req_file ) { - $failed += check_pass_requirements($pass_req_file, $test_file_1, $test_file_2, $is_golden); - } else { - print "[ERROR] Cannot find pass_requirements_file. Checked for $task_path/config/$pass_req_filename or $vtr_flow_path/parse/$pass_req_filename or $pass_req_filename\n"; - $failed += 1; - } - } - } else { - print - "[Warning] No 'pass_requirements_file' in task configuration file ($task_path/config/config.txt)\n"; - } - - - if ($failed == 0) { - print "[Pass]\n"; - } - return $failed; -} - -sub check_pass_requirements() { - my ($pass_req_file, $test_file_1, $test_file_2, $is_golden) = @_; - - my $line; - - my @first_test_data; - my @second_test_data; - my @pass_req_data; - - my @params; - my %type; - my %min_threshold; - my %max_threshold; - my %abs_diff_threshold; - my $failed = 0; - - ############################################################## - # Read files - ############################################################## - if ( !-r $test_file_2 ) { - print "[ERROR] Failed to open $test_file_2: $!\n"; - $failed += 1; - return $failed; - } - open( GOLDEN_DATA, "<$test_file_2" ); - @second_test_data = ; - close(GOLDEN_DATA); - - if ( !-r $pass_req_file ) { - print "[ERROR] Failed to open $pass_req_file: $!\n"; - $failed += 1; - return $failed; - } - - @pass_req_data = load_file_with_includes($pass_req_file); - - if ( !-r $test_file_1 ) { - print "[ERROR] Failed to open $test_file_1: $!\n"; - $failed += 1; - return $failed; - } - open( TEST_DATA, "<$test_file_1" ); - @first_test_data = ; - close(TEST_DATA); - - ############################################################## - # Process and check all parameters for consistency - ############################################################## - my $second_test_params = shift @second_test_data; - my $first_test_params = shift @first_test_data; - - my @second_test_params = split( /\t/, $second_test_params ); # get parameters of the second file results - my @first_test_params = split( /\t/, $first_test_params ); # get parameters of the first file results - - my @second_test_params = map(trim($_), @second_test_params); - my @first_test_params = map(trim($_), @first_test_params); - - my %first_params_index; - my $i = 0; - foreach my $param (@first_test_params) { - $first_params_index{$param} = $i; - $i++; - } - my %second_params_index; - $i = 0; - foreach my $param (@second_test_params) { - $second_params_index{$param} = $i; - $i++; - } - - # Check to ensure all parameters to compare are consistent - foreach $line (@pass_req_data) { - - # Ignore comments - if ( $line =~ /^\s*#.*$/ or $line =~ /^\s*$/ ) { next; } - - my @data = split( /;/, $line ); - my $name = trim( $data[0] ); - my $check_arg = trim($data[1]); - my ($check_func) = $check_arg =~ m/(.*)\(/; - my ($check_func_args_str) = $check_arg =~ m/\((.*)\)/; - my @check_func_args = split( /,/, $check_func_args_str); - $type{$name} = trim($check_func); - if ( $check_func eq "Range" ) { - $min_threshold{$name} = trim( $check_func_args[0] ); - $max_threshold{$name} = trim( $check_func_args[1] ); - } elsif ($check_func eq "RangeAbs") { - $min_threshold{$name} = trim( $check_func_args[0] ); - $max_threshold{$name} = trim( $check_func_args[1] ); - $abs_diff_threshold{$name} = trim( $check_func_args[2] ); #Third element is absolute threshold - } elsif ($check_func eq "Equal") { - #Pass - } else { - print "[ERROR] $name has no valid comparison check specified (e.g. Range, RangeAbs, Equal) (was '$check_func').\n"; - $failed += 1; - return $failed; - } - - #Ensure item is in the first file - if (!exists $second_params_index{$name}) { - if ($is_golden){ - print "[ERROR] $name is not in the golden results file.\n"; - }else{ - print "[ERROR] $name is not in the second parse file.\n"; - } - $failed += 1; - } - - # Ensure item is in new results - if (!exists $first_params_index{$name}) { - if ($is_golden){ - print "[ERROR] $name is not in the results file.\n"; - }else{ - print "[ERROR] $name is not in the first parse file.\n"; - } - $failed += 1; - } - - push( @params, $name ); - } - - ############################################################## - # Compare first file data data with second file data - ############################################################## - if ( ( scalar @first_test_data ) != ( scalar @second_test_data ) ) { - print - "[ERROR] Different number of entries in the two files.\n"; - $failed += 1; - } - - # Iterate through each line of the test results data and compare with the golden data - foreach $line (@first_test_data) { - my @first_file_line = split( /\t/, $line ); - my @second_file_line = split( /\t/, shift @second_test_data ); - - - my $second_file_arch = trim(@second_file_line[$second_params_index{'arch'}]); - my $second_file_circuit = trim(@second_file_line[$second_params_index{'circuit'}]); - my $second_file_script_params = trim(@second_file_line[$second_params_index{'script_params'}]); - my $first_file_arch = trim(@first_file_line[$first_params_index{'arch'}]); - my $first_file_circuit = trim(@first_file_line[$first_params_index{'circuit'}]); - my $first_file_script_params = trim(@first_file_line[$first_params_index{'script_params'}]); - - if ( ( $first_file_circuit ne $first_file_circuit ) - or ( $first_file_arch ne $first_file_arch ) - or ( $first_file_script_params ne $first_file_script_params )) { - if ($is_golden){ - print "[ERROR] Circuit/Architecture/Script-Params mismatch between golden results ($second_file_arch/$second_file_circuit/$second_file_script_params) and result ($first_file_arch/$first_file_circuit/$first_file_script_params).\n"; - } else{ - print "[ERROR] Circuit/Architecture/Script-Params mismatch between first result file($second_file_arch/$second_file_circuit/$second_file_script_params) and second result file ($first_file_arch/$first_file_circuit/$first_file_script_params).\n"; - } - $failed += 1; - return $failed; - } - my $circuitarch = "$first_file_arch/$first_file_circuit/$first_file_script_params"; - - # Check each parameter where the type determines what to check for - foreach my $value (@params) { - my $first_file_index = $first_params_index{$value}; - my $second_file_index = $second_params_index{$value}; - my $first_file_value = trim(@first_file_line[$first_file_index]); - my $second_file_value = trim(@second_file_line[$second_file_index]); - - - if ( $type{$value} eq "Range" or $type{$value} eq "RangeAbs" ) { - my $abs_diff = abs($first_file_value - $second_file_value); - my $ratio; - if ($second_file_value == 0) { - $ratio = "inf"; - } else { - $ratio = $first_file_value / $second_file_value; - } - - if($verbose) { - print "\tParam: $value\n"; - print "\t\tTest: $first_file_value\n"; - print "\t\tGolden Value: $second_file_value\n"; - print "\t\tRatio: $ratio\n"; - print "\t\tAbsDiff $abs_diff\n"; - print "\t\tMinRatio $min_threshold{$value}\n"; - print "\t\tMaxRatio $max_threshold{$value}\n"; - print "\t\tAbsThreshold $abs_diff_threshold{$value}\n"; - } - - if ( exists $abs_diff_threshold{$value} - and $abs_diff <= $abs_diff_threshold{$value}) { - #Within absolute threshold - next; - } - - if ( $ratio >= $min_threshold{$value} - and $ratio <= $max_threshold{$value}) { - #Within relative thershold - next; - } - - if ($first_file_value == $second_file_value) { - #Equal (e.g. both zero) - next; - } - - if ( $first_file_value eq 'nan' - and $second_file_value eq 'nan') { - #Both literal Not-a-Numbers - next; - } - - #Beyond absolute and relative thresholds - if ($is_golden){ - print - "[Fail] \n $circuitarch $value: golden = $second_file_value result = $first_file_value\n"; - }else{ - print - "[Fail] \n $circuitarch $value: first result = $first_file_value second result = $second_file_value\n"; - } - $failed += 1; - } elsif ($type{$value} eq "Equal") { - if ( $first_file_value ne $second_file_value ) { - if ($is_golden) { - print "[Fail] \n $circuitarch $value: golden = $second_file_value result = $first_file_value\n"; - } else { - print "[Fail] \n $circuitarch $value: first result = $first_file_value second result = $second_file_value\n"; - } - $failed += 1; - } - } else { - # If the check type is unknown - $failed += 1; - print "[Fail] \n $circuitarch $value: unrecognized check type '$type{$value}' (e.g. Range, RangeAbs, Equal)\n"; - - } - } - } - return $failed; -} - -sub trim() { - my $string = shift; - $string =~ s/^\s+//; - $string =~ s/\s+$//; - return $string; -} - -sub expand_user_path { - my $str = shift; - $str =~ s/^~\//$ENV{"HOME"}\//; - return $str; -} - -sub get_important_file { - my $task_path = shift; - my $vtr_flow_path = shift; - my $file = shift; - if ( -e "$task_path/config/$file" ) { - return "$task_path/config/$file"; - } - elsif ( -e "$vtr_flow_path/parse/parse_config/$file" ) { - return "$vtr_flow_path/parse/parse_config/$file"; - } - elsif ( -e "$vtr_flow_path/parse/qor_config/$file" ) { - return "$vtr_flow_path/parse/qor_config/$file"; - } - elsif ( $file !~ /^\/.*/ ) { - die "Important file does not exist ($file)"; - } -} - -sub load_file_with_includes { - my ($filepath) = @_; - - my @lines; - - foreach my $line (load_file_lines($filepath)) { - if ($line =~ m/^\s*%include\s+"(.*)"\s*$/) { - #Assume the included file is in the same direcotry - my $include_filepath = File::Spec->catfile(dirname($filepath), $1); - $include_filepath = Cwd::realpath($include_filepath); - - #Load it's lines, note that this is done recursively to resolve all includes - my @included_file_lines = load_file_with_includes($include_filepath); - push(@lines, "#Starting %include $include_filepath\n"); - push(@lines, @included_file_lines); - push(@lines, "#Finished %include $include_filepath\n"); - } else { - push(@lines, $line); - } - } - - return @lines; -} - -sub load_file_lines { - my ($filepath) = @_; - - open(FILE, "<$filepath" ) or die("Failed to open file $filepath\n"); - my @lines = ; - close(FILE); - - return @lines; -} diff --git a/vtr_flow/scripts/run_vtr_flow.pl b/vtr_flow/scripts/run_vtr_flow.pl deleted file mode 100755 index 0a63125518b..00000000000 --- a/vtr_flow/scripts/run_vtr_flow.pl +++ /dev/null @@ -1,1774 +0,0 @@ -#!/usr/bin/env perl -################################################################################### -# This script runs the VTR flow for a single benchmark circuit and architecture -# file. -# -# Usage: -# run_vtr_flow.pl [OPTIONS] -# -# Parameters: -# circuit_file: Path to the input circuit file (verilog, blif, etc) -# architecture_file: Path to the architecture file (.xml) -# -# Options: -# Note that all unrecognized parameters are forwarded to VPR. -# -starting_stage : Start the VTR flow at the specified stage. -# Acceptable values: odin, abc, script, vpr. -# Default value is odin. -# -ending_stage : End the VTR flow at the specified stage. Acceptable -# values: odin, abc, script, vpr. Default value is -# vpr. -# -delete_intermediate_files : Deletes the intermediate files (.xml, .v, .dot, .rc, ...) -# -delete_result_files : Deletes the result files (.net, .place, .route) -# -track_memory_usage : Print out memory usage for each stage (NOT fully portable) -# -limit_memory_usage : Kill benchmark if it is taking up too much memory to avoid -# slow disk swaps. -# -timeout : Maximum amount of time to spend on a single stage of a task in seconds; -# default is 14 days. -# -temp_dir : Directory used for all temporary files -# -valgrind : Runs the flow with valgrind with the following options (--leak-check=full, -# --errors-for-leak-kinds=none, --error-exitcode=1, --track-origins=yes) -# -min_hard_mult_size : Tells ODIN II what is the minimum multiplier size that should be synthesized using hard -# multipliers (Default = 3) -# -min_hard_adder_size : Tells ODIN II what is the minimum adder size that should be synthesizes -# using hard adders (Default = 1) -# -# Any unrecognized arguments are forwarded to VPR. -################################################################################### - -use strict; -use warnings; -use Cwd; -use Sys::Hostname; -use File::Spec; -use POSIX; -use File::Copy; -use FindBin; -use File::Find; -use File::Basename; -use Time::HiRes; - -use lib "$FindBin::Bin/perl_libs/XML-TreePP-0.41/lib"; -use XML::TreePP; - -my $vtr_flow_start_time = Time::HiRes::gettimeofday(); - -# Check the parameters. Note PERL does not consider the script itself a parameter. -my $number_arguments = @ARGV; -if ( $number_arguments < 2 ) { - print( - "usage: run_vtr_flow.pl [OPTIONS]\n" - ); - exit(-1); -} - -# Get Absolute Path of vtr_flow -Cwd::abs_path($0) =~ m/(.*\/vtr_flow)\//; -my $vtr_flow_path = $1; -# my $vtr_flow_path = "./vtr_flow"; - -sub stage_index; -sub file_ext_for_stage; -sub expand_user_path; -sub file_find_and_replace; -sub xml_find_LUT_Kvalue; -sub xml_find_mem_size; -sub exe_for_platform; - -my $temp_dir = "./temp"; -my $diff_exec = "diff"; -my $copy_exec = "cp"; -my $move_exec = "mv"; - -my $stage_idx_odin = 1; -my $stage_idx_abc = 2; -my $stage_idx_ace = 3; -my $stage_idx_prevpr = 4; -my $stage_idx_vpr = 5; - -my $circuit_file_path = expand_user_path( shift(@ARGV) ); -my $architecture_file_path = expand_user_path( shift(@ARGV) ); -my $sdc_file_path = undef; -my $pad_file_path = undef; - -my $ext; -my $starting_stage = stage_index("odin"); -my $ending_stage = stage_index("vpr"); -my @vpr_stages = (); -my $keep_intermediate_files = 1; -my $keep_result_files = 1; -my $lut_size = undef; -my $tech_file = ""; -my $do_power = 0; -my $check_equivalent = "off"; -my $min_hard_mult_size = 3; -my $min_hard_adder_size = 1; - -my $ace_seed = 1; - -my $track_memory_usage = 1; -my $memory_tracker = "/usr/bin/env"; -my @memory_tracker_args = ("time", "-v"); -my $limit_memory_usage = -1; -my $timeout = 14 * 24 * 60 * 60; # 14 day execution timeout -my $valgrind = 0; -my @valgrind_args = ("--leak-check=full", "--suppressions=$vtr_flow_path/../vpr/valgrind.supp", "--error-exitcode=22", "--track-origins=yes", "--error-limit=no"); -my $abc_quote_addition = 0; -my @forwarded_vpr_args; # VPR arguments that pass through the script -my $verify_rr_graph = 0; -my $rr_graph_ext = ".xml"; -my $check_route = 0; -my $check_place = 0; -my $use_old_abc_script = 0; -my $check_incremental_sta_consistency = 0; -my $run_name = ""; -my $expect_fail = undef; -my $verbosity = 0; -my $odin_adder_config_path = "default"; -my $odin_adder_cin_global = ""; -my $use_odin_xml_config = 1; -my $relax_W_factor = 1.3; -my $crit_path_router_iterations = 150; #We set a higher routing iterations (vs 50 default) - #to avoid spurious routing failures at relaxed W - #caused by small perturbations in pattern or - #placement. Usually these failures show up on small - #circuits (with low W). Setting a higher value here - #will help avoids them. -my $show_failures = 0; - - -########## -# ABC flow modifiers -my $flow_type = 2; #Use iterative black-boxing flow for multi-clock circuits -my $use_new_latches_restoration_script = 1; -my $odin_run_simulation = 0; - -while ( scalar(@ARGV) != 0 ) { #While non-empty - my $token = shift(@ARGV); - if ( $token eq "-sdc_file" ) { - $sdc_file_path = shift(@ARGV);#let us take the user input as an absolute path - if ( !-e $sdc_file_path) { #check if absolute path exists - $sdc_file_path = "${vtr_flow_path}/${sdc_file_path}"; #assume this is a relative path - } - if ( !-e $sdc_file_path) { #check if relative path exists - die - "Error: Invalid SDC file specified"; - } - } elsif ( $token eq "-fix_pins" and $ARGV[0] ne "random") { - $pad_file_path = shift(@ARGV); - if ( !-e $pad_file_path) { #check if absolute path exists - $pad_file_path = "${vtr_flow_path}/${pad_file_path}"; #assume this is a relative path - } - if ( !-e $pad_file_path) { #check if relative path exists - die - "Error: Invalid pad file specified"; - } - } elsif ( $token eq "-starting_stage" ) { - $starting_stage = stage_index( shift(@ARGV) ); - } elsif ( $token eq "-ending_stage" ) { - $ending_stage = stage_index( shift(@ARGV) ); - } elsif ( $token eq "-delete_intermediate_files" ) { - $keep_intermediate_files = 0; - } elsif ( $token eq "-delete_result_files" ) { - $keep_result_files = 0; - } elsif ( $token eq "-track_memory_usage" ) { - $track_memory_usage = 1; - } elsif ( $token eq "-limit_memory_usage" ) { - $limit_memory_usage = shift(@ARGV); - $abc_quote_addition = 1; - } elsif ( $token eq "-timeout" ) { - $timeout = shift(@ARGV); - } elsif ( $token eq "-valgrind" ) { - $valgrind = 1; - } elsif ( $token eq "-lut_size" ) { - $lut_size = shift(@ARGV); - } elsif ( $token eq "-temp_dir" ) { - $temp_dir = shift(@ARGV); - } elsif ( $token eq "-cmos_tech" ) { - $tech_file = shift(@ARGV); - } elsif ( $token eq "-power" ) { - $do_power = 1; - } elsif ( $token eq "-check_equivalent" ) { - $check_equivalent = "on"; - $keep_intermediate_files = 1; - } elsif ( $token eq "-min_hard_mult_size" ) { - $min_hard_mult_size = shift(@ARGV); - } elsif ( $token eq "-min_hard_adder_size" ) { - $min_hard_adder_size = shift(@ARGV); - } elsif ( $token eq "-verify_rr_graph" ){ - $verify_rr_graph = 1; - } elsif ( $token eq "-rr_graph_ext" ){ - $rr_graph_ext = shift(@ARGV); - } elsif ( $token eq "-check_route" ){ - $check_route = 1; - } elsif ( $token eq "-check_place" ){ - $check_place = 1; - } elsif ( $token eq "-use_old_abc_script"){ - $use_old_abc_script = 1; - } elsif ( $token eq "-name"){ - $run_name = shift(@ARGV); - } elsif ( $token eq "-expect_fail"){ - $expect_fail = shift(@ARGV); - } - elsif ( $token eq "-verbose"){ - $verbosity = 1; - } - elsif ( $token eq "-adder_type"){ - $odin_adder_config_path = shift(@ARGV); - if ( ($odin_adder_config_path ne "default") && ($odin_adder_config_path ne "optimized") ) { - $odin_adder_config_path = $vtr_flow_path . $odin_adder_config_path; - } - } - elsif ( $token eq "-disable_odin_xml" ){ - $use_odin_xml_config = 0; - } - elsif ( $token eq "-use_odin_simulation" ){ - $odin_run_simulation = 1; - } - elsif ( $token eq "-adder_cin_global" ){ - $odin_adder_cin_global = "--adder_cin_global"; - } - elsif ( $token eq "-use_new_latches_restoration_script" ){ - $use_new_latches_restoration_script = 1; - } - elsif ( $token eq "-show_failures" ){ - $show_failures = 1; - } - elsif ( $token eq "-iterative_bb" ){ - $flow_type = 2; - $use_new_latches_restoration_script = 1; - } - elsif ( $token eq "-once_bb" ){ - $flow_type = 1; - $use_new_latches_restoration_script = 1; - } - elsif ( $token eq "-blanket_bb" ){ - $flow_type = 3; - $use_new_latches_restoration_script = 1; - } - elsif ( $token eq "-relax_W_factor" ){ - $relax_W_factor = shift(@ARGV); - } - elsif ( $token eq "-crit_path_router_iterations" ){ - $crit_path_router_iterations = shift(@ARGV); - } - elsif ( $token eq "-check_incremental_sta_consistency" ){ - $check_incremental_sta_consistency = 1; - } - # else forward the argument - else { - push @forwarded_vpr_args, $token; - } - - if ( $starting_stage == -1 or $ending_stage == -1 ) { - die - "Error: Invalid starting/ending stage name (start $starting_stage end $ending_stage).\n"; - } -} - -{ - my ($basename, $parentdir, $extension) = fileparse($architecture_file_path, '\.[^\.]*'); - if ($extension ne ".xml") { - die "Error: Expected circuit file as first argument (was $circuit_file_path), and FPGA Architecture file as second argument (was $architecture_file_path)\n"; - } -} - -if ( $ending_stage < $starting_stage ) { - die "Error: Ending stage is before starting stage."; -} -if ($do_power) { - if ( $tech_file eq "" ) { - die "A CMOS technology behavior file must be provided."; - } - elsif ( not -r $tech_file ) { - die "The CMOS technology behavior file ($tech_file) cannot be opened."; - } - $tech_file = Cwd::abs_path($tech_file); -} - -if ( !-d $temp_dir ) { - system "mkdir $temp_dir"; -} --d $temp_dir or die "Could not make temporary directory ($temp_dir)\n"; -if ( !( $temp_dir =~ /.*\/$/ ) ) { - $temp_dir = $temp_dir . "/"; -} - -my $results_path = "${temp_dir}output.txt"; - -my $error = ""; -my $error_code = 0; -my $error_status = "OK"; - -my $arch_param; -my $cluster_size; -my $inputs_per_cluster = -1; - -# Test for file existance -( -f $circuit_file_path ) - or die "Circuit file not found ($circuit_file_path)"; -( -f $architecture_file_path ) - or die "Architecture file not found ($architecture_file_path)"; - -my $vpr_path; -if ( $stage_idx_vpr >= $starting_stage and $stage_idx_vpr <= $ending_stage ) { - $vpr_path = exe_for_platform("$vtr_flow_path/../vpr/vpr"); - ( -r $vpr_path or -r "${vpr_path}.exe" ) or die "Cannot find vpr exectuable ($vpr_path)"; -} - -#odin is now necessary for simulation -my $odin2_path; my $odin_config_file_name; my $odin_config_file_path; -if ( $stage_idx_abc >= $starting_stage - and $stage_idx_odin <= $ending_stage ) -{ - $odin2_path = exe_for_platform("$vtr_flow_path/../ODIN_II/odin_II"); - ( -e $odin2_path ) - or die "Cannot find ODIN_II executable ($odin2_path)"; - - $odin_config_file_name = "basic_odin_config_split.xml"; - - $odin_config_file_path = "$vtr_flow_path/misc/$odin_config_file_name"; - ( -e $odin_config_file_path ) - or die "Cannot find ODIN config template ($odin_config_file_path)"; - - $odin_config_file_name = "odin_config.xml"; - my $odin_config_file_path_new = "$temp_dir" . "odin_config.xml"; - copy( $odin_config_file_path, $odin_config_file_path_new ); - $odin_config_file_path = $odin_config_file_path_new; -} - -my $abc_path; -my $abc_rc_path; -if ( $stage_idx_abc >= $starting_stage or $stage_idx_vpr <= $ending_stage ) { - #Need ABC for either synthesis or post-VPR verification - my $abc_dir_path = "$vtr_flow_path/../abc"; - $abc_path = "$abc_dir_path/abc"; - $abc_rc_path = "$abc_dir_path/abc.rc"; - - ( -e $abc_path or -e "${abc_path}.exe" ) - or die "Cannot find ABC executable ($abc_path)"; - ( -e $abc_rc_path ) or die "Cannot find ABC RC file ($abc_rc_path)"; - - copy( $abc_rc_path, $temp_dir ); -} - -my $restore_multiclock_info_script; -if($use_new_latches_restoration_script) -{ - $restore_multiclock_info_script = "$vtr_flow_path/scripts/restore_multiclock_latch.pl"; -} -else -{ - $restore_multiclock_info_script = "$vtr_flow_path/scripts/restore_multiclock_latch_information.pl"; -} - -my $blackbox_latches_script = "$vtr_flow_path/scripts/blackbox_latches.pl"; - -my $ace_path; -if ( $stage_idx_ace >= $starting_stage and $stage_idx_ace <= $ending_stage and $do_power) { - $ace_path = "$vtr_flow_path/../ace2/ace"; - ( -e $ace_path or -e "${ace_path}.exe" ) - or die "Cannot find ACE executable ($ace_path)"; -} -my $ace_clk_extraction_path = "$vtr_flow_path/../ace2/scripts/extract_clk_from_blif.py"; - -#Extract the circuit/architecture name and filename -my ($benchmark_name, $tmp_path1, $circuit_suffix) = fileparse($circuit_file_path, '\.[^\.]*'); -my $circuit_file_name = $benchmark_name . $circuit_suffix; - -my ($architecture_name, $tmp_path2, $arch_suffix1) = fileparse($architecture_file_path, '\.[^\.]*'); -my ($architecture_name_error, $tmp_path3, $arch_suffix) = fileparse($architecture_file_path, '\.[^\.]*'); - -my $architecture_file_name = $architecture_name . $arch_suffix; -my $error_architecture_file_name = join "", $architecture_name, "_error", $arch_suffix; - - -if ($run_name eq "") { - $run_name = "$architecture_name/$benchmark_name"; -} -printf("%-120s", $run_name); - -# Get Memory Size -my $mem_size = -1; -my $line; -my $in_memory_block; -my $in_mode; - -# Read arch XML -my $tpp = XML::TreePP->new(); -my $xml_tree = $tpp->parsefile($architecture_file_path); - -# Get memory size -$mem_size = xml_find_mem_size($xml_tree); - -my $odin_output_file_name = "$benchmark_name" . file_ext_for_stage($stage_idx_odin, $circuit_suffix); -my $odin_output_file_path = "$temp_dir$odin_output_file_name"; - -#The raw unprocessed ABC output -my $abc_raw_output_file_name = "$benchmark_name" . ".raw" . file_ext_for_stage($stage_idx_abc, $circuit_suffix); -my $abc_raw_output_file_path = "$temp_dir$abc_raw_output_file_name"; - -#The processed ABC output useable by downstream tools -my $abc_output_file_name = "$benchmark_name" . file_ext_for_stage($stage_idx_abc, $circuit_suffix); -my $abc_output_file_path = "$temp_dir$abc_output_file_name"; - -#Clock information for ACE -my $ace_clk_file_name = "ace_clk.txt"; -my $ace_clk_file_path = "$temp_dir$ace_clk_file_name"; - -#The raw unprocessed ACE output -my $ace_raw_output_blif_name = "$benchmark_name" . ".raw" . file_ext_for_stage($stage_idx_ace, $circuit_suffix); -my $ace_raw_output_blif_path = "$temp_dir$ace_raw_output_blif_name"; - -#The processed ACE output useable by downstream tools -my $ace_output_blif_name = "$benchmark_name" . file_ext_for_stage($stage_idx_ace, $circuit_suffix); -my $ace_output_blif_path = "$temp_dir$ace_output_blif_name"; - -my $ace_output_act_name = "$benchmark_name" . ".act"; -my $ace_output_act_path = "$temp_dir$ace_output_act_name"; - -my $prevpr_output_file_name = "$benchmark_name" . file_ext_for_stage($stage_idx_prevpr, $circuit_suffix); -my $prevpr_output_file_path = "$temp_dir$prevpr_output_file_name"; - -my $prevpr_sdc_file_name = "$benchmark_name" . ".sdc"; -my $prevpr_sdc_file_path = "$temp_dir$prevpr_output_file_name"; - -my $prevpr_pad_file_name = "$benchmark_name" . ".pad"; -my $prevpr_pad_file_path = "$temp_dir$prevpr_output_file_name"; - -my $vpr_route_output_file_name = "$benchmark_name.route"; -my $vpr_route_output_file_path = "$temp_dir$vpr_route_output_file_name"; -my $vpr_postsynthesis_netlist = ""; - -#system"cp $abc_rc_path $temp_dir"; -#system "cp $architecture_path $temp_dir"; -#system "cp $circuit_path $temp_dir/$benchmark_name" . file_ext_for_stage($starting_stage - 1); -#system "cp $odin2_base_config" - -#Copy architecture -my $architecture_file_path_new = "$temp_dir$architecture_file_name"; -copy( $architecture_file_path, $architecture_file_path_new ); -$architecture_file_path = $architecture_file_path_new; - -#Copy circuit -my $circuit_file_path_new = "$temp_dir$benchmark_name" . file_ext_for_stage($starting_stage - 1, $circuit_suffix); -copy( $circuit_file_path, $circuit_file_path_new ); -$circuit_file_path = $circuit_file_path_new; - -#Copy SDC file -if (defined $sdc_file_path) { - my $sdc_file_path_new = "$temp_dir$prevpr_sdc_file_name"; - copy($sdc_file_path, $sdc_file_path_new); - $sdc_file_path = $sdc_file_path_new; -} - -#Copy PAD file -if (defined $pad_file_path) { - my $pad_file_path_new = "$temp_dir$prevpr_pad_file_name"; - copy($pad_file_path, $pad_file_path_new); - $pad_file_path = $pad_file_path_new; -} - -# Call executable and time it -my $StartTime = time; -my $q = "not_run"; - -################################################################################# -################################## ODIN ######################################### -################################################################################# - -if ( $starting_stage <= $stage_idx_odin and !$error_code ) { - - #system "sed 's/XXX/$benchmark_name.v/g' < $odin2_base_config > temp1.xml"; - #system "sed 's/YYY/$arch_name/g' < temp1.xml > temp2.xml"; - #system "sed 's/ZZZ/$odin_output_file_path/g' < temp2.xml > temp3.xml"; - #system "sed 's/PPP/$mem_size/g' < temp3.xml > circuit_config.xml"; - - file_find_and_replace( $odin_config_file_path, "XXX", $circuit_file_name ); - file_find_and_replace( $odin_config_file_path, "YYY", $architecture_file_name ); - file_find_and_replace( $odin_config_file_path, "ZZZ", $odin_output_file_name ); - file_find_and_replace( $odin_config_file_path, "PPP", $mem_size ); - file_find_and_replace( $odin_config_file_path, "MMM", $min_hard_mult_size ); - file_find_and_replace( $odin_config_file_path, "AAA", $min_hard_adder_size ); - - if ( !$error_code ) { - if ( $use_odin_xml_config ) { - $q = &system_with_timeout( "$odin2_path", "odin.out", $timeout, $temp_dir, - "-c", $odin_config_file_name, - "--adder_type", $odin_adder_config_path, - $odin_adder_cin_global, - "-U0"); - } else { - $q = &system_with_timeout( "$odin2_path", "odin.out", $timeout, $temp_dir, - "--adder_type", $odin_adder_config_path, - "-a", $temp_dir . $architecture_file_name, - "-V", $temp_dir . $circuit_file_name, - "-o", $temp_dir . $odin_output_file_name, - $odin_adder_cin_global, - "-U0"); - } - - if ( ! -e $odin_output_file_path or $q ne "success") { - $error_status = "failed: odin"; - $error_code = 1; - } - } -} - -################################################################################# -######################## PRE-ABC SIMULATION VERIFICATION ####################### -################################################################################# - -if ( $starting_stage <= $stage_idx_abc - and $ending_stage >= $stage_idx_abc - and !$error_code ) -{ - # this is not made mandatory since some hardblocks are not recognized by odin - # we let odin figure out the best number of vector to simulate for best coverage - if($odin_run_simulation) { - system "mkdir simulation_init"; - - $q = &system_with_timeout( "$odin2_path", "sim_produce_vector.out", $timeout, $temp_dir, - "-b", $temp_dir . $odin_output_file_name, - "-a", $temp_dir . $architecture_file_name, - "-sim_dir", $temp_dir."simulation_init/", - "-g", "100", - "--best_coverage", - "-U0"); #ABC sets DC bits as 0 - - if ( $q ne "success") - { - $odin_run_simulation = 0; - print "\tfailed to include simulation\n"; - system "rm -Rf ${temp_dir}simulation_init"; - } - } -} - -################################################################################# -#################################### ABC ######################################## -################################################################################# - -if ( $starting_stage <= $stage_idx_abc - and $ending_stage >= $stage_idx_abc - and !$error_code ) -{ - #added so that valgrind will not run on abc and perl because of existing memory errors - my $skip_valgrind = $valgrind; - $valgrind = 0; - - - # Get lut size if undefined - if (!defined $lut_size) { - $lut_size = xml_find_LUT_Kvalue($xml_tree); - } - if ( $lut_size < 1 ) { - $error_status = "failed: cannot determine arch LUT k-value"; - $error_code = 1; - } - - my @clock_list; - - if ( $flow_type ) - { - ########## - # Populate the clock list - # - # get all the clock domains and parse the initial values for each - # we will iterate though the file and use each clock iteratively - my $clk_list_filename = "report_clk.out"; - $q = &system_with_timeout($blackbox_latches_script, "report_clocks.abc.out", $timeout, $temp_dir, - "--input", $odin_output_file_name, "--output_list", $clk_list_filename); - - if ($q ne "success") { - $error_status = "failed: to find available clocks in blif file"; - $error_code = 1; - } - - # parse the clock file and populate the clock list using it - my $clock_list_file; - open ($clock_list_file, "<", $temp_dir.$clk_list_filename) or die "Unable to open \"".$clk_list_filename."\": $! \n"; - #read line and strip whitespace of line - my $line = ""; - while(($line = <$clock_list_file>)) - { - $line =~ s/^\s+|\s+$//g; - if($line =~ /^latch/) - { - #get the initial value out (last char) - push(@clock_list , $line); - } - } - } - - # for combinationnal circuit there will be no clock, this works around this - # also unless we request itterative optimization, only run ABC once - my $number_of_itteration = scalar @clock_list; - if( not $number_of_itteration - or $flow_type != 2 ) - { - $number_of_itteration = 1; - } - - ################ - # ABC iterative optimization - # - my $input_blif = $odin_output_file_name; - - ABC_OPTIMIZATION: foreach my $domain_itter ( 0 .. ($number_of_itteration-1) ) - { - my $pre_abc_blif = $domain_itter."_".$odin_output_file_name; - my $post_abc_raw_blif = $domain_itter."_".$abc_raw_output_file_name; - my $post_abc_blif = $domain_itter."_".$abc_output_file_name; - - if( $flow_type == 3 ) - { - # black box latches - $q = &system_with_timeout($blackbox_latches_script, $domain_itter."_blackboxing_latch.out", $timeout, $temp_dir, - "--input", $input_blif, "--output", $pre_abc_blif); - - if ($q ne "success") { - $error_status = "failed: to black box the clocks for file_in: ".$input_blif." file_out: ".$pre_abc_blif; - $error_code = 1; - last ABC_OPTIMIZATION; - } - } - elsif ( exists $clock_list[$domain_itter] ) - { - # black box latches - $q = &system_with_timeout($blackbox_latches_script, $domain_itter."_blackboxing_latch.out", $timeout, $temp_dir, - "--clk_list", $clock_list[$domain_itter], "--input", $input_blif, "--output", $pre_abc_blif); - - if ($q ne "success") { - $error_status = "failed: to black box the clock <".$clock_list[$domain_itter]."> for file_in: ".$input_blif." file_out: ".$pre_abc_blif; - $error_code = 1; - last ABC_OPTIMIZATION; - } - } - else - { - $pre_abc_blif = $input_blif; - } - - ########### - # ABC Optimizer - - #For ABC’s documentation see: https://people.eecs.berkeley.edu/~alanmi/abc/abc.htm - # - #Some key points on the script used: - # - # strash : The strash command (which build's ABC's internal AIG) is needed before clean-up - # related commands (e.g. ifraig) otherwise they will fail with “Only works for - # structurally hashed networks”. - # - # if –K #: This command techmaps the logic to LUTS. It should appear as the (near) final step - # before writing the optimized netlist. In recent versions, ABC does not remember - # that LUT size you want to techmap to. As a result, specifying if -K # early in - # the script causes ABC techmap to 2-LUTs, greatly increasing the amount of logic required (CLB’s, blocks, nets, etc.). - # - # The current script is based off the one used by YOSYS and on discussions with Alan Mishchenko (ABC author). - # On 2018/04/28 Alan suggested the following: - # (1) run synthesis commands such as "dc2" after "ifraig" and "scorr" (this way more equivalences are typically found - improves quality) - # (2) run "ifraig" before "scorr" (this way comb equivalences are removed before seq equivalences are computed - improves runtime) - # (3) run "dch -f" immediately before mapping "if" (this alone greatly improves both area and delay of mapping) - # (4) no need to run "scleanup" if "scorr" is used ("scorr" internally performs "scleanup" - improves runtime) - # (5) no need to run"dc2" if "dch -f" is used, alternatively run "dc2; dch -f" (this will take more runtime but may not improve quality) - # (6) the only place to run "strash" is after technology mapping (if the script is run more than once - can improve quality) - my $abc_commands=" - echo ''; - echo 'Load Netlist'; - echo '============'; - read ${pre_abc_blif}; - time; - - echo ''; - echo 'Circuit Info'; - echo '=========='; - print_stats; - print_latch; - time; - - echo ''; - echo 'LUT Costs'; - echo '========='; - print_lut; - time; - - echo ''; - echo 'Logic Opt + Techmap'; - echo '==================='; - strash; - ifraig -v; - scorr -v; - dc2 -v; - dch -f; - if -K ${lut_size} -v; - mfs2 -v; - print_stats; - time; - - echo ''; - echo 'Output Netlist'; - echo '=============='; - write_hie ${pre_abc_blif} ${post_abc_raw_blif}; - time; - "; - - if ($use_old_abc_script) { - #Legacy ABC script adapted for new ABC by moving scleanup before if - $abc_commands=" - read $pre_abc_blif; - time; - resyn; - resyn2; - time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; - if -K $lut_size; - write_hie ${pre_abc_blif} ${post_abc_raw_blif}; - print_stats; - "; - } - - $abc_commands =~ s/\R/ /g; #Convert new-lines to spaces - - if ($abc_quote_addition) {$abc_commands = "'" . $abc_commands . "'";} - - $q = &system_with_timeout( $abc_path, "abc".$domain_itter.".out", $timeout, $temp_dir, "-c", - $abc_commands); - - if ( $q ne "success") { - $error_status = "failed: abc"; - $error_code = 1; - last ABC_OPTIMIZATION; - } - elsif ( not -e "${temp_dir}/$post_abc_raw_blif" ) { - $error_status = "failed: abc did not produce the expected output: ${temp_dir}/${post_abc_raw_blif}"; - $error_code = 1; - last ABC_OPTIMIZATION; - } - - if ( $flow_type != 3 and exists $clock_list[$domain_itter] ) - { - # restore latches with the clock - $q = &system_with_timeout($blackbox_latches_script, "restore_latch".$domain_itter.".out", $timeout, $temp_dir, - "--restore", $clock_list[$domain_itter], "--input", $post_abc_raw_blif, "--output", $post_abc_blif); - - if ($q ne "success") { - $error_status = "failed: to restore latches to their clocks <".$clock_list[$domain_itter]."> for file_in: ".$input_blif." file_out: ".$pre_abc_blif; - $error_code = 1; - last ABC_OPTIMIZATION; - } - } - else - { - # Restore Multi-Clock Latch Information from ODIN II that was striped out by ABC - $q = &system_with_timeout($restore_multiclock_info_script, "restore_latch".$domain_itter.".out", $timeout, $temp_dir, - $pre_abc_blif, $post_abc_raw_blif, $post_abc_blif); - - if ($q ne "success") { - $error_status = "failed: to restore multi-clock latch info"; - $error_code = 1; - last ABC_OPTIMIZATION; - } - } - - $input_blif = $post_abc_blif; - - if ( $flow_type != 2 ) - { - last ABC_OPTIMIZATION; - } - } - - ################ - # POST-ABC - - #return all clocks to vanilla clocks - $q = &system_with_timeout($blackbox_latches_script, "vanilla_restore_clocks.out", $timeout, $temp_dir, - "--input", $input_blif, "--output", $abc_output_file_name, "--vanilla"); - - if ($q ne "success") { - $error_status = "failed: to return to vanilla.\n"; - $error_code = 1; - } - - - ################ - # Cleanup - if ( !$error_code - and !$keep_intermediate_files ) { - if (! $do_power) { - system "rm -f $odin_output_file_path"; - } - system "rm -f ${temp_dir}*.dot"; - system "rm -f ${temp_dir}*.v"; - system "rm -f ${temp_dir}*.rc"; - } - - #restore the current valgrind flag - $valgrind = $skip_valgrind; -} - -################################################################################# -######################## POST-ABC SIMULATION VERIFICATION ####################### -################################################################################# - -if ( $starting_stage <= $stage_idx_abc - and $ending_stage >= $stage_idx_abc - and !$error_code ) -{ - if($odin_run_simulation) { - - system "mkdir simulation_test"; - - $q = &system_with_timeout( "$odin2_path", "odin_simulation.out", $timeout, $temp_dir, - "-b", $temp_dir . $abc_output_file_name, - "-a", $temp_dir . $architecture_file_name, - "-t", $temp_dir . "simulation_init/input_vectors", - "-T", $temp_dir . "simulation_init/output_vectors", - "-sim_dir", $temp_dir."simulation_test/", - "-U0"); - - if ( $q ne "success") { - print " (failed: odin Simulation) "; - } - - if ( !$error_code - and !$keep_intermediate_files ) - { - system "rm -Rf ${temp_dir}simulation_test"; - system "rm -Rf ${temp_dir}simulation_init"; - } - } -} - -################################################################################# -################################## ACE ########################################## -################################################################################# -if ( $starting_stage <= $stage_idx_ace - and $ending_stage >= $stage_idx_ace - and $do_power - and !$error_code ) -{ - my $abc_clk_name; - - $q = &system_with_timeout($ace_clk_extraction_path, "ace_clk_extraction.out", $timeout, $temp_dir, - $ace_clk_file_name, $abc_output_file_name); - - if ($q ne "success") { - $error_status = "failed: ace clock extraction (only single clock activiy estimation is supported)"; - $error_code = 1; - } - - { - local $/ = undef; - open(FILE, $ace_clk_file_path) or die "Can't read file '$ace_clk_file_path' ($!)\n"; - $abc_clk_name = ; - close (FILE); - } - - if (!$error_code) { - $q = &system_with_timeout( - $ace_path, "ace.out", $timeout, $temp_dir, - "-b", $abc_output_file_name, - "-c", $abc_clk_name, - "-n", $ace_raw_output_blif_name, - "-o", $ace_output_act_name, - "-s", $ace_seed - ); - - if ( -e $ace_raw_output_blif_path and $q eq "success") { - - #added so that valgrind will not run on perl because of existing memory errors - my $skip_valgrind = $valgrind; - $valgrind = 0; - - # Restore Multi-Clock Latch Information from ODIN II that was striped out by ACE - $q = &system_with_timeout($restore_multiclock_info_script, "restore_multiclock_latch_information.ace.out", $timeout, $temp_dir, - $odin_output_file_name, $ace_raw_output_blif_name, $ace_output_blif_name); - - #restore the current valgrind flag - $valgrind = $skip_valgrind; - - if ($q ne "success") { - $error_status = "failed: to restore multi-clock latch info"; - $error_code = 1; - - } - - if ( !$keep_intermediate_files ) { - system "rm -f $abc_output_file_path"; - system "rm -f $odin_output_file_path"; - } - } - else { - $error_status = "failed: ace"; - $error_code = 1; - } - } -} - -################################################################################# -################################## PRE-VPR ###################################### -################################################################################# -if ( $starting_stage <= $stage_idx_prevpr - and $ending_stage >= $stage_idx_prevpr - and !$error_code ) -{ - my $prevpr_success = 1; - my $prevpr_input_blif_path; - if ($do_power) { - $prevpr_input_blif_path = $ace_output_blif_path; - } else { - $prevpr_input_blif_path = $abc_output_file_path; - } - - copy($prevpr_input_blif_path, $prevpr_output_file_path); - - if ($prevpr_success) { - if ( !$keep_intermediate_files ) { - system "rm -f $prevpr_input_blif_path"; - } - } - else { - $error_status = "failed: prevpr"; - $error_code = 1; - } -} - -################################################################################# -################################## VPR ########################################## -################################################################################# - -if ( $ending_stage >= $stage_idx_vpr and !$error_code ) { - my @vpr_power_args; - - if ($do_power) { - push(@forwarded_vpr_args, "--power"); - push(@forwarded_vpr_args, "--tech_properties"); - push(@forwarded_vpr_args, "$tech_file"); - } - - #True if a fixed channel width routing is desired - my $route_fixed_W = (grep(/^--route_chan_width$/, @forwarded_vpr_args)); - - #set a min chan width if it is not set to ensure equal results - if (($check_route or $check_place) and !$route_fixed_W){ - push(@forwarded_vpr_args, ("--route_chan_width", "300")); - $route_fixed_W = 1; - } - - #Where any VPR stages explicitly requested? - my $explicit_pack_vpr_stage = (grep(/^--pack$/, @forwarded_vpr_args)); - my $explicit_place_vpr_stage = (grep(/^--place$/, @forwarded_vpr_args)); - my $explicit_route_vpr_stage = (grep(/^--route$/, @forwarded_vpr_args)); - my $explicit_analysis_vpr_stage = (grep(/^--analysis$/, @forwarded_vpr_args)); - - #If no VPR stages are explicitly specified, then all stages run by default - my $implicit_all_vpr_stage = !($explicit_pack_vpr_stage - or $explicit_place_vpr_stage - or $explicit_route_vpr_stage - or $explicit_analysis_vpr_stage); - - if (!$route_fixed_W) { - #Determine the mimimum channel width - - my $min_W_log_file = "vpr.out"; - $q = run_vpr({ - arch_name => $architecture_file_name, - circuit_name => $benchmark_name, - circuit_file => $prevpr_output_file_name, - sdc_file => $sdc_file_path, - pad_file => $pad_file_path, - extra_vpr_args => \@forwarded_vpr_args, - log_file => $min_W_log_file - }); - - my $do_routing = ($explicit_route_vpr_stage or $implicit_all_vpr_stage); - - if ($do_routing) { - # Critical path delay and wirelength is nonsensical at minimum channel width because congestion constraints - # dominate the cost function. - # - # Additional channel width needs to be added so that there is a reasonable trade-off between delay and area. - # Commercial FPGAs are also desiged to have more channels than minimum for this reason. - - if ($q eq "success") { - my $min_W = parse_min_W("$temp_dir/$min_W_log_file"); - - - if ($min_W >= 0) { - my $relaxed_W = calculate_relaxed_W($min_W, $relax_W_factor); - - my @relaxed_W_extra_vpr_args = @forwarded_vpr_args; - push(@relaxed_W_extra_vpr_args, ("--route")); - push(@relaxed_W_extra_vpr_args, ("--route_chan_width", "$relaxed_W")); - - if (defined $crit_path_router_iterations) { - push(@relaxed_W_extra_vpr_args, ("--max_router_iterations", "$crit_path_router_iterations")); - } - - my $relaxed_W_log_file = "vpr.crit_path.out"; - $q = run_vpr({ - arch_name => $architecture_file_name, - circuit_name => $benchmark_name, - circuit_file => $prevpr_output_file_name, - sdc_file => $sdc_file_path, - pad_file => $pad_file_path, - extra_vpr_args => \@relaxed_W_extra_vpr_args, - log_file => $relaxed_W_log_file, - }); - } else { - my $abs_log_file = File::Spec->rel2abs($temp_dir/$min_W_log_file); - $q = "Failed find minimum channel width (see $abs_log_file)"; - } - } - } - } else { # specified channel width - my $fixed_W_log_file = "vpr.out"; - - my $rr_graph_out_file = "rr_graph" . $rr_graph_ext; - - my @fixed_W_extra_vpr_args = @forwarded_vpr_args; - - if ($verify_rr_graph){ - push(@fixed_W_extra_vpr_args, ("--write_rr_graph", $rr_graph_out_file)); - } - - $q = run_vpr({ - arch_name => $architecture_file_name, - circuit_name => $benchmark_name, - circuit_file => $prevpr_output_file_name, - sdc_file => $sdc_file_path, - pad_file => $pad_file_path, - extra_vpr_args => \@fixed_W_extra_vpr_args, - log_file => $fixed_W_log_file, - }); - - - if ($verify_rr_graph && (! -e $rr_graph_out_file || -z $rr_graph_out_file)) { - $error_status = "failed: vpr (no RR graph file produced)"; - $error_code = 1; - } - - #Run vpr again with additional parameters. - #This is used to ensure that files generated by VPR can be re-loaded by it - my $do_second_vpr_run = ($verify_rr_graph or $check_route or $check_place); - - if ($do_second_vpr_run) { - my @second_run_extra_vpr_args = @forwarded_vpr_args; - - my $rr_graph_out_file2 = "rr_graph2" . $rr_graph_ext; - if ($verify_rr_graph){ - push( @second_run_extra_vpr_args, ("--read_rr_graph", $rr_graph_out_file)); - push( @second_run_extra_vpr_args, ("--write_rr_graph", $rr_graph_out_file2)); - } - - if ($check_route){ - push( @second_run_extra_vpr_args, "--analysis"); - } - - if ($check_place) { - push( @second_run_extra_vpr_args, "--route"); - } - - my $second_run_log_file = "vpr_second_run.out"; - - $q = run_vpr({ - arch_name => $architecture_file_name, - circuit_name => $benchmark_name, - circuit_file => $prevpr_output_file_name, - sdc_file => $sdc_file_path, - pad_file => $pad_file_path, - extra_vpr_args => \@second_run_extra_vpr_args, - log_file => $second_run_log_file, - }); - - if ($verify_rr_graph) { - #Sanity check that the RR graph produced after reading the - #previously dumped RR graph is identical. - # - #This ensures no precision loss/value changes occur - - my @diff_args; - push(@diff_args, $rr_graph_out_file); - push(@diff_args, $rr_graph_out_file2); - - my $diff_result = &system_with_timeout( - $diff_exec, "diff.rr_graph.out", - $timeout, $temp_dir, - @diff_args - ); - - if ($diff_result ne "success") { - $error_status = "failed: vpr (RR Graph XML output not consistent when reloaded)"; - $error_code = 1; - } - } - } - } - - - #Removed check for existing vpr_route_output_path in order to pass when routing is turned off (only pack/place) - if ($q eq "success") { - if($check_equivalent eq "on") { - - find(\&find_postsynthesis_netlist, "."); - - #Pick the netlist to verify against - # - #We pick the 'earliest' netlist of the stages that where run - my $reference_netlist = ""; - if($starting_stage <= $stage_idx_odin) { - $reference_netlist = $odin_output_file_name; - } elsif ($starting_stage <= $stage_idx_abc) { - $reference_netlist = $abc_output_file_name; - } else { - #VPR's input - $reference_netlist = $prevpr_output_file_name; - } - - - #First try ABC's Unbounded Sequential Equivalence Check (DSEC) - $q = &system_with_timeout($abc_path, - "abc.sec.out", - $timeout, - $temp_dir, - "-c", - "dsec $reference_netlist $vpr_postsynthesis_netlist" - ); - - # Parse ABC verification output - if ( open( SECOUT, "< $temp_dir/abc.sec.out" ) ) { - undef $/; - my $sec_content = ; - close(SECOUT); - $/ = "\n"; # Restore for normal behaviour later in script - - if ( $sec_content =~ m/(.*The network has no latches. Used combinational command "cec".*)/i ) { - # This circuit has no latches, ABC's 'sec' command only supports circuits with latches. - # Re-try using ABC's Combinational Equivalence Check (CEC) - $q = &system_with_timeout($abc_path, - "abc.cec.out", - $timeout, - $temp_dir, - "-c", - "cec $reference_netlist $vpr_postsynthesis_netlist;" - ); - - if ( open( CECOUT, "< $temp_dir/abc.cec.out" ) ) { - undef $/; - my $cec_content = ; - close(CECOUT); - $/ = "\n"; # Restore for normal behaviour later in script - - if ( $cec_content !~ m/(.*Networks are equivalent.*)/i ) { - $error_status = "failed: formal verification"; - $error_code = 1; - } - } else { - $error_status = "failed: no CEC output"; - $error_code = 1; - } - } elsif ( $sec_content !~ m/(.*Networks are equivalent.*)/i ) { - $error_status = "failed: formal verification"; - $error_code = 1; - } - } else { - $error_status = "failed: no DSEC output"; - $error_code = 1; - } - } - - # Do a second-run of the incremental analysis to compare the result files - if ($check_incremental_sta_consistency) { - my $cmp_result = &cmp_full_vs_incr_STA; - - if ($cmp_result ne "success") { - $error_status = $cmp_result; - $error_code = 1; - } - } - - if (! $keep_intermediate_files) - { - system "rm -f $prevpr_output_file_name"; - system "rm -f ${temp_dir}*.xml"; - system "rm -f ${temp_dir}*.sdf"; - system "rm -f ${temp_dir}*.v"; - if (! $keep_result_files) { - system "rm -f ${temp_dir}*.net"; - system "rm -f ${temp_dir}*.place"; - system "rm -f ${temp_dir}*.route"; - } - if ($do_power) { - system "rm -f $ace_output_act_path"; - } - } - } else { - $error_status = "failed: vpr ($q)"; - $error_code = 1; - } -} - -my $EndTime = time; - -# Determine running time -my $seconds = ( $EndTime - $StartTime ); -my $runseconds = $seconds % 60; - -# Start collecting results to output.txt -open( RESULTS, "> $results_path" ); - -# Output vpr status and runtime -print RESULTS "vpr_status=$q\n"; -print RESULTS "vpr_seconds=$seconds\n"; -print RESULTS "rundir=" . getcwd() . "\n"; -print RESULTS "hostname=" . hostname() . "\n"; - -# Parse VPR output -if ( open( VPROUT, "< vpr.out" ) ) { - undef $/; - my $content = ; - close(VPROUT); - $/ = "\n"; # Restore for normal behaviour later in script -} -print RESULTS "error=$error\n"; - -close(RESULTS); - -if (defined $expect_fail) { - my $old_error_status = $error_status; - - my $failure_matched = 0; - - if ($error_code != 0) { - if ($q eq $expect_fail) { - $failure_matched = 1; - } else { - $failure_matched = 0; - } - } else { #Did not fail - $failure_matched = 0; - } - - if ($failure_matched) { - #Failed as expected, invert message - $error_code = 0; - $error_status = "OK"; - if ($verbosity > 0) { - $error_status .= " (as expected " . $old_error_status . ")"; - } else { - $error_status .= "*"; - } - } else { - #Passed when expected failure - $error_status = "failed: expected to fail"; - $error_status .= " with '" . $expect_fail . "'"; - $error_status .= " but was '" . $q . "'"; - } -} - -my $elapsed_time_str = sprintf("(took %.2f seconds)", Time::HiRes::gettimeofday() - $vtr_flow_start_time); - -printf(" %-15s %19s", $error_status, $elapsed_time_str); - -print "\n"; - -exit $error_code; - -################################################################################ -# Subroutine to execute a system call with a timeout -# system_with_timeout(, , , , , , etc) -# make sure args is an array -# Returns: "timeout", "exited", "success", "crashed" -################################################################################ -sub system_with_timeout { - # Check args - ( $#_ > 2 ) or die "system_with_timeout: not enough args\n"; - if ($valgrind) { - my $program = shift @_; - unshift @_, "valgrind"; - splice @_, 4, 0, , @valgrind_args, $program; - } - # Use a memory tracker to call executable usr/bin/time - my $program = shift @_; - unshift @_, $memory_tracker; - splice @_, 4, 0, @memory_tracker_args, $program; - - if ($limit_memory_usage > 0) { - my $program = shift @_; - unshift @_, "bash"; - # flatten array - my $params = join(" ", @_[4 .. ($#_)]); - splice @_, 4, 1000, "-c", "ulimit -Sv $limit_memory_usage;" . $program . " " . $params; - } - # ( -f $_[0] ) or die "system_with_timeout: can't find executable $_[0]\n"; - ( $_[2] > 0 ) or die "system_with_timeout: invalid timeout\n"; - - #start valgrind output on new line - if ($valgrind) { - print "\n"; - } - - # Save the pid of child process - my $pid = fork; - - if ( $pid == 0 ) { - - # Redirect STDOUT for vpr - chdir $_[3]; - - - open( STDOUT, "> $_[1]" ); - open( STDERR, ">&STDOUT" ); - - # Copy the args and cut out first four - my @VPRARGS = @_; - shift @VPRARGS; - shift @VPRARGS; - shift @VPRARGS; - shift @VPRARGS; - - # Run command - # This must be an exec call and there most be no special shell characters - # like redirects so that perl will use execvp and $pid will actually be - # that of vpr so we can kill it later. - - # first strip out empty - @VPRARGS = grep { $_ ne ''} @VPRARGS; - - print "\n$_[0] @VPRARGS\n"; - exec $_[0], @VPRARGS; - } - else { - my $timed_out = "false"; - - # Register signal handler, to kill child process (SIGABRT) - $SIG{ALRM} = sub { kill 6, $pid; $timed_out = "true"; }; - - # Register handlers to take down child if we are killed (SIGHUP) - $SIG{INT} = sub { print "SIGINT\n"; kill 1, $pid; exit; }; - $SIG{HUP} = sub { print "SIGHUP\n"; kill 1, $pid; exit; }; - - # Set SIGALRM timeout - alarm $_[2]; - - # Wait for child process to end OR timeout to expire - wait; - - # Unset the alarm in case we didn't timeout - alarm 0; - - # Check if timed out or not - if ( $timed_out eq "true" ) { - return "timeout"; - } - else { - my $did_crash = "false"; - if ( $? & 127 ) { $did_crash = "true"; } - - my $return_code = $? >> 8; - - if ( $did_crash eq "true" ) { - if ($show_failures && not defined $expect_fail) { - my $abs_log_path = Cwd::abs_path($_[1]); - print "\n Failed log file follows ($abs_log_path):\n"; - cat_file($_[1], "\t> "); - } - return "crashed"; - } - elsif ( $return_code != 0 ) { - if ($show_failures && not defined $expect_fail) { - my $abs_log_path = Cwd::abs_path($_[1]); - print "\n Failed log file follows ($abs_log_path):\n"; - cat_file($_[1], "\t> "); - } - return "exited with return code $return_code"; - } - else { - return "success"; - } - } - } -} - -sub cat_file { - my $file = $_[0]; - my $indent = $_[1]; - - open(my $fh, "<", $file) or die "Could not open '$file'\n"; - while (my $line = <$fh>) { - print $indent . $line; - } -} - -sub stage_index { - my $stage_name = $_[0]; - - if ( lc($stage_name) eq "odin" ) { - return $stage_idx_odin; - } - if ( lc($stage_name) eq "abc" ) { - return $stage_idx_abc; - } - if ( lc($stage_name) eq "ace" ) { - return $stage_idx_ace; - } - if ( lc($stage_name) eq "prevpr" ) { - return $stage_idx_prevpr; - } - if ( lc($stage_name) eq "vpr" ) { - return $stage_idx_vpr; - } - return -1; -} - -sub file_ext_for_stage { - my $stage_idx = $_[0]; - my $circuit_suffix = $_[1]; - - my $vpr_netlist_suffix = ".blif"; - if ($circuit_suffix eq ".eblif") { - $vpr_netlist_suffix = $circuit_suffix; - } - - if ( $stage_idx == 0 ) { - return ".v"; - } - elsif ( $stage_idx == $stage_idx_odin ) { - return ".odin.blif"; - } - elsif ( $stage_idx == $stage_idx_abc ) { - return ".abc.blif"; - } - elsif ( $stage_idx == $stage_idx_ace ) { - return ".ace.blif"; - } - elsif ( $stage_idx == $stage_idx_prevpr ) { - return ".pre-vpr" . $vpr_netlist_suffix; - } -} - -sub expand_user_path { - my $str = shift; - $str =~ s/^~\//$ENV{"HOME"}\//; - return $str; -} - -sub file_find_and_replace { - my $file_path = shift(); - my $search_string = shift(); - my $replace_string = shift(); - - open( FILE_IN, "$file_path" ); - my $file_contents = do { local $/; }; - close(FILE_IN); - - $file_contents =~ s/$search_string/$replace_string/mg; - - open( FILE_OUT, ">$file_path" ); - print FILE_OUT $file_contents; - close(FILE_OUT); -} - -sub xml_find_key { - my $tree = shift(); - my $key = shift(); - - foreach my $subtree ( keys %{$tree} ) { - if ( $subtree eq $key ) { - return $tree->{$subtree}; - } - } - return ""; -} - -sub xml_find_child_by_key_value { - my $tree = shift(); - my $key = shift(); - my $val = shift(); - - if ( ref($tree) eq "HASH" ) { - - # Only a single item in the child array - if ( $tree->{$key} eq $val ) { - return $tree; - } - } - elsif ( ref($tree) eq "ARRAY" ) { - - # Child Array - foreach my $child (@$tree) { - if ( $child->{$key} eq $val ) { - return $child; - } - } - } - - return ""; -} - -sub xml_find_LUT_Kvalue { - my $tree = shift(); - - #Check if this is a LUT - if ( xml_find_key( $tree, "-blif_model" ) eq ".names" ) { - return $tree->{input}->{"-num_pins"}; - } - - my $max = 0; - my $val = 0; - - foreach my $subtree ( keys %{$tree} ) { - my $child = $tree->{$subtree}; - - if ( ref($child) eq "ARRAY" ) { - foreach my $item (@$child) { - $val = xml_find_LUT_Kvalue($item); - if ( $val > $max ) { - $max = $val; - } - } - } - elsif ( ref($child) eq "HASH" ) { - $val = xml_find_LUT_Kvalue($child); - if ( $val > $max ) { - $max = $val; - } - } - else { - - # Leaf - do nothing - } - } - - return $max; -} - -sub xml_find_mem_size_recursive { - my $tree = shift(); - - #Check if this is a Memory - if ( xml_find_key( $tree, "-blif_model" ) =~ "port_ram" ) { - my $input_pins = $tree->{input}; - foreach my $input_pin (@$input_pins) { - if ( xml_find_key( $input_pin, "-name" ) =~ "addr" ) { - return $input_pin->{"-num_pins"}; - } - } - return 0; - } - - # Otherwise iterate down - my $max = 0; - my $val = 0; - - foreach my $subtree ( keys %{$tree} ) { - my $child = $tree->{$subtree}; - - if ( ref($child) eq "ARRAY" ) { - foreach my $item (@$child) { - $val = xml_find_mem_size_recursive($item); - if ( $val > $max ) { - $max = $val; - } - } - } - elsif ( ref($child) eq "HASH" ) { - $val = xml_find_mem_size_recursive($child); - if ( $val > $max ) { - $max = $val; - } - } - else { - - # Leaf - do nothing - } - } - - return $max; -} - -sub xml_find_mem_size { - my $tree = shift(); - - my $pb_tree = $tree->{architecture}->{complexblocklist}->{pb_type}; - if ( $pb_tree eq "" ) { - return ""; - } - - my $memory_pb = xml_find_child_by_key_value ($pb_tree, "-name", "memory"); - if ( $memory_pb eq "" ) { - return ""; - } - - return xml_find_mem_size_recursive($memory_pb); -} - -sub find_postsynthesis_netlist { - my $file_name = $_; - if ($file_name =~ /_post_synthesis\.blif/) { - $vpr_postsynthesis_netlist = $file_name; - return; - } -} - -# Returns the executable extension based on the platform -sub exe_for_platform { - my $file_name = shift(); - if ($^O eq "cygwin" or $^O eq "MSWIN32") { - $file_name = $file_name . ".exe"; - } - return $file_name; -} - -sub get_clocks { - my $filename = shift(); - - open my $fh, $filename or die "Cannot open $filename: $!\n"; - - my @clocks = <$fh>; - - return @clocks; -} - -sub run_vpr { - my ($args) = @_; - - my @vpr_args; - push(@vpr_args, $args->{arch_name}); - push(@vpr_args, $args->{circuit_name}); - push(@vpr_args, "--circuit_file" ); - push(@vpr_args, $args->{circuit_file}); - - if (defined $args->{sdc_file}){ - push(@vpr_args, "--sdc_file" ); - push(@vpr_args, $args->{sdc_file}); - } - - if (defined $args->{pad_file}){ - push(@vpr_args, "--fix_pins" ); - push(@vpr_args, $args->{pad_file}); - } - - #Additional VPR arguments - my @extra_vpr_args = @{$args->{extra_vpr_args}}; - if (defined $args->{extra_vpr_args} && scalar(@extra_vpr_args) > 0) { - push(@vpr_args, @extra_vpr_args); - } - - #Extra options to fine-tune LeakSanitizer (LSAN) behaviour. - #Note that if VPR was compiled without LSAN these have no effect - # 'suppressions=...' Add the LeakSanitizer (LSAN) suppression file - # 'exitcode=12' Use a consistent exitcode (on some systems LSAN don't use the default exit code of 23) - # 'fast_unwind_on_malloc=0' Provide more accurate leak stack traces - local $ENV{"LSAN_OPTIONS"} = "suppressions=$vtr_flow_path/../vpr/lsan.supp exitcode=23 fast_unwind_on_malloc=0"; - - #Run the command - $q = &system_with_timeout( - $vpr_path, $args->{log_file}, - $timeout, $temp_dir, @vpr_args - ); - - return $q; -} - -sub parse_min_W { - my ($log_file) = @_; - - my $min_W = -1; - # Parse out min_chan_width - if ( open( VPROUT, $log_file) ) { - undef $/; - my $content = ; - close(VPROUT); - $/ = "\n"; # Restore for normal behaviour later in script - - if ( $content =~ /Best routing used a channel width factor of (\d+)/m ) { - $min_W = $1; - } - } - - return $min_W; -} - -sub calculate_relaxed_W { - my ($min_W, $relax_W_factor) = @_; - - my $relaxed_W = $min_W * $relax_W_factor; - $relaxed_W = floor($relaxed_W); - $relaxed_W += $relaxed_W % 2; - - return $relaxed_W; -} - -sub cmp_full_vs_incr_STA { - #Sanity check that full STA and the incremental STA - #produce the same *.net, *.place, *.route files - #as well as identical timing report files - - my @default_output_filenames = ( - "$benchmark_name" . ".net", - "$benchmark_name" . ".place", - "$benchmark_name" . ".route", - "report_timing.setup.rpt", - "report_timing.hold.rpt", - "report_unconstrained_timing.setup.rpt", - "report_unconstrained_timing.hold.rpt" - ); - - # The full STA flow should have already been run - # directly rename the output files - foreach my $filename (@default_output_filenames) { - system_with_timeout( - $move_exec, "move.out", - $timeout, $temp_dir, - $filename, "full_sta_" . $filename - ); - } - - # run incremental STA flow - my @incremental_vpr_args = @forwarded_vpr_args; - push(@incremental_vpr_args, ("--timing_update_type", "incremental")); - my $fixed_W_log_file = "vpr.incr_sta.out"; - - $q = run_vpr({ - arch_name => $architecture_file_name, - circuit_name => $benchmark_name, - circuit_file => $prevpr_output_file_name, - sdc_file => $sdc_file_path, - pad_file => $pad_file_path, - extra_vpr_args => \@incremental_vpr_args, - log_file => $fixed_W_log_file, - }); - - # Rename the incremental STA output files - foreach my $filename (@default_output_filenames) { - system_with_timeout( - $move_exec, "move.out", - $timeout, $temp_dir, - $filename, "incremental_sta_" . $filename - ); - } - - # Run diff command on the two sets of outputs - my $failed_msg = "Failed with these files (not identical):"; - my $identical = 1; - - foreach my $filename (@default_output_filenames) { - my $diff_result = &system_with_timeout( - $diff_exec, "diff.out", - $timeout, $temp_dir, - "full_sta_" . $filename, "incremental_sta_" . $filename - ); - - # If different, set the flag and append filename to the error message - if ($diff_result ne "success") { - $identical = 0; - $failed_msg = $failed_msg . " " . $filename; - } - } - - return $identical ? "success" : $failed_msg; -} - diff --git a/vtr_flow/scripts/run_vtr_task.pl b/vtr_flow/scripts/run_vtr_task.pl deleted file mode 100755 index 101d546a4b0..00000000000 --- a/vtr_flow/scripts/run_vtr_task.pl +++ /dev/null @@ -1,967 +0,0 @@ -#!/usr/bin/env perl -################################################################################### -# This script executes one or more VTR tasks -# -# Usage: -# run_vtr_task.pl ... [OPTIONS] -# -# Options: -# -s : Treat the remaining command line options as parameters -# to forward to the VPR calling script (e.g. run_vtr_flow.pl). -# -p : Perform parallel execution using N threads. Note: Large benchmarks -# will use very large amounts of memory (several gigabytes). Because -# of this, parallel execution often saturates the physical memory, -# requiring the use of swap memory, which will cause slower -# execution. Be sure you have allocated a sufficiently large swap -# memory or errors may result. -# -j : Same as -p -# -l : A file containing a list of tasks to execute. Each task -# name should be on a separate line. -# -# -hide_runtime: Do not show runtime estimates -# -# Note: At least one task must be specified, either directly as a parameter or -# through the -l option. -# -# Authors: Jason Luu and Jeff Goeders -# -################################################################################### - -use strict; -use warnings; - -# This loads the thread libraries, but within an eval so that if they are not available -# the script will not fail. If successful, $threaded = 1 -my $threaded = eval 'use threads; use Thread::Queue; 1'; - -use Cwd; -use File::Spec; -use File::Basename; -use File::Path qw(make_path); -use POSIX qw(strftime); -use Time::HiRes qw(time); - -# Function Prototypes -sub trim; -sub run_single_task; -sub do_work; -sub get_result_file_metrics; -sub ret_expected_runtime; -sub ret_expected_memory; -sub ret_expected_min_W; -sub ret_expected_vpr_status; - -sub format_human_readable_time; -sub format_human_readable_memory; -sub uniq; - -my $start_time = time(); - -# Get Absolute Path of 'vtr_flow -Cwd::abs_path($0) =~ m/(.*vtr_flow)/; -my $vtr_flow_path = $1; - -my @tasks; -my @task_files; -my $token; -my $processors = 1; -my $run_prefix = "run"; -my $show_runtime_estimates = 1; -my $system_type = "local"; -my $script = "run_vtr_flow.py"; -my $shared_script_params = ""; -my $verbosity = 0; -my $short_task_names = 0; -my $minw_hint_factor = 1.; -my $show_failures = 0; - -# Parse Input Arguments -while ( $token = shift(@ARGV) ) { - - # Check for -pN - if ( $token =~ /^-p(\d+)$/ ) { - $processors = int($1); - } - - # Check for -jN - if ( $token =~ /^-j(\d+)$/ ) { - $processors = int($1); - } - - # Check for -p N or -j N - elsif ( $token eq "-p" or $token eq "-j" ) { - $processors = int( shift(@ARGV) ); - } - - elsif ( $token eq "-verbosity") { - $verbosity = int( shift(@ARGV) ); - } - - elsif ( $token eq "-script") { - $script = shift(@ARGV); - } - - # Treat the remainder of the command line options as script parameters shared by all tasks - elsif ( $token eq "-s" ) { - $shared_script_params = join(' ', @ARGV); - while ($token = shift(@ARGV)) { - print "adding to shared script params: $token\n" - } - print "shared script params: $shared_script_params\n" - } - - - elsif ( $token eq "-system" ) { - $system_type = shift(@ARGV); - } - - # Check for a task list file - elsif ( $token =~ /^-l(.+)$/ ) { - push( @task_files, expand_user_path($1) ); - } - elsif ( $token eq "-l" ) { - push( @task_files, expand_user_path( shift(@ARGV) ) ); - } - - elsif ( $token eq "-hide_runtime" ) { - $show_runtime_estimates = 0; - } - elsif ( $token eq "-short_task_names" ) { - $short_task_names = 1; - } - elsif ( $token eq "-show_failures" ) { - $show_failures = 1; - } - - elsif ( $token eq "-minw_hint_factor" ) { - $minw_hint_factor = shift(@ARGV); - } - - elsif ( $token =~ /^-/ ) { - die "Invalid option: $token\n"; - } - - # must be a task name - else { - if ( $token =~ /(.*)\/$/ ) { - $token = $1; - } - push( @tasks, $token ); - } -} - -# Check threaded -if ( $processors > 1 and not $threaded ) { - print - "Multithreaded option specified, but is not supported by this version of perl. Execution will be single threaded.\n"; - $processors = 1; -} - -# Read Task Files -foreach (@task_files) { - open( FH, $_ ) or die "$! ($_)\n"; - while () { - push( @tasks, $_ ); - } - close(FH); -} - -# Remove duplicate tasks, use uniq() to preserve ordering -@tasks = uniq(@tasks); - -#print "Processors: $processors\n"; -#print "Tasks: @tasks\n"; - -if ( $#tasks == -1 ) { - die "\n" - . "Incorrect usage. You must specify at least one task to execute\n" - . "\n" - . "USAGE:\n" - . "run_vtr_task.pl ... \n" . "\n" - . "OPTIONS:\n" - . "-l - Provides a text file with a list of tasks\n" - . "-p - Execution is performed in parallel using N threads (Default: 1)\n"; -} - -############################################################## -# Run tasks -############################################################## - -my $common_task_prefix = ""; - -if ($short_task_names) { - $common_task_prefix = find_common_task_prefix(\@tasks); -} - -#Collect the actions for all tasks -my @all_task_actions; -foreach my $task (@tasks) { - chomp($task); - my $task_actions = generate_single_task_actions($task, $common_task_prefix, $script); - push(@all_task_actions, @$task_actions); -} - -#Run all the actions (potentially in parallel) -my $num_total_failures = run_actions(\@all_task_actions); - -my $elapsed_time = time() - $start_time; -printf("Elapsed time: %.1f seconds\n", $elapsed_time); -exit $num_total_failures; - -############################################################## -# Subroutines -############################################################## - -sub generate_single_task_actions { - my $circuits_dir; - my $archs_dir; - my $sdc_dir = undef; - my $script_default = "run_vtr_flow"; - my $script_path; - my $script_params_common = $shared_script_params; # start with the shared ones then build unique ones - my @circuits_list; - my @archs_list; - my @script_params_list; - my $cmos_tech_path = ""; - - my ($task, $common_prefix,$script) = @_; - (my $task_dir = "$vtr_flow_path/tasks/$task") =~ s/\s+$//; # trim right white spaces for chdir to work on Windows - chdir($task_dir) or die "Task directory does not exist ($task_dir): $!\n"; - - # Get Task Config Info - - my $task_config_file_path = "config/config.txt"; - open( CONFIG_FH, $task_config_file_path ) - or die - "Cannot find task configuration file ($task_dir/$task_config_file_path)"; - while () { - my $line = $_; - chomp($line); - - #Skip comment-only or blank lines - if ( $line =~ /^\s*#.*$/ or $line =~ /^\s*$/ ) { next; } - - #Trim off a line-ending comment - $line =~ s/#.*$//; - - my @data = split( /=/, $line ); - my $key = trim( $data[0] ); - my $value = undef; - - if (scalar @data > 1) { #Key may have no value - $value = trim( $data[1] ); - } - - if (not defined $value) { - next; #Currently ignore values with no keys - } - - if ( $key eq "circuits_dir" ) { - $circuits_dir = $value; - } elsif ( $key eq "archs_dir" ) { - $archs_dir = $value; - } elsif ( $key eq "sdc_dir" ) { - $sdc_dir = $value; - } elsif ( $key eq "circuit_list_add" ) { - push( @circuits_list, $value ); - } elsif ( $key eq "arch_list_add" ) { - push( @archs_list, $value ); - } elsif ( $key eq "script_params_list_add" ) { - push( @script_params_list, $value ); - } elsif ( $key eq "script_path" ) { - $script = $value; - } elsif ( $key eq "script_params" || $key eq "script_params_common") { - $script_params_common .= ' ' . $value; - } elsif ( $key eq "cmos_tech_behavior" ) { - $cmos_tech_path = $value; - } elsif ($key eq "parse_file" - or $key eq "qor_parse_file" - or $key eq "pass_requirements_file" ) - { - - #Used by parser - } - else { - die "Invalid option (" . $key . ") in configuration file."; - } - } - close(CONFIG_FH); - - # Using default script - if (index($script, $script_default) != -1 ) { - - # This is hack to automatically add the option '-temp_dir .' if using the run_vtr_flow.pl script - # This ensures that a 'temp' folder is not created in each circuit directory - if ( !( $script_params_common =~ /-temp_dir/ ) ) { - #-temp_dir must come before the script_params, so that it gets picked up by run_vtr_flow - # and not passed on as an argument to a tool (e.g. VPR) - $script_params_common = " -temp_dir . " . $script_params_common; - } - } - else { - $show_runtime_estimates = 0; - } - - $circuits_dir = expand_user_path($circuits_dir); - $archs_dir = expand_user_path($archs_dir); - - if ( -d "$vtr_flow_path/$circuits_dir" ) { - $circuits_dir = "$vtr_flow_path/$circuits_dir"; - } - elsif ( -d $circuits_dir ) { - } - else { - die "Circuits directory not found ($circuits_dir)"; - } - - if ( -d "$vtr_flow_path/$archs_dir" ) { - $archs_dir = "$vtr_flow_path/$archs_dir"; - } - elsif ( -d $archs_dir ) { - } - else { - die "Archs directory not found ($archs_dir)"; - } - - - if (defined $sdc_dir) { - $sdc_dir = expand_user_path($sdc_dir); - if ( -d "$vtr_flow_path/$sdc_dir" ) { - $sdc_dir = "$vtr_flow_path/$sdc_dir"; - } - elsif ( -d $sdc_dir ) { - } - else { - die "SDC directory not found ($sdc_dir)"; - } - } - - (@circuits_list) or die "No circuits specified for task $task"; - (@archs_list) or die "No architectures specified for task $task"; - - if (!@script_params_list) { - #Add a default empty param if none otherwise specified - #(i.e. only base params) - push(@script_params_list, ""); - } - - my @circuit_dups = find_duplicates(\@circuits_list); - if (@circuit_dups) { - die "Duplicate circuits specified for task $task '@circuit_dups'" - } - my @arch_dups = find_duplicates(\@archs_list); - if (@arch_dups) { - die "Duplicate architectures specified for task $task '@arch_dups'"; - } - my @script_params_dups = find_duplicates(\@script_params_list); - if (@script_params_dups) { - die "Duplicate script parameters specified for task $task '@script_params_dups'"; - } - - # Check script - $script = expand_user_path($script); - if ( -e "$task_dir/config/$script" ) { - $script_path = "$task_dir/config/$script"; - } - elsif ( -e "$vtr_flow_path/scripts/$script" ) { - $script_path = "$vtr_flow_path/scripts/$script"; - } - elsif ( -e $script ) { - } - else { - die - "Cannot find script for task $task ($script). Looked for $task_dir/config/$script or $vtr_flow_path/scripts/$script"; - } - - # Check architectures - foreach my $arch (@archs_list) { - (-f "$archs_dir/$arch") or die "Architecture file not found ($archs_dir/$arch)"; - } - - # Check circuits - foreach my $circuit (@circuits_list) { - (-f "$circuits_dir/$circuit") or die "Circuit file not found ($circuits_dir/$circuit)"; - } - - # Check CMOS tech behavior - if ( $cmos_tech_path ne "" ) { - $cmos_tech_path = expand_user_path($cmos_tech_path); - if ( -e "$task_dir/config/$cmos_tech_path" ) { - $cmos_tech_path = "$task_dir/config/$cmos_tech_path"; - } - elsif ( -e "$vtr_flow_path/tech/$cmos_tech_path" ) { - $cmos_tech_path = "$vtr_flow_path/tech/$cmos_tech_path"; - } - elsif ( -e $cmos_tech_path ) { - } - else { - die - "Cannot find CMOS technology behavior file for $task ($script). Looked for $task_dir/config/$cmos_tech_path or $vtr_flow_path/tech/$cmos_tech_path"; - } - $script_params_common .= " -cmos_tech $cmos_tech_path"; - } - - my $task_name = $task; - $task =~ s/^$common_prefix//; - - # Check if golden file exists - my $golden_results_file = "$task_dir/config/golden_results.txt"; - - ############################################################## - # Create a new experiment directory to run experiment in - # Counts up until directory number doesn't exist - ############################################################## - my $experiment_number = 0; - - my $run_dir = ""; - my $run_dir_no_prefix = ""; - do { - $experiment_number += 1; - $run_dir = sprintf("run%03d", $experiment_number); - $run_dir_no_prefix = sprintf("run%d", $experiment_number); - } while (-e $run_dir or -e $run_dir_no_prefix); - - #Create the run directory - mkdir( $run_dir, 0775 ) or die "Failed to make directory ($run_dir): $!"; - chmod( 0775, $run_dir ); - - #Create a symlink that points to the latest run - my $symlink_name = "latest"; - if (-l $symlink_name) { - unlink $symlink_name; #Remove link if it exists - } - symlink($run_dir, $symlink_name) or die "Failed to make symlynk $symlink_name to $run_dir: $!"; - - #Move to the run directory - chdir($run_dir) or die "Failed to change to directory ($run_dir): $!"; - - - ############################################################## - # Build up the list of commands to run - ############################################################## - my @actions; - foreach my $circuit (@circuits_list) { - foreach my $arch (@archs_list) { - foreach my $params (@script_params_list) { - - my $full_params = $script_params_common; - my $full_params_dirname = "common"; - if ($params ne "") { - $full_params .= " " . $params; - $full_params_dirname .= "_" . $params; - $full_params_dirname =~ s/ /_/g; #Convert spaces to underscores for directory name - } - - #Determine the directory where to run - my $dir = "$task_dir/$run_dir/${arch}/${circuit}/${full_params_dirname}"; - - my $name = sprintf("'%-25s %-54s'", "${task}:", "${arch}/${circuit}/${full_params_dirname}"); - - #Do we expect a specific status? - my $expect_fail = ""; - my $expected_vpr_status = ret_expected_vpr_status($circuit, $arch, $full_params_dirname, $golden_results_file); - if ($expected_vpr_status ne "success" and $expected_vpr_status ne "Unkown") { - $expect_fail = "-expect_fail '$expected_vpr_status'"; - } - - my $show_failure_args = ""; - if ($show_failures) { - $show_failure_args = "-show_failures"; - } - - my $sdc_arg = ""; - if ($sdc_dir) { - (my $name, my $dir, my $ext) = fileparse($circuit, "\.[^.]*"); - $sdc_arg = "-sdc_file $sdc_dir/$name.sdc" - } - - #Build the command to run - my $command = "$script_path $circuits_dir/$circuit $archs_dir/$arch $sdc_arg $expect_fail -name $name $show_failure_args $full_params"; - - #Add a hint about the minimum channel width (potentially saves run-time) - my $expected_min_W = ret_expected_min_W($circuit, $arch, $full_params_dirname, $golden_results_file); - $expected_min_W = int($expected_min_W * $minw_hint_factor); - $expected_min_W += ($expected_min_W % 2); - if($expected_min_W > 0) { - $command .= " --min_route_chan_width_hint $expected_min_W"; - } - - #Estimate runtime - my $runtime_estimate = ret_expected_runtime($circuit, $arch, $full_params_dirname, $golden_results_file); - my $memory_estimate = ret_expected_memory($circuit, $arch, $full_params_dirname, $golden_results_file); - - my $run_script_file = create_run_script({ - dir => $dir, - command => $command, - runtime_estimate => $runtime_estimate, - memory_estimate => $memory_estimate - }); - - my @action = [$dir, $run_script_file, $runtime_estimate, $memory_estimate]; - push(@actions, @action); - } - } - } - - return \@actions; -} - -sub run_actions { - my ($actions) = @_; - - my $num_failures = 0; - - if ( $system_type eq "local" ) { - my $thread_work = Thread::Queue->new(); - my $thread_result = Thread::Queue->new(); - my $thread_return_code = Thread::Queue->new(); - my $threads = $processors; - - foreach my $action (@$actions) { - my ($run_dir, $command, $runtime_estimate, $memory_estimate) = @$action; - - if ($verbosity > 0) { - print "$command\n"; - } - - $thread_work->enqueue("$run_dir||||$command||||$runtime_estimate||||$memory_estimate"); - - } - - my @pool = map { threads->create( \&do_work, $thread_work, $thread_result, $thread_return_code ) } 1 .. $threads; - - #Each thread puts an 'undef' into it's output queues when it is finished (no more work to do) - # - #To ensure we get *all* the results we count the number of undef's recieved and ensure they match - #the number of threads before moving on. - # - #This is done for bothe the result (i.e. stdout) queue and return code queue - my $undef_result_count = 0; - while ($undef_result_count < $threads) { - my $result = $thread_result->dequeue(); - - if (defined $result) { - #Valid output - chomp($result); - print "$result\n"; - } else { - #One thread finished - $undef_result_count += 1; - } - } - die("Thread result queue was non-empty (before join)") if ($thread_result->pending() != 0); - - my $undef_return_code_count = 0; - while ($undef_return_code_count < $threads) { - my $return_code = $thread_return_code->dequeue(); - - if (defined $return_code) { - #Valid return code - if ($return_code != 0) { - $num_failures += 1; - } - } else { - #One thread finished - $undef_return_code_count += 1; - } - } - die("Thread result queue was non-empty (before join)") if ($thread_return_code->pending() != 0); - - #Wait on the threads (Note: should already be finished) - $_->join for @pool; - - #Since we joined the threads after they had already finished, - #there should be nothing left in the result queues at this point. - die("Thread result queue was non-empty (after join)") if ($thread_result->pending() != 0); - die("Thread return code queue was non-empty (after join)") if ($thread_return_code->pending() != 0); - - } elsif ( $system_type eq "scripts" ) { - foreach my $action (@$actions) { - my ($run_dir, $command, $runtime_estimate, $memory_estimate) = @$action; - print "$command\n"; - } - } else { - die("Unrecognized job system '$system_type'"); - } - - return $num_failures; -} - -sub do_work { - my ( $work_queue, $return_queue, $return_code_queue ) = @_; - my $tid = threads->tid; - - while (1) { - my $work_str = $work_queue->dequeue_nb(); - - if (!defined $work_str) { - #Work queue was empty, nothing left to do - last; - } - - my ($dir, $command, $runtime_estimate, $memory_estimate) = split( /\|\|\|\|/, $work_str ); - - my $return_status = system "cd $dir; $command > vtr_flow.out"; - my $exit_code = $return_status >> 8; #Shift to get real exit code - - open( OUT_FILE, "$dir/vtr_flow.out" ) or die "Cannot open $dir/vtr_flow.out: $!"; - my $sys_output = do { local $/; }; - - $return_queue->enqueue($sys_output); - - $return_code_queue->enqueue($exit_code); - } - #We indicate that a thread has finished by putting an undef in the queue - $return_queue->enqueue(undef); - $return_code_queue->enqueue(undef); -} - -sub create_run_script { - my ($args) = @_; - - my $dir = $args->{dir}; - my $runtime_est = $args->{runtime_estimate}; - my $memory_est = $args->{memory_estimate}; - - if ($runtime_est < 0) { - $runtime_est = 0; - } - - if ($memory_est < 0) { - $memory_est = 0; - } - - my $humanreadable_runtime_est = format_human_readable_time($runtime_est); - my $humanreadable_memory_est = format_human_readable_memory($memory_est); - - make_path( "$dir", { mode => 0775 } ) or die "Failed to create directory ($dir): $!"; - - my $run_script_file = "$dir/vtr_flow.sh"; - - open(my $fh, '>', $run_script_file); - - print $fh "#!/usr/bin/env bash\n"; - print $fh "\n"; - print $fh "VTR_RUNTIME_ESTIMATE_SECONDS=$runtime_est\n"; - print $fh "VTR_MEMORY_ESTIMATE_BYTES=$memory_est\n"; - print $fh "\n"; - print $fh "VTR_RUNTIME_ESTIMATE_HUMAN_READABLE=\"$humanreadable_runtime_est\"\n"; - print $fh "VTR_MEMORY_ESTIMATE_HUMAN_READABLE=\"$humanreadable_memory_est\"\n"; - print $fh "\n"; - print $fh "#We redirect all command output to both stdout and the log file with 'tee'.\n"; - print $fh "\n"; - print $fh "#Begin I/O redirection\n"; - print $fh "{\n"; - print $fh "\n"; - print $fh " $args->{command}\n"; - print $fh "\n"; - print $fh " #The IO redirection occurs in a sub-shell,\n"; - print $fh " #so we need to exit it with the correct code\n"; - print $fh " exit \$?\n"; - print $fh "\n"; - print $fh "} |& tee vtr_flow.out\n"; - print $fh "#End I/O redirection\n"; - print $fh "\n"; - print $fh "#We used a pipe to redirect IO.\n"; - print $fh "#To get the correct exit status we need to exit with the\n"; - print $fh "#status of the first element in the pipeline (i.e. the real\n"; - print $fh "#command run above)\n"; - print $fh "exit \${PIPESTATUS[0]}\n"; - - close($fh); - - #Make executable - chmod 0775, $run_script_file; - - return $run_script_file; -} - -# trim leading and trailing whitespace -sub trim { - my ($string) = @_; - $string =~ s/^\s+//; - $string =~ s/\s+$//; - return $string; -} - -sub expand_user_path { - my ($str) = @_; - $str =~ s/^~\//$ENV{"HOME"}\//; - return $str; -} - -#Returns a hash mapping the name to column index in the results file -sub get_result_file_keys { - my ($results_file_path) = @_; - - my %index; - - if ( -r $results_file_path) { - - #Load the results file - open( RESULTS, $results_file_path ); - my @lines = ; - close(RESULTS); - - my $header_line = shift(@lines); - my @headers = map(trim($_), split( /\t/, $header_line )); - - #Build hash look-up from name to index - @index{@headers} = ( 0 .. $#headers ); - } - - return %index; -} - -#Returns a hash corresponding to the first row from a parse_results.txt/golden_results.txt file -#which matches the given set of keys. -# -#If none is found, returns an empty hash -sub get_result_file_metrics { - my ($results_file_path, $keys_ref) = @_; - my %keys = %{$keys_ref}; - - my %metrics; - - if ( -r $results_file_path) { - - my %index = get_result_file_keys($results_file_path); - - #Skip checking for script_params if not included in the results file - # - #Note that this is a temporary work-around since not all golden results have been - #updated to record the script params - # - #TODO: Once all golden results updated, remove this check to unconditionally - #check for script params - if (not exists $index{'script_params'}) { - delete $keys{'script_params'}; - } - - #Check that the required keys exist - my $missing_keys = 0; - foreach my $key (keys %keys) { - - if (not exists $index{$key}) { - $missing_keys++; - } - } - - if ($missing_keys == 0) { - - #Load the results file - open( RESULTS, $results_file_path ); - my @lines = ; - close(RESULTS); - - #Find the entry which matches the key values - my @line_array; - foreach my $line (@lines) { - @line_array = map(trim($_), split( /\t/, $line )); - - #Check all key values match - my $found = 1; - foreach my $key (keys %keys) { - my $value = @line_array[$index{$key}]; - - if ($value ne $keys{$key}) { - $found = 0; - last; - } - } - - if ($found) { - #Matching row, build hash of entry row - for my $metric_name (keys %index) { - $metrics{$metric_name} = @line_array[$index{$metric_name}]; - } - last; - } - } - } - } - - return %metrics; -} - -#Returns the expected run-time (in seconds) of the specified run, or -1 if unkown -sub ret_expected_runtime { - my $circuit_name = shift; - my $arch_name = shift; - my $script_params = shift; - my $golden_results_file_path = shift; - my $seconds = -1; - - my %keys = ( - "arch" => $arch_name, - "circuit" => $circuit_name, - "script_params" => $script_params, - ); - - my %metrics = get_result_file_metrics($golden_results_file_path, \%keys); - - if (exists $metrics{'vtr_flow_elapsed_time'}) { - $seconds = $metrics{'vtr_flow_elapsed_time'} - } - - return $seconds; -} - -#Returns the expected memory usage (in bytes) of the specified run, or -1 if unkown -sub ret_expected_memory { - my $circuit_name = shift; - my $arch_name = shift; - my $script_params = shift; - my $golden_results_file_path = shift; - - - my %keys = ( - "arch" => $arch_name, - "circuit" => $circuit_name, - "script_params" => $script_params, - ); - - my %metrics = get_result_file_metrics($golden_results_file_path, \%keys); - - #Memory use is recorded in KiB - my $memory_kib = -1; - - #Estimate the peak memory as the maximum accross all tools - foreach my $metric ('max_odin_mem', 'max_abc_mem', 'max_ace_mem', 'max_vpr_mem') { - if (exists $metrics{$metric} && $metrics{$metric} > $memory_kib) { - $memory_kib = $metrics{$metric}; - } - } - - my $memory_bytes = $memory_kib * 1024; - - return $memory_bytes; -} - -sub ret_expected_min_W { - my $circuit_name = shift; - my $arch_name = shift; - my $script_params = shift; - my $golden_results_file_path = shift; - - my %keys = ( - "arch" => $arch_name, - "circuit" => $circuit_name, - "script_params" => $script_params, - ); - - my %metrics = get_result_file_metrics($golden_results_file_path, \%keys); - - if (not exists $metrics{'min_chan_width'}) { - return -1; - } - - return $metrics{'min_chan_width'}; -} - -sub ret_expected_vpr_status { - my $circuit_name = shift; - my $arch_name = shift; - my $script_params = shift; - my $golden_results_file_path = shift; - - my %keys = ( - "arch" => $arch_name, - "circuit" => $circuit_name, - "script_params" => $script_params, - ); - - my %metrics = get_result_file_metrics($golden_results_file_path, \%keys); - - if (not exists $metrics{'vpr_status'}) { - return "Unkown"; - } - - return $metrics{'vpr_status'}; -} - -sub format_human_readable_time { - my ($seconds) = @_; - - - if ( $seconds < 60 ) { - my $str = sprintf( "%.0f seconds", $seconds ); - return $str; - } elsif ( $seconds < 3600 ) { - my $min = $seconds / 60; - my $str = sprintf( "%.0f minutes", $min ); - return $str; - } else { - my $hour = $seconds / 60 / 60; - my $str = sprintf( "%.0f hours", $hour ); - return $str; - } -} - -sub format_human_readable_memory { - my ($bytes) = @_; - - my $str = ""; - if ( $bytes < 1024 ** 3) { - $str = sprintf( "%.2f MiB", $bytes / (1024. ** 2)); - } else { - $str = sprintf( "%.2f GiB", $bytes / (1024. ** 3)); - } - - return $str; -} - -sub find_common_task_prefix { - my ($tasks) = @_; - - - - my $first_task = @$tasks[0]; - - my $min_length = length($first_task); - foreach my $task (@$tasks) { - if (length($task) < $min_length) { - $min_length = length($task); - } - } - - my $index = 0; - my $common_prefix = ""; - while ($index < $min_length) { - my $valid_prefix = 1; - for my $task (@$tasks) { - - if ($task !~ m/^$common_prefix/) { - $valid_prefix = 0; - last; - } - } - - if ($valid_prefix != 1) { - $common_prefix =~ s/.$//; #Drop last character since not common - last; - } else { - $common_prefix = substr($first_task, 0, $index); - $index += 1; - } - } - - return $common_prefix; -} - -sub find_duplicates { - my ($list) = @_; - - my @duplicates; - my %seen; - foreach my $str (@$list) { - next unless $seen{$str}++; - push(@duplicates, $str); - } - return @duplicates; -} - -sub uniq { - my %seen; - grep !$seen{$_}++, @_; -}