blob: 4366a8813474f3ec6a6eb6470289b1249c3903ba [file] [log] [blame]
#!/usr/bin/perl
###################################################################################
# This script runs the VTR flow for a single benchmark circuit and architecture
# file.
#
# Usage:
# run_vtr_flow.pl <circuit_file> <architecture_file> [OPTIONS]
#
# Parameters:
# circuit_file: Path to the input circuit file (verilog, blif, etc)
# architecture_file: Path to the architecture file (.xml)
#
# Options:
# -starting_stage <stage>: Start the VTR flow at the specified stage.
# Acceptable values: odin, abc, script, vpr.
# Default value is odin.
# -ending_stage <stage>: End the VTR flow at the specified stage. Acceptable
# values: odin, abc, script, vpr. Default value is
# vpr.
# -keep_intermediate_files: Do not delete the intermediate files.
#
# -temp_dir <dir>: Directory used for all temporary files
###################################################################################
use strict;
use Cwd 'abs_path';
use File::Spec;
use POSIX;
use File::Copy;
# check the parametes. Note PERL does not consider the script itself a parameter.
my $number_arguments = @ARGV;
if ($number_arguments < 2)
{
print("usage: run_vtr_flow.pl <circuit_file> <architecture_file> [OPTIONS]\n");
exit(-1);
}
# Get Absoluate Path of 'vtr_flow
Cwd::abs_path($0) =~ m/(.*\/vtr_flow)\//;
my $vtr_flow_path = $1;
sub stage_index;
sub file_ext_for_stage;
sub expand_user_path;
sub file_find_and_replace;
my $temp_dir = "./temp";
my $stage_idx_odin = 1;
my $stage_idx_abc = 2;
my $stage_idx_script = 3;
my $stage_idx_vpr = 4;
my $circuit_file_path = expand_user_path(shift(@ARGV));
my $architecture_file_path = expand_user_path(shift(@ARGV));
my $token;
my $starting_stage = stage_index("odin");
my $ending_stage = stage_index("vpr");
my $keep_intermediate_files = 0;
my $has_memory = 1;
my $timing_driven = "on";
my $min_chan_width = -1;
my $lut_size = -1;
my $vpr_cluster_seed_type = "";
while ($token = shift(@ARGV))
{
if ($token eq "-starting_stage")
{
$starting_stage = stage_index(shift(@ARGV));
}
elsif ($token eq "-ending_stage")
{
$ending_stage = stage_index(shift(@ARGV));
}
elsif ($token eq "-keep_intermediate_files")
{
$keep_intermediate_files = 1;
}
elsif ($token eq "-no_mem")
{
$has_memory = 0;
}
elsif ($token eq "-no_timing")
{
$timing_driven = "off";
}
elsif ($token eq "-vpr_route_chan_width")
{
$min_chan_width = shift(@ARGV);
}
elsif ($token eq "-lut_size")
{
$lut_size = shift(@ARGV);
}
elsif ($token eq "-vpr_cluster_seed_type")
{
$vpr_cluster_seed_type = shift(@ARGV);
}
elsif ($token eq "-temp_dir")
{
$temp_dir = shift(@ARGV);
}
else
{
die "Error: Invalid argument ($token)\n";
}
if ($starting_stage == -1 or $ending_stage == -1)
{
die "Error: Invalid starting/ending stage name (start $starting_stage end $ending_stage).\n";
}
}
if ($ending_stage < $starting_stage)
{
die "Error: Ending stage is before starting stage.\n";
}
if($vpr_cluster_seed_type eq "") {
if($timing_driven eq "off") {
$vpr_cluster_seed_type = "max_inputs";
} else {
$vpr_cluster_seed_type = "timing";
}
}
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 $timeout = 10*24*60*60; # 10 day execution timeout
my $results_path = "${temp_dir}output.txt";
my $error;
my $error_code = 0;
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)";
# Select executables
my $vpr_path = "$vtr_flow_path/../vpr/vpr";
(-e $vpr_path) or die "Cannot find vpr exectuable ($vpr_path)";
my $odin2_path = "$vtr_flow_path/../ODIN_II/odin_II.exe";
(-e $odin2_path) or die "Cannot find ODIN_II executable ($odin2_path)";
my $abc_path = "$vtr_flow_path/../abc_with_bb_support/abc";
(-e $abc_path) or die "Cannot find ABC executable ($abc_path)";
my $hack_fix_lines_n_latches = "$vtr_flow_path/scripts/hack_fix_lines_and_latches.pl";
(-e $hack_fix_lines_n_latches) or die "Cannot find line/latch fixing script ($hack_fix_lines_n_latches)";
# Select files
my $abc_rc_path = "$vtr_flow_path/../abc_with_bb_support/abc.rc";
(-e $abc_rc_path) or die "Cannot find ABC RC file ($abc_rc_path)";
my $clock_path = "$vtr_flow_path/benchmarks/misc/benchmark_clocks.clock";
(-e $clock_path) or die "Cannot find benchmark clock file ($clock_path)";
my $clock_path_user = "$vtr_flow_path/benchmarks/misc/user_clocks.clock";
my $odin_config_file_name = "basic_odin_config_split.xml";
my $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)";
# Get circuit name (everything up to the first '.' in the circuit file)
my ($vol, $path, $circuit_file_name) = File::Spec->splitpath( $circuit_file_path);
$circuit_file_name =~ m/(.*)[.].*?/;
my $benchmark_name = $1;
# Get architecture name
$architecture_file_path =~ m/.*\/(.*?.xml)/;
my $architecture_file_name = $1;
$architecture_file_name =~ m/(.*).xml$/;
my $architecture_name = $1;
print "$architecture_name/$benchmark_name...";
# Get Memory Size
my $mem_size = -1;
my $line;
my $in_memory_block;
my $in_mode;
open(ARCH_IN_FILE, $architecture_file_path);
my $arch_contents = do { local $/; <ARCH_IN_FILE> };
close(ARCH_IN_FILE);
if($has_memory == 1)
{
open(ARCH_IN_FILE, $architecture_file_path);
while (<ARCH_IN_FILE>)
{
$line = $_;
chomp($line);
if ($line =~ m/pb_type name="memory"/)
{
$in_memory_block = 1;
}
elsif ($in_memory_block && !$in_mode && $line =~ m/<\/pb_type>/)
{
$in_memory_block = 0;
}
if ($in_memory_block && $line =~ m/<mode.*?>/)
{
$in_mode = 1;
}
elsif ($in_memory_block && $in_mode && $line =~ m/<\/mode>/)
{
$in_mode = 0;
}
if ($in_memory_block && $in_mode)
{
if ($line =~ m/<input name="addr" num_pins="(\d+)"/)
{
if (int($1) > $mem_size)
{
$mem_size = int($1)
}
}
}
}
if ($mem_size < 0)
{
print "failed: cannot determine arch mem size";
$error_code = 1;
}
close(ARCH_IN_FILE);
} else {
$mem_size = 0;
}
# Get lut size
if (! $error_code)
{
if($lut_size < 0) {
if ($arch_contents =~ m/pb_type name="clb".*?pb_type name="ble".*?input name="in" num_pins="(\d+)"/s)
{
$lut_size = $1;
}
else
{
print "failed: cannot determine arch LUT k-value";
$error_code = 1;
}
}
}
my $odin_output_file_name = "$benchmark_name" . file_ext_for_stage($stage_idx_odin);
my $odin_output_file_path = "$temp_dir$odin_output_file_name";
my $abc_output_file_name = "$benchmark_name" . file_ext_for_stage($stage_idx_abc);
my $abc_output_file_path = "$temp_dir$abc_output_file_name";
my $scripts_output_file_name;
if($starting_stage == stage_index("vpr"))
{
$scripts_output_file_name = "$benchmark_name" . ".blif";
} else {
$scripts_output_file_name = "$benchmark_name" . file_ext_for_stage($stage_idx_script);
}
my $scripts_output_file_path = "$temp_dir$scripts_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";
#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"
my $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 $architecture_file_path_new = "$temp_dir$architecture_file_name";
copy ($architecture_file_path, $architecture_file_path_new);
$architecture_file_path = $architecture_file_path_new;
my $circuit_file_path_new = "$temp_dir$circuit_file_name";
copy ($circuit_file_path, $circuit_file_path_new);
$circuit_file_path = $circuit_file_path_new;
copy ($abc_rc_path, $temp_dir);
# 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);
if (! $error_code)
{
$q = &system_with_timeout("$odin2_path",
"odin.out",
$timeout,
$temp_dir,
"-c", $odin_config_file_name
);
if (-e $odin_output_file_path)
{
if (! $keep_intermediate_files)
{
system "rm -f ${temp_dir}*.dot";
system "rm -f ${temp_dir}*.v";
system "rm -f $odin_config_file_path";
}
}
else
{
print "failed: odin";
$error_code = 1;
}
}
}
#################################################################################
################################## ABC ##########################################
#################################################################################
if ($starting_stage <= $stage_idx_abc and $ending_stage >= $stage_idx_abc and ! $error_code)
{
$q = &system_with_timeout($abc_path,
"abc.out",
$timeout,
$temp_dir,
"-c",
"read $odin_output_file_name; time; resyn; resyn2; if -K $lut_size -a; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; scleanup; time; write_hie $odin_output_file_name $abc_output_file_name; print_stats"
);
if (-e $abc_output_file_path)
{
#system "rm -f abc.out";
if (! $keep_intermediate_files)
{
system "rm -f $odin_output_file_path";
system "rm -f ${temp_dir}*.rc";
}
}
else
{
print "failed: abc";
$error_code = 1;
}
}
#################################################################################
################################## FIX SCRIPTS ##################################
#################################################################################
if ($starting_stage <= $stage_idx_script and $ending_stage >= $stage_idx_script and ! $error_code)
{
my $scripts_success = 0;
my $stage_output_file = "${temp_dir}fix_scripts.out";
system "$hack_fix_lines_n_latches $abc_output_file_path $scripts_output_file_path $clock_path $clock_path_user > $stage_output_file";
if (-r $scripts_output_file_path)
{
open(OUTPUT_FILE, $stage_output_file);
my $file_contents = do { local $/; <OUTPUT_FILE> };
close(OUTPUT_FILE);
if ($file_contents =~ /^Success$/m)
{
$scripts_success = 1;
}
}
if ($scripts_success)
{
if (! $keep_intermediate_files)
{
system "rm -f $abc_output_file_path";
}
}
else
{
print "failed: scripts";
$error_code = 1;
}
}
#################################################################################
################################## VPR ##########################################
#################################################################################
if ($ending_stage >= $stage_idx_vpr and ! $error_code)
{
if($min_chan_width < 0) {
$q = &system_with_timeout($vpr_path,
"vpr.out",
$timeout,
$temp_dir,
$architecture_file_name,
"$benchmark_name",
"--blif_file", "$scripts_output_file_name",
"--timing_analysis", "$timing_driven",
"--timing_driven_clustering", "$timing_driven",
"--cluster_seed_type", "$vpr_cluster_seed_type",
"--nodisp"
);
if($timing_driven eq "on") {
# Critical path delay is nonsensical at minimum channel width because congestion constraints completely 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
# Parse out min_chan_width
if(open(VPROUT, "<${temp_dir}vpr.out"))
{
undef $/;
my $content = <VPROUT>;
close (VPROUT);
$/ = "\n"; # Restore for normal behaviour later in script
if ($content =~ m/(.*Error.*)/i) {
$error = $1;
}
if ($content =~ /Best routing used a channel width factor of (\d+)/m) {
$min_chan_width = $1 ;
}
}
$min_chan_width = ($min_chan_width * 1.3);
$min_chan_width = floor($min_chan_width);
if($min_chan_width % 2) {
$min_chan_width = $min_chan_width + 1;
}
if (-e $vpr_route_output_file_path)
{
system "rm -f $vpr_route_output_file_path";
$q = &system_with_timeout($vpr_path,
"vpr.crit_path.out",
$timeout,
$temp_dir,
$architecture_file_name,
"$benchmark_name",
"--place_file", "$benchmark_name.place",
"--route",
"--route_chan_width", "$min_chan_width",
"--cluster_seed_type", "$vpr_cluster_seed_type",
"--nodisp",
);
}
}
} else {
$q = &system_with_timeout($vpr_path,
"vpr.out",
$timeout,
$temp_dir,
$architecture_file_path,
"$benchmark_name",
"--blif_file", "$scripts_output_file_name",
"--timing_analysis", "$timing_driven",
"--timing_driven_clustering", "$timing_driven",
"--route_chan_width", "$min_chan_width",
"--nodisp",
"--cluster_seed_type", "$vpr_cluster_seed_type"
);
}
if (-e $vpr_route_output_file_path)
{
if (! $keep_intermediate_files)
{
system "rm -f $scripts_output_file_path";
system "rm -f ${temp_dir}*.xml";
system "rm -f ${temp_dir}*.net";
system "rm -f ${temp_dir}*.place";
system "rm -f ${temp_dir}*.route";
}
}
else
{
print ("failed: vpr");
$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";
# Parse VPR output
if(open(VPROUT, "< vpr.out"))
{
undef $/;
my $content = <VPROUT>;
close (VPROUT);
$/ = "\n"; # Restore for normal behaviour later in script
if ($content =~ m/(.*Error.*)/i) {
$error = $1;
}
}
print RESULTS "error=$error\n";
close(RESULTS);
# Clean up files not used that take up a lot of space
#system "rm -f *.blif";
#system "rm -f *.xml";
#system "rm -f core.*";
#system "rm -f gc.txt";
if (! $error_code)
{
system "rm -f *.echo";
print "OK";
}
print "\n";
################################################################################
# Subroutine to execute a system call with a timeout
# system_with_timeout(<program>, <stdout file>, <timeout>, <dir>, <arg1>, <arg2>, 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";
(-f $_[0]) or die "system_with_timeout: can't find executable\n";
($_[2] > 0) or die "system_with_timeout: invalid timeout\n";
# Save the pid of child process
my $pid = fork;
if ($pid == 0)
{
# Redirect STDOUT for vpr
chdir $_[3];
open(STDOUT, "> $_[1]");
open(STDERR, "> $_[1]");
# 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.
print "$_[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{INTR} = sub { print "SIGINTR\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")
{
return "crashed";
}
elsif ($return_code != 0)
{
return "exited";
}
else
{
return "success";
}
}
}
}
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 "scripts")
{
return $stage_idx_script;
}
if (lc($stage_name) eq "vpr")
{
return $stage_idx_vpr;
}
return -1;
}
sub file_ext_for_stage
{
my $stage_idx = $_[0];
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_script)
{
return ".pre-vpr.blif";
}
}
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 $/; <FILE_IN>};
close(FILE_IN);
$file_contents =~ s/$search_string/$replace_string/mg;
open (FILE_OUT, ">$file_path");
print FILE_OUT $file_contents;
close (FILE_OUT);
}