blob: b298769cfb4cb7d8fbe0a5224a3dd294b884429a [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:
# Note that all unrecognized parameters are forwarded to VPR.
# -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.
# -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 <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 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 <circuit_file> <architecture_file> [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 $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;
my $pad_file_path;
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=1", "--errors-for-leak-kinds=none", "--track-origins=yes", "--log-file=valgrind.log","--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 $check_route = 0;
my $check_place = 0;
my $use_old_abc_script = 0;
my $run_name = "";
my $expect_fail = 0;
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 = undef;
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 "-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 = 1;
}
elsif ( $token eq "-verbose"){
$expect_fail = shift(@ARGV);
}
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);
}
# 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 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;
}
# 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 $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"
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$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;
# 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;
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 = <FILE>;
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.xml";
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.xml";
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 = <SECOUT>;
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 = <CECOUT>;
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;
}
}
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";
# Parse VPR output
if ( open( VPROUT, "< vpr.out" ) ) {
undef $/;
my $content = <VPROUT>;
close(VPROUT);
$/ = "\n"; # Restore for normal behaviour later in script
}
print RESULTS "error=$error\n";
close(RESULTS);
if ($expect_fail) {
if ($error_code != 0) {
#Failed as expected, invert message
$error_code = 0;
$error_status = "OK";
if ($verbosity > 0) {
$error_status .= "(as expected " . $error_status . ")";
} else {
$error_status .= "*";
}
} else {
#Passed when expected failure
$error_status = "failed: expected to fail but was " . $error_status;
}
}
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(<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";
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]" );
if (!$valgrind) {
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 && !$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 && !$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 $/; <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);
}
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);
}
#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 = <VPROUT>;
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;
}