#!usr/bin/perl -w # use the strict mode use strict; # Use the Shell enviornment #use Shell; # Use the time use Time::gmtime; # Use switch module #use Switch; use File::Path; use Cwd; use FileHandle; # Multi-thread support use threads; use threads::shared; # use ceil and floor numeric function use POSIX; # Date my $mydate = gmctime(); # Current Path my $cwd = getcwd(); # Global Variants # input Option Hash my %opt_h; my $opt_ptr = \%opt_h; # configurate file hash my %conf_h; my $conf_ptr = \%conf_h; # reports hash my %rpt_h; my $rpt_ptr = \%rpt_h; # tb_names hash my %tb_names_h; my $tb_names_ptr = \%tb_names_h; # Benchmarks my @benchmark_names; my %benchmarks; my $benchmarks_ptr = \%benchmarks; my %task_status; my $task_status_ptr = \%task_status; # Testbench names my @pb_mux_tb_names; my @cb_mux_tb_names; my @sb_mux_tb_names; my @lut_tb_names; my @hardlogic_tb_names; my @grid_tb_names; my @cb_tb_names; my @sb_tb_names; my @top_tb_name; # Configuration file keywords list # Category for conf file. # main category : 1st class my @mctgy; # sub category : 2nd class my @sctgy; # Initialize these categories @mctgy = ("dir_path", "task_conf", "csv_tags", ); # refer to the keywords of dir_path @{$sctgy[0]} = ("result_dir", "shell_script_name", "top_tb_dir_name", "grid_tb_dir_name", "lut_tb_dir_name", "hardlogic_tb_dir_name", "pb_mux_tb_dir_name", "cb_mux_tb_dir_name", "sb_mux_tb_dir_name", "cb_tb_dir_name", "sb_tb_dir_name", "top_tb_prefix", "pb_mux_tb_prefix", "cb_mux_tb_prefix", "sb_mux_tb_prefix", "lut_tb_prefix", "hardlogic_tb_prefix", "grid_tb_prefix", "cb_tb_prefix", "sb_tb_prefix", "top_tb_postfix", "pb_mux_tb_postfix", "cb_mux_tb_postfix", "sb_mux_tb_postfix", "lut_tb_postfix", "hardlogic_tb_postfix", "grid_tb_postfix", "cb_tb_postfix", "sb_tb_postfix", ); # refer to the keywords of flow_type @{$sctgy[1]} = ("auto_check", "num_pb_mux_tb", "num_cb_mux_tb", "num_sb_mux_tb", "num_lut_mux_tb", "num_hardlogic_tb", "num_grid_mux_tb", "num_top_tb", "num_cb_tb", "num_sb_tb", ); # refer to the keywords of csv_tags @{$sctgy[2]} = ("top_tb_leakage_power_tags", "top_tb_dynamic_power_tags", "pb_mux_tb_leakage_power_tags", "pb_mux_tb_dynamic_power_tags", "cb_mux_tb_leakage_power_tags", "cb_mux_tb_dynamic_power_tags", "sb_mux_tb_leakage_power_tags", "sb_mux_tb_dynamic_power_tags", "lut_tb_leakage_power_tags", "lut_tb_dynamic_power_tags", "hardlogic_tb_leakage_power_tags", "hardlogic_tb_dynamic_power_tags", "grid_tb_leakage_power_tags", "grid_tb_dynamic_power_tags", "cb_tb_leakage_power_tags", "cb_tb_dynamic_power_tags", "sb_tb_leakage_power_tags", "sb_tb_dynamic_power_tags", ); # ----------Subrountines------------# # Print TABs and strings sub tab_print($ $ $) { my ($FILE,$str,$num_tab) = @_; my ($my_tab) = (" "); for (my $i = 0; $i < $num_tab; $i++) { print $FILE "$my_tab"; } print $FILE "$str"; } # Create paths if it does not exist. sub generate_path($) { my ($mypath) = @_; if (!(-e "$mypath")) { mkpath "$mypath"; print "Path($mypath) does not exist...Create it.\n"; } return 1; } # Print the usage sub print_usage() { print "Usage:\n"; print " run_fpga_spice.pl [-options ]\n"; print " Mandatory options: \n"; print " -conf : specify the basic configuration files for run_fpga_spice\n"; print " -task : the configuration file contains benchmark file names\n"; print " -rpt : CSV file consists of data\n"; print " Other Options:\n"; print " -monte_carlo : Specify Monte Carlo simulation is enabled in FPGA-SPICE, specify the type of reports. (Simple: only max/min/avg is reported; Detail: every MC case is reported.\n"; print " -parse_pb_mux_tb: parse the results in pb_mux_testbench\n"; print " -parse_cb_mux_tb: parse the results in cb_mux_testbench\n"; print " -parse_sb_mux_tb: parse the results in sb_mux_testbench\n"; print " -parse_lut_tb: parse the results in lut_testbench\n"; print " -parse_hardlogic_tb: parse the results in hardlogic_testbench\n"; print " -parse_grid_tb: parse the results in grid_testbench\n"; print " -parse_cb_tb: parse the results in cb_testbench\n"; print " -parse_sb_tb: parse the results in sb_testbench\n"; print " -parse_top_tb: parse the results in top_testbench\n"; print " -multi_thread : turn on multi-thread mode, specify the number of processors could be pushed\n"; print " -sim_leakage_power_only : simulate leakage power only.\n"; print " -parse_results_only : only parse HSPICE simulation results\n"; print " -debug : debug mode\n"; print " -help : print usage\n"; exit(1); return 1; } sub spot_option($ $) { my ($start,$target) = @_; my ($arg_no,$flag) = (-1,"unfound"); for (my $iarg = $start; $iarg < $#ARGV+1; $iarg++) { if ($ARGV[$iarg] eq $target) { if ("found" eq $flag) { print "Error: Repeated Arguments!(IndexA: $arg_no,IndexB: $iarg)\n"; &print_usage(); } else { $flag = "found"; $arg_no = $iarg; } } } # return the arg_no if target is found # or return -1 when target is missing return $arg_no; } # Specify in the input list, # 1. Option Name # 2. Whether Option with value. if yes, choose "on" # 3. Whether Option is mandatory. If yes, choose "on" sub read_opt_into_hash($ $ $) { my ($opt_name,$opt_with_val,$mandatory) = @_; # Check the -$opt_name my ($opt_fact) = ("-".$opt_name); my ($cur_arg) = (0); my ($argfd) = (&spot_option($cur_arg,"$opt_fact")); if ($opt_with_val eq "on") { if (-1 != $argfd) { if ($ARGV[$argfd+1] =~ m/^-/) { print "The next argument cannot start with '-'!\n"; print "it implies an option!\n"; } else { $opt_ptr->{"$opt_name\_val"} = $ARGV[$argfd+1]; $opt_ptr->{"$opt_name"} = "on"; } } else { $opt_ptr->{"$opt_name"} = "off"; if ($mandatory eq "on") { print "Mandatory option: $opt_fact is missing!\n"; &print_usage(); } } } else { if (-1 != $argfd) { $opt_ptr->{"$opt_name"} = "on"; } else { $opt_ptr->{"$opt_name"} = "off"; if ($mandatory eq "on") { print "Mandatory option: $opt_fact is missing!\n"; &print_usage(); } } } return 1; } # Read options sub opts_read() { # if no arguments detected, print the usage. if (-1 == $#ARGV) { print "Error : No input arguments!\n"; &print_usage(); exit(1); } # Read in the options my ($cur_arg,$arg_found); $cur_arg = 0; print "Analyzing your options...\n"; # Read the options with internal options my $argfd; # Check help fist $argfd = &spot_option($cur_arg,"-help"); if (-1 != $argfd) { print "Help desk:\n"; &print_usage(); } # Then Check the debug with highest priority $argfd = &spot_option($cur_arg,"-debug"); if (-1 != $argfd) { $opt_ptr->{"debug"} = "on"; } else { $opt_ptr->{"debug"} = "off"; } # Check mandatory options # Check the -conf # Read Opt into Hash(opt_ptr) : "opt_name","with_val","mandatory" &read_opt_into_hash("conf","on","on"); &read_opt_into_hash("task","on","on"); &read_opt_into_hash("rpt","on","on"); # Optional options &read_opt_into_hash("monte_carlo","on","off"); &read_opt_into_hash("sim_leakage_power_only","off","off"); &read_opt_into_hash("parse_results_only","off","off"); &read_opt_into_hash("multi_thread","on","off"); &read_opt_into_hash("parse_pb_mux_tb","off","off"); &read_opt_into_hash("parse_cb_mux_tb","off","off"); &read_opt_into_hash("parse_sb_mux_tb","off","off"); &read_opt_into_hash("parse_lut_tb","off","off"); &read_opt_into_hash("parse_hardlogic_tb","off","off"); &read_opt_into_hash("parse_grid_tb","off","off"); &read_opt_into_hash("parse_cb_tb","off","off"); &read_opt_into_hash("parse_sb_tb","off","off"); &read_opt_into_hash("parse_top_tb","off","off"); &print_opts(); return 1; } # List the options sub print_opts() { print "List your options\n"; while(my ($key,$value) = each(%opt_h)) { print "$key : $value\n"; } return 1; } # Read each line and ignore the comments which starts with given arg # return the valid information of line sub read_line($ $) { my ($line,$com) = @_; my @chars; if (defined($line)) { @chars = split/$com/,$line; if (!($line =~ m/[\w\d]/)) { $chars[0] = undef; } if ($line =~ m/^\s*$com/) { $chars[0] = undef; } } else { $chars[0] = undef; } if (defined($chars[0])) { $chars[0] =~ s/^(\s+)//g; $chars[0] =~ s/(\s+)$//g; } return $chars[0]; } # Check each keywords has been defined in configuration file sub check_keywords_conf() { for (my $imcg = 0; $imcg<$#mctgy+1; $imcg++) { for (my $iscg = 0; $iscg<$#{$sctgy[$imcg]}+1; $iscg++) { if (defined($conf_ptr->{$mctgy[$imcg]}->{$sctgy[$imcg]->[$iscg]}->{val})) { if ("on" eq $opt_ptr->{debug}) { print "Keyword($mctgy[$imcg],$sctgy[$imcg]->[$iscg]) = "; print "$conf_ptr->{$mctgy[$imcg]}->{$sctgy[$imcg]->[$iscg]}->{val}"; print "\n"; } } else { die "Error: Keyword($mctgy[$imcg],$sctgy[$imcg]->[$iscg]) is missing!\n"; } } } return 1; } # Read the configuration file sub read_conf() { # Read in these key words my ($line,$post_line); my @equation; my $cur = "unknown"; open (CONF, "< $opt_ptr->{conf_val}") or die "Fail to open $opt_ptr->{conf_val}!\n"; print "Reading $opt_ptr->{conf_val}...\n"; while(defined($line = )) { chomp $line; $post_line = &read_line($line,"#"); if (defined($post_line)) { if ($post_line =~ m/\[(\w+)\]/) { $cur = $1; } elsif ("unknown" eq $cur) { die "Error: Unknown tags for this line!\n$post_line\n"; } else { $post_line =~ s/\s//g; @equation = split /=/,$post_line; if (!(defined($equation[1]))) { $equation[1] = ""; } $conf_ptr->{$cur}->{$equation[0]}->{val} = $equation[1]; } } } # Check these key words print "Read complete!\n"; &check_keywords_conf(); print "Checking these keywords..."; print "Successfully\n"; close(CONF); return 1; } sub read_benchmarks() { # Read in file names my ($line,$post_line,$cur); $cur = 0; open (FCONF,"< $opt_ptr->{task_val}") or die "Fail to open $opt_ptr->{task}!\n"; print "Reading $opt_ptr->{task_val}...\n"; while(defined($line = )) { chomp $line; $post_line = &read_line($line,"#"); if (defined($post_line)) { $post_line =~ s/\s+//g; my @tokens = split(",",$post_line); # first is the benchmark name, #the second is the channel width, if applicable if ($#tokens < 2) { die "ERROR: invalid definition for a benchmark! At least include 3 tokens!\n"; } if ($tokens[0]) { $benchmark_names[$cur] = $tokens[0]; } else { die "ERROR: invalid definition for benchmarks!\n"; } $benchmarks_ptr->{"$benchmark_names[$cur]"}->{spice_netlist_prefix} = $tokens[1]; $benchmarks_ptr->{"$benchmark_names[$cur]"}->{spice_dir} = $tokens[2]; $cur++; } } print "Benchmarks(total $cur):\n"; foreach my $temp(@benchmark_names) { print "$temp\n"; } close(FCONF); return 1; } # Input program path is like "~/program_dir/program_name" # We split it from the scalar sub split_prog_path($) { my ($prog_path) = @_; my @path_elements = split /\//,$prog_path; my ($prog_dir,$prog_name); $prog_name = $path_elements[$#path_elements]; $prog_dir = $prog_path; $prog_dir =~ s/$prog_name$//g; return ($prog_dir,$prog_name); } # Detect and convert unit, NO Case Insentive. sub process_unit($ $) { my ($unit,$type) = @_; my ($ret,$coeff) = (0,0); # Check type, can be if ("time" eq $type) { $unit =~ s/s$//i; } elsif ("current" eq $type) { $unit =~ s/A$//; # Special should not mix with "a" = 1e-18 } elsif ("power" eq $type) { $unit =~ s/W$//; } elsif ("voltage" eq $type) { $unit =~ s/V$//; } elsif ("capacitance" eq $type) { $unit =~ s/F$//; # Special should not mix with "f" = 1e-15 } elsif ("empty" ne $type) { die "Error: (process_unit)Unknown type!Should be \n"; } # Accepte unit: m = 1e-3, u = 1e-6, n = 1e-9, p = 1e-12, f = 1e-15, a = 1e-18 if ($unit =~ m/a$/) { $unit =~ s/a$//; $coeff = 1e-18; } elsif ($unit =~ m/f$/) { $unit =~ s/f$//; $coeff = 1e-15; } elsif ($unit =~ m/p$/) { $unit =~ s/p$//; $coeff = 1e-12; } elsif ($unit =~ m/n$/) { $unit =~ s/n$//; $coeff = 1e-9; } elsif ($unit =~ m/u$/) { $unit =~ s/u$//; $coeff = 1e-6; } elsif ($unit =~ m/m$/) { $unit =~ s/m$//; $coeff = 1e-3; } elsif ($unit =~ m/k$/) { $unit =~ s/k$//; $coeff = 1e3; } elsif ($unit =~ m/Meg$/) { $unit =~ s/Meg$//; $coeff = 1e6; } elsif ($unit =~ m/\d$/i) { $coeff = 1; } # Chomp the possible point at the end $unit =~ s/\.$//; # Quick check, there should be only numbers in remaining if (!($unit =~ m/\d$/)) { die "Error: (process_unit) Invalid number($unit)!\n"; } return $ret = $unit*$coeff; } # TODO: Check settings sub check_fpga_spice() { # Format the dir_path # Format SPICE prefix } # Initialize the status of all tasks sub init_tasks_status() { foreach my $bm(@benchmark_names) { $task_status_ptr->{$bm}->{status} = "wait"; $task_status_ptr->{$bm}->{thread_id} = undef; } } sub check_all_fpga_spice_tasks_done() { foreach my $bm(@benchmark_names) { if ("done" ne $task_status_ptr->{$bm}->{status}) { return 0; } } return 1; } sub mark_all_fpga_spice_tasks_done() { foreach my $bm(@benchmark_names) { $task_status_ptr->{$bm}->{status} = "done"; } return 1; } sub print_tasks_status() { my ($num_jobs_running, $num_jobs_to_run, $num_jobs_finish, $num_jobs) = (0, 0, 0, 0); foreach my $benchmark(@benchmark_names) { # Count the number of jobs $num_jobs++; # Count to do jobs if ("wait" eq $task_status_ptr->{$benchmark}->{status}) { $num_jobs_to_run++; next; } if ("running" eq $task_status_ptr->{$benchmark}->{status}) { # Count running jobs $num_jobs_running++; next; } # Count finished jobs if ("done" eq $task_status_ptr->{$benchmark}->{status}) { $num_jobs_finish++; next; } } if ($num_jobs == ($num_jobs_running + $num_jobs_finish + $num_jobs_to_run)) { print "Jobs Progress: (Finish rate = ".sprintf("%.2f",100*$num_jobs_finish/$num_jobs) ."%)\n"; print "Thead utilization rate = ".sprintf("%.2f",100*$num_jobs_running/$opt_ptr->{multi_thread_val})."%\n"; print "Total No. of Jobs: $num_jobs.\n"; print "No. of Running Jobs: $num_jobs_running.\n"; print "No. of Finished Jobs: $num_jobs_finish.\n"; print "No. of To Run Jobs: $num_jobs_to_run.\n"; } else { print "Internal problem: num_jobs($num_jobs) != num_jobs_running($num_jobs_running)\n"; print " +num_jobs_finish($num_jobs_finish)\n"; die " +num_jobs_to_run($num_jobs_to_run)\n"; } return; } sub format_dir_path($) { my ($dir_path) = @_; my ($formatted_dir_path) = ($dir_path); if (!($formatted_dir_path =~ m/\/$/)) { $formatted_dir_path = $formatted_dir_path."/"; } return $formatted_dir_path; } sub gen_fpga_spice_netlists_path($ $ $) { my ($spice_dir, $sp_prefix, $sp_postfix) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($sp_path) = ($formatted_spice_dir.$sp_prefix.$sp_postfix); return ($sp_path); } sub determine_fpga_grid_nx_ny($) { my ($grid_no) = @_; my ($nx, $ny) = (ceil(sqrt($grid_no)), ceil(sqrt($grid_no))); return ($nx, $ny); } sub gen_fpga_tb_spice_netlist_path($ $ $ $ $ $) { my ($spice_dir, $sp_prefix, $tb_prefix, $ix, $iy, $sp_postfix) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($sp_path) = ($formatted_spice_dir.$sp_prefix.$tb_prefix.$ix."_".$iy.$sp_postfix); return ($sp_path); } sub gen_fpga_spice_measure_results_path($ $ $) { my ($spice_dir, $sp_prefix, $sp_postfix) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($formatted_result_dir) = &format_dir_path($conf_ptr->{dir_path}->{result_dir}->{val}); my ($mt_path) = ($formatted_spice_dir.$formatted_result_dir.$sp_prefix.$sp_postfix); $mt_path =~ s/\.sp/.mt0/; return ($mt_path); } sub gen_fpga_tb_spice_measure_results_path($ $ $ $ $) { my ($spice_dir, $sp_prefix, $tb_prefix, $no, $sp_postfix) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($formatted_result_dir) = &format_dir_path($conf_ptr->{dir_path}->{result_dir}->{val}); my ($mt_path) = ($formatted_spice_dir.$formatted_result_dir.$sp_prefix.$tb_prefix.$no.$sp_postfix); $mt_path =~ s/\.sp/.mt0/; return ($mt_path); } sub convert_fpga_tb_names_to_measure_names($) { my ($tb_sp_path, $spice_dir) = @_; my ($mt_path); my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($formatted_result_dir) = &format_dir_path($conf_ptr->{dir_path}->{result_dir}->{val}); # Split tb_sp_path my @sp_parts = split('/', $tb_sp_path); my ($mt_name) = ($sp_parts[$#sp_parts]); $mt_name =~ s/\.sp$/\.mt0/g; ($mt_path) = ($formatted_spice_dir.$formatted_result_dir.$mt_name); return $mt_path; } sub remove_fpga_spice_results($) { my ($spice_dir) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($formatted_result_dir) = &format_dir_path($conf_ptr->{dir_path}->{result_dir}->{val}); my ($result_dir_path) = ($formatted_spice_dir.$formatted_result_dir); #rmtree($result_dir_path,1,1); `rm -rf $result_dir_path/`; print "INFO: Simulation Results($result_dir_path) are removed...\n"; #`rm -rf $spice_dir/`; #print "INFO: Spice Directory($spice_dir) is removed...\n"; return; } sub check_one_spice_lis_error($) { my ($lis_path) = @_; my ($line, $line_no, $warn_no, $err_no) = (undef,0,0,0); my ($LISFH) = FileHandle->new; print "INFO: Checking LIS file($lis_path)...\n"; if (!(-e $lis_path)) { die "ERROR: Fail to find SPICE lis file($lis_path)!\n"; } if ($LISFH->open("< $lis_path")) { while(defined($line = <$LISFH>)) { chomp $line; $line_no++; if ($line =~ m/error/i) { $err_no++; print "ERROR($err_no),LIS FILE[LINE$line_no]: ".$line."\n"; } if ($line =~ m/warning/i) { $warn_no++; print "WARNING($warn_no),LIS FILE[LINE$line_no]: ".$line."\n"; } } } else { die "ERROR: fail to open $lis_path!\n"; } # Close the lis file close($LISFH); # Print HSPICE ERROR and Warning Stats print "HSPICE Sim. reports $err_no errors and $warn_no warnings.\n"; if ($err_no > 0) { die "ERROR: Terminate due to errors in HSPICE Sim. reports.\n"; } return; } sub check_one_fpga_spice_task_lis($ $ $) { my ($benchmark, $spice_netlist_prefix, $spice_dir) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($shell_script_path) = $formatted_spice_dir.$conf_ptr->{dir_path}->{shell_script_name}->{val}; my ($itb, $lis_file_path); if ("on" eq $opt_ptr->{parse_top_tb}) { my ($top_lis_path) = &gen_fpga_spice_measure_results_path($formatted_spice_dir, $spice_netlist_prefix,$conf_ptr->{dir_path}->{top_tb_postfix}->{val}); $top_lis_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($top_lis_path); } if ("on" eq $opt_ptr->{parse_pb_mux_tb}) { # generate measure results paths @pb_mux_tb_names = split (',', $tb_names_ptr->{$benchmark}->{pb_mux_tb_names}); foreach my $tb_sp_filename (@pb_mux_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } if ("on" eq $opt_ptr->{parse_cb_mux_tb}) { # generate measure results paths @cb_mux_tb_names = split (',', $tb_names_ptr->{$benchmark}->{cb_mux_tb_names}); foreach my $tb_sp_filename (@cb_mux_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } if ("on" eq $opt_ptr->{parse_sb_mux_tb}) { # generate measure results paths @sb_mux_tb_names = split (',', $tb_names_ptr->{$benchmark}->{sb_mux_tb_names}); foreach my $tb_sp_filename (@sb_mux_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } if ("on" eq $opt_ptr->{parse_lut_tb}) { # generate measure results paths @lut_tb_names = split (',', $tb_names_ptr->{$benchmark}->{lut_tb_names}); foreach my $tb_sp_filename (@lut_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } if (0 == $conf_ptr->{task_conf}->{num_hardlogic_tb}->{val}) { } elsif ("on" eq $opt_ptr->{parse_hardlogic_tb}) { # generate measure results paths @hardlogic_tb_names = split (',', $tb_names_ptr->{$benchmark}->{hardlogic_tb_names}); foreach my $tb_sp_filename (@hardlogic_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } if ("on" eq $opt_ptr->{parse_grid_tb}) { # generate measure results paths @grid_tb_names = split (',', $tb_names_ptr->{$benchmark}->{grid_tb_names}); foreach my $tb_sp_filename (@grid_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } if ("on" eq $opt_ptr->{parse_cb_tb}) { # generate measure results paths @cb_tb_names = split (',', $tb_names_ptr->{$benchmark}->{cb_tb_names}); foreach my $tb_sp_filename (@cb_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } if ("on" eq $opt_ptr->{parse_sb_tb}) { # generate measure results paths @sb_tb_names = split (',', $tb_names_ptr->{$benchmark}->{sb_tb_names}); foreach my $tb_sp_filename (@sb_tb_names) { $lis_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path =~ s/\.mt0/.lis/; &check_one_spice_lis_error($lis_file_path); } } return; } sub init_one_fpga_spice_task_one_tb_results($ $ $ $) { my ($benchmark, $tbname_tag, $tb_leakage_tags, $tb_dynamic_tags) = @_; my (@leakage_tags) = split('\|', $tb_leakage_tags); my (@dynamic_tags) = split('\|', $tb_dynamic_tags); my ($temp); # Check if there is any conflict to reserved words foreach my $tag(@leakage_tags) { if (("peak_mem_used" eq $tag)||("total_elapsed_time" eq $tag)) { die "ERROR: $tbname_tag leakage_power_tags has a conflict word($tag)!\n"; } } if ("off" eq $opt_ptr->{sim_leakage_power_only}) { foreach my $tag(@dynamic_tags) { if (("peak_mem_used" eq $tag)||("total_elapsed_time" eq $tag)) { die "ERROR: $tbname_tag dynamic_power_tags has a conflict word($tag)!\n"; } } } $rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used} = 0; $rpt_ptr->{$benchmark}->{$tbname_tag}->{total_elapsed_time} = 0; foreach my $tag(@leakage_tags) { $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag} = 0; } if ("off" eq $opt_ptr->{sim_leakage_power_only}) { foreach my $tag(@dynamic_tags) { $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag} = 0; } } return; } sub parse_one_fpga_spice_task_one_regular_tb_results($ $ $ $ $ $) { my ($benchmark, $tbname_tag, $tb_lispath, $tb_mtpath, $tb_leakage_tags, $tb_dynamic_tags) = @_; my (@leakage_tags) = split('\|', $tb_leakage_tags); my (@dynamic_tags) = split('\|', $tb_dynamic_tags); my ($line, $found_tran_analysis); my ($LISFH) = FileHandle->new; my ($temp); # Check if there is any conflict to reserved words foreach my $tag(@leakage_tags) { if (("peak_mem_used" eq $tag)||("total_elapsed_time" eq $tag)) { die "ERROR: $tbname_tag leakage_power_tags has a conflict word($tag)!\n"; } } if ("off" eq $opt_ptr->{sim_leakage_power_only}) { foreach my $tag(@dynamic_tags) { if (("peak_mem_used" eq $tag)||("total_elapsed_time" eq $tag)) { die "ERROR: $tbname_tag dynamic_power_tags has a conflict word($tag)!\n"; } } } if (!(-e $tb_lispath)) { die "ERROR: Fail to find SPICE lis file($tb_lispath)!\n"; } if ($LISFH->open("< $tb_lispath")) { $found_tran_analysis = 0; while(defined($line = <$LISFH>)) { chomp $line; if ((0 == $found_tran_analysis)&&($line =~ m/transient\s+analysis/)) { $found_tran_analysis = 1; } if (0 == $found_tran_analysis) { next; } # Special: get peak memory used and total elapsed time if ($line =~ m/peak\s+memory\s+used\s+([\d.]+)\s+megabytes/i) { $temp = $1; if ((!defined($rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used})) ||($rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used} < $temp)) { $rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used} = $temp; } next; # We find a match, ignore the rest } # Special: get peak memory used and total elapsed time if ($line =~ m/total\s+elapsed\s+time\s+([\d.]+)\s+seconds/i) { $temp = $1; $rpt_ptr->{$benchmark}->{$tbname_tag}->{total_elapsed_time} += $temp; next; # We find a match, ignore the rest } # For leakage power foreach my $tag(@leakage_tags) { if ($line =~ m/$tag\s*=\s*([\d.\w\-\+]+)/i) { $temp = $1; $temp = &process_unit($temp, "empty"); $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{avg} += $temp; next; # We find a match, ignore the rest } } # Bypass dynamic tags if leakage_only is enabled if ("on" eq $opt_ptr->{sim_leakage_power_only}) { next; } # For dynamic power foreach my $tag(@dynamic_tags) { if ($line =~ m/$tag\s*=\s*([\d.\w\-\+]+)/i) { $temp = $1; $temp = &process_unit($temp, "empty"); $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{avg} += $temp; next; # We find a match, ignore the rest } } } } else { die "ERROR: fail to open $tb_lispath!\n"; } # Close file close($LISFH); return; } sub parse_one_fpga_spice_task_one_mc_tb_results($ $ $ $ $ $ $) { my ($benchmark, $tbname_tag, $tb_lispath, $tb_mtpath, $tb_leakage_tags, $tb_dynamic_tags) = @_; my (@leakage_tags) = split('\|', $tb_leakage_tags); my (@dynamic_tags) = split('\|', $tb_dynamic_tags); my ($line, $found_tran_analysis); my ($LISFH) = FileHandle->new; my ($temp, $mc_cnt) = ("", 0); # Check if there is any conflict to reserved words foreach my $tag(@leakage_tags) { if (("peak_mem_used" eq $tag)||("total_elapsed_time" eq $tag)) { die "ERROR: $tbname_tag leakage_power_tags has a conflict word($tag)!\n"; } # for a clear start $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{mc_cnt} = 0; } if ("off" eq $opt_ptr->{sim_leakage_power_only}) { foreach my $tag(@dynamic_tags) { if (("peak_mem_used" eq $tag)||("total_elapsed_time" eq $tag) ||("mc_min" eq $tag)||("mc_max" eq $tag)||("mc_avg" eq $tag) ||("mc_total" eq $tag)||("mc_cnt" eq $tag)) { die "ERROR: $tbname_tag dynamic_power_tags has a conflict word($tag)!\n"; } # for a clear start $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{mc_cnt} = 0; } } if (!(-e $tb_lispath)) { die "ERROR: Fail to find SPICE lis file($tb_lispath)!\n"; } if ($LISFH->open("< $tb_lispath")) { $found_tran_analysis = 0; while(defined($line = <$LISFH>)) { chomp $line; if ((0 == $found_tran_analysis)&&($line =~ m/transient\s+analysis/)) { $found_tran_analysis = 1; } if (0 == $found_tran_analysis) { next; } # Special: get peak memory used and total elapsed time if ($line =~ m/peak\s+memory\s+used\s+([\d.]+)\s+megabytes/i) { $temp = $1; if ((!defined($rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used})) ||($rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used} < $temp)) { $rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used} = $temp; } next; # We find a match, ignore the rest } # Special: get peak memory used and total elapsed time if ($line =~ m/total\s+elapsed\s+time\s+([\d.]+)\s+seconds/i) { $temp = $1; $rpt_ptr->{$benchmark}->{$tbname_tag}->{total_elapsed_time} += $temp; next; # We find a match, ignore the rest } # For leakage power foreach my $tag(@leakage_tags) { if ($line =~ m/$tag\s*=\s*([\d.\w\-\+]+)/i) { $temp = $1; $temp = &process_unit($temp, "empty"); $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{mc_cnt} += 1; $mc_cnt = $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{mc_cnt}; if (defined($rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{"mc".$mc_cnt})) { $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{"mc".$mc_cnt} += $temp; } else { $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{"mc".$mc_cnt} = $temp; } next; # We find a match, ignore the rest } } # Bypass dynamic tags if leakage_only is enabled if ("on" eq $opt_ptr->{sim_leakage_power_only}) { next; } # For dynamic power foreach my $tag(@dynamic_tags) { if ($line =~ m/$tag\s*=\s*([\d.\w\-\+]+)/i) { $temp = $1; $temp = &process_unit($temp, "empty"); $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{mc_cnt} += 1; $mc_cnt = $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{mc_cnt}; if (defined($rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{"mc".$mc_cnt})) { $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{"mc".$mc_cnt} += $temp; } else { $rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{"mc".$mc_cnt} = $temp; } next; # We find a match, ignore the rest } } } } else { die "ERROR: fail to open $tb_lispath!\n"; } # Close file close($LISFH); return; } sub parse_one_fpga_spice_task_one_tb_results($ $ $ $ $ $) { my ($benchmark, $tbname_tag, $tb_lispath, $tb_mtpath, $tb_leakage_tags, $tb_dynamic_tags) = @_; if ("on" eq $opt_ptr->{monte_carlo}) { &parse_one_fpga_spice_task_one_mc_tb_results($benchmark, $tbname_tag, $tb_lispath, $tb_mtpath, $tb_leakage_tags, $tb_dynamic_tags); } else { &parse_one_fpga_spice_task_one_regular_tb_results($benchmark, $tbname_tag, $tb_lispath, $tb_mtpath, $tb_leakage_tags, $tb_dynamic_tags); } return; } sub count_num_tb_one_folder($) { my ($dir) = @_; my (@files) = <$dir/*.sp>; my ($count) = $#files + 1; return $count; } sub get_tb_file_names($) { my ($tb_dir) = @_; my @ret_file_names; my ($dh); # Search all the sp files matching the name opendir($dh, $tb_dir) || die "Fail to open directory of $tb_dir\n"; @ret_file_names = grep(/\.sp$/, readdir($dh)); print "Getting testbench file names for $tb_dir ...\n"; closedir($dh); # join the names together my ($ret) = join(",", @ret_file_names); return $ret; } sub auto_check_tb_num($ $ $) { my ($benchmark, $spice_netlist_prefix, $spice_dir) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($tb_dir); print "Autocheck Results:\n"; # count pb_mux_tb $conf_ptr->{task_conf}->{num_pb_mux_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{pb_mux_tb_dir_name}->{val}); print "INFO: No. of CLB MUXes testbenches = $conf_ptr->{task_conf}->{num_pb_mux_tb}->{val}\n"; # count sb_mux_tb $conf_ptr->{task_conf}->{num_sb_mux_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{sb_mux_tb_dir_name}->{val}); print "INFO: No. of Switch Box MUXes testbenches = $conf_ptr->{task_conf}->{num_sb_mux_tb}->{val}\n"; # count cb_mux_tb $conf_ptr->{task_conf}->{num_cb_mux_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{cb_mux_tb_dir_name}->{val}); print "INFO: No. of Connection Box MUXes testbenches = $conf_ptr->{task_conf}->{num_cb_mux_tb}->{val}\n"; # count lut_tb $conf_ptr->{task_conf}->{num_lut_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{lut_tb_dir_name}->{val}); print "INFO: No. of LUT testbenches = $conf_ptr->{task_conf}->{num_lut_tb}->{val}\n"; # count hardlogic_tb $conf_ptr->{task_conf}->{num_hardlogic_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{hardlogic_tb_dir_name}->{val}); print "INFO: No. of FF testbenches = $conf_ptr->{task_conf}->{num_hardlogic_tb}->{val}\n"; # count grid_tb $conf_ptr->{task_conf}->{num_grid_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{grid_tb_dir_name}->{val}); print "INFO: No. of Grid testbenches = $conf_ptr->{task_conf}->{num_grid_tb}->{val}\n"; # count cb_tb $conf_ptr->{task_conf}->{num_cb_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{cb_tb_dir_name}->{val}); print "INFO: No. of CB testbenches = $conf_ptr->{task_conf}->{num_cb_tb}->{val}\n"; # count sb_tb $conf_ptr->{task_conf}->{num_sb_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{sb_tb_dir_name}->{val}); print "INFO: No. of SB testbenches = $conf_ptr->{task_conf}->{num_sb_tb}->{val}\n"; # count top_tb $conf_ptr->{task_conf}->{num_top_tb}->{val} = &count_num_tb_one_folder($formatted_spice_dir.$conf_ptr->{dir_path}->{top_tb_dir_name}->{val}); print "INFO: No. of Top-level testbench = $conf_ptr->{task_conf}->{num_top_tb}->{val}\n"; my ($toptb_sp_path) = &gen_fpga_spice_netlists_path($formatted_spice_dir.$conf_ptr->{dir_path}->{top_tb_dir_name}->{val}, $spice_netlist_prefix, $conf_ptr->{dir_path}->{top_tb_postfix}->{val}); if (("on" eq $opt_ptr->{parse_top_tb})&&(!(-e $toptb_sp_path))) { die "ERROR: File($toptb_sp_path) does not exist!"; } if ("on" eq $opt_ptr->{parse_pb_mux_tb}) { $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{pb_mux_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{pb_mux_tb_names} = &get_tb_file_names($tb_dir); } if ("on" eq $opt_ptr->{parse_cb_mux_tb}) { # Search all the sp files matching the name $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{cb_mux_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{cb_mux_tb_names} = &get_tb_file_names($tb_dir); } if ("on" eq $opt_ptr->{parse_sb_mux_tb}) { $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{sb_mux_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{sb_mux_tb_names} = &get_tb_file_names($tb_dir); } if ("on" eq $opt_ptr->{parse_lut_tb}) { # Search all the sp files matching the name $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{lut_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{lut_tb_names} = &get_tb_file_names($tb_dir); } # Special, if there is no hardlogic, this is comb circuit, we don't collect the information if (0 == $conf_ptr->{task_conf}->{num_hardlogic_tb}->{val}) { print "INFO: None DFF testbenches detected... This may caused by a combinational circuit!\n"; } elsif ("on" eq $opt_ptr->{parse_hardlogic_tb}) { # Search all the sp files matching the name $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{hardlogic_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{hardlogic_tb_names} = &get_tb_file_names($tb_dir); } if ("on" eq $opt_ptr->{parse_grid_tb}) { # Search all the sp files matching the name $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{grid_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{grid_tb_names} = &get_tb_file_names($tb_dir); } if ("on" eq $opt_ptr->{parse_cb_tb}) { # Search all the sp files matching the name $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{cb_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{cb_tb_names} = &get_tb_file_names($tb_dir); } if ("on" eq $opt_ptr->{parse_sb_tb}) { # Search all the sp files matching the name $tb_dir = $formatted_spice_dir.$conf_ptr->{dir_path}->{sb_tb_dir_name}->{val}; $tb_names_ptr->{$benchmark}->{sb_tb_names} = &get_tb_file_names($tb_dir); } return; } sub parse_one_fpga_spice_task_results($ $ $) { my ($benchmark, $spice_netlist_prefix, $spice_dir) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($shell_script_path) = $formatted_spice_dir.$conf_ptr->{dir_path}->{shell_script_name}->{val}; my ($itb, $mt_file_path, $lis_file_path); &auto_check_tb_num($benchmark, $spice_netlist_prefix, $spice_dir); if ("on" eq $opt_ptr->{parse_top_tb}) { my ($top_mt_path) = &gen_fpga_spice_measure_results_path($spice_dir, $spice_netlist_prefix,$conf_ptr->{dir_path}->{top_tb_postfix}->{val}); my ($top_lis_path) = ($top_mt_path); $top_lis_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"top_tb", $top_lis_path, $top_mt_path, $conf_ptr->{csv_tags}->{top_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{top_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_pb_mux_tb}) { # generate measure results paths @pb_mux_tb_names = split (',', $tb_names_ptr->{$benchmark}->{pb_mux_tb_names}); foreach my $tb_sp_filename (@pb_mux_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"pb_mux_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{pb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{pb_mux_tb_dynamic_power_tags}->{val}); } } if ("on" eq $opt_ptr->{parse_cb_mux_tb}) { # generate measure results paths @cb_mux_tb_names = split (',', $tb_names_ptr->{$benchmark}->{cb_mux_tb_names}); foreach my $tb_sp_filename (@cb_mux_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"cb_mux_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{cb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{cb_mux_tb_dynamic_power_tags}->{val}); } } if ("on" eq $opt_ptr->{parse_sb_mux_tb}) { # generate measure results paths @sb_mux_tb_names = split (',', $tb_names_ptr->{$benchmark}->{sb_mux_tb_names}); foreach my $tb_sp_filename (@sb_mux_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"sb_mux_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{sb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{sb_mux_tb_dynamic_power_tags}->{val}); } } if ("on" eq $opt_ptr->{parse_lut_tb}) { # generate measure results paths @lut_tb_names = split (',', $tb_names_ptr->{$benchmark}->{lut_tb_names}); foreach my $tb_sp_filename (@lut_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"lut_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{lut_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{lut_tb_dynamic_power_tags}->{val}); } } if (0 == $conf_ptr->{task_conf}->{num_hardlogic_tb}->{val}) { &init_one_fpga_spice_task_one_tb_results($benchmark,"hardlogic_tb",$conf_ptr->{csv_tags}->{hardlogic_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{hardlogic_tb_dynamic_power_tags}->{val}); } elsif ("on" eq $opt_ptr->{parse_hardlogic_tb}) { # generate measure results paths @hardlogic_tb_names = split (',', $tb_names_ptr->{$benchmark}->{hardlogic_tb_names}); foreach my $tb_sp_filename (@hardlogic_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"hardlogic_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{hardlogic_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{hardlogic_tb_dynamic_power_tags}->{val}); } } if ("on" eq $opt_ptr->{parse_grid_tb}) { # generate measure results paths @grid_tb_names = split (',', $tb_names_ptr->{$benchmark}->{grid_tb_names}); foreach my $tb_sp_filename (@grid_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"grid_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{grid_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{grid_tb_dynamic_power_tags}->{val}); } } if ("on" eq $opt_ptr->{parse_cb_tb}) { # generate measure results paths @cb_tb_names = split (',', $tb_names_ptr->{$benchmark}->{cb_tb_names}); foreach my $tb_sp_filename (@cb_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"cb_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{cb_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{cb_tb_dynamic_power_tags}->{val}); } } if ("on" eq $opt_ptr->{parse_sb_tb}) { # generate measure results paths @sb_tb_names = split (',', $tb_names_ptr->{$benchmark}->{sb_tb_names}); foreach my $tb_sp_filename (@sb_tb_names) { $mt_file_path = &convert_fpga_tb_names_to_measure_names($tb_sp_filename, $spice_dir); $lis_file_path = $mt_file_path; $lis_file_path =~ s/\.mt0$/.lis/; &parse_one_fpga_spice_task_one_tb_results($benchmark,"sb_tb", $lis_file_path, $mt_file_path, $conf_ptr->{csv_tags}->{sb_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{sb_tb_dynamic_power_tags}->{val}); } } return; } # Run a fpga_spice_task sub run_one_fpga_spice_task($ $ $) { my ($benchmark, $spice_netlist_prefix, $spice_dir) = @_; my ($formatted_spice_dir) = &format_dir_path($spice_dir); my ($shell_script_path) = $formatted_spice_dir.$conf_ptr->{dir_path}->{shell_script_name}->{val}; my ($itb, $ix, $iy, $dh, $tb_dir); # change to the spice_dir chdir $spice_dir; # Check all the SPICE netlists and shell scripts exist &auto_check_tb_num($benchmark, $spice_netlist_prefix, $spice_dir); if (!(-e $shell_script_path)) { die "ERROR: File($shell_script_path) does not exist!"; } # Call the shell script print "INFO: Running shell script ($shell_script_path)...\n"; `csh -cx 'source $shell_script_path'` or die "ERROR: fail in executing $shell_script_path!\n"; # return to current dir chdir $cwd; &check_one_fpga_spice_task_lis($benchmark, $spice_netlist_prefix, $spice_dir); print "INFO: Check LIS file finished.\n"; return; } sub parse_all_tasks_results() { print "Parsing all the results...\n"; foreach my $benchmark(@benchmark_names) { if ("done" eq $task_status_ptr->{$benchmark}->{status}) { print "Parsing simulation results for benchmark($benchmark)...\n"; &parse_one_fpga_spice_task_results($benchmark,$benchmarks_ptr->{$benchmark}->{spice_netlist_prefix},$benchmarks_ptr->{$benchmark}->{spice_dir}); } else { die "ERROR: found unfinished tasks when try to parse results!\n"; } # Remove results to avoid storage overflow #&remove_fpga_spice_results($benchmarks_ptr->{$benchmark}->{spice_dir}); } } # Multi-thread Running FPGA SPICE tasks sub multi_thread_run_fpga_spice_tasks($) { my ($num_threads) = @_; # Evaluate include threads ok if (!(eval 'use threads; 1')) { die "ERROR: cannot use threads package in Perl! Please check the installation of package...\n"; } # Lauch threads up to the limited number of threads number if ($num_threads < 2) { $num_threads = 2; } my ($num_thread_running) = (0); # Iterate until all the tasks has been assigned, finished while (1 != &check_all_fpga_spice_tasks_done()) { foreach my $benchmark(@benchmark_names) { # Bypass finished job if ("done" eq $task_status_ptr->{$benchmark}->{status}) { next; } # Check running job if ("running" eq $task_status_ptr->{$benchmark}->{status}) { my ($thr_id) = ($task_status_ptr->{$benchmark}->{thread_id}); if (!($thr_id)) { die "INTERNAL ERROR: invalid thread_id for task: $benchmark!\n"; } # Check if there is any error if ($thr_id->error()) { die "ERROR: Task: $benchmark, Thread(ID:$thr_id) exit abnormally!\n"; } if ($thr_id->is_running()) { $task_status_ptr->{$benchmark}->{status} = "running"; } if ($thr_id->is_joinable()) { $num_thread_running--; $thr_id->join(); # Join the thread results # Update task status $task_status_ptr->{$benchmark}->{status} = "done"; print "INFO: task: $benchmark finished!\n"; &print_tasks_status(); } next; } # If we reach the thread number limit, we have to wait... if (($num_thread_running == $num_threads) ||($num_thread_running > $num_threads)) { next; } # Start a new thread for a waiting task if ("wait" eq $task_status_ptr->{$benchmark}->{status}) { # We try to start a new thread since there are still threads available my ($thr_new) = (threads->create(\&run_one_fpga_spice_task, $benchmark,$benchmarks_ptr->{$benchmark}->{spice_netlist_prefix},$benchmarks_ptr->{$benchmark}->{spice_dir})); if ($thr_new) { print "INFO: a new thread for task (benchmark: $benchmark) is lauched!\n"; # Update status $task_status_ptr->{$benchmark}->{status} = "running"; $task_status_ptr->{$benchmark}->{thread_id} = $thr_new; $num_thread_running++; &print_tasks_status(); } else { # Fail to create a new thread, wait... print "INFO: fail to lauch a new thread for task (benchmark: $benchmark)!\n"; } } } } &print_tasks_status(); # Parse_results &parse_all_tasks_results(); } # Single-thread mode Running FPGA SPICE tasks sub single_thread_run_fpga_spice_tasks() { # Iterate until all the tasks has been assigned, finished while (1 != &check_all_fpga_spice_tasks_done()) { foreach my $benchmark(@benchmark_names) { # Bypass finished job if ("done" eq $task_status_ptr->{$benchmark}->{status}) { next; } &run_one_fpga_spice_task($benchmark,$benchmarks_ptr->{$benchmark}->{spice_netlist_prefix},$benchmarks_ptr->{$benchmark}->{spice_dir}); &parse_one_fpga_spice_task_results($benchmark,$benchmarks_ptr->{$benchmark}->{spice_netlist_prefix},$benchmarks_ptr->{$benchmark}->{spice_dir}); $task_status_ptr->{$benchmark}->{status} = "done"; # Remove results to avoid storage overflow &remove_fpga_spice_results($benchmarks_ptr->{$benchmark}->{spice_dir}); } } # Parse_results #&parse_all_tasks_results(); return; } # Plan to run tasks sub plan_run_tasks() { &init_tasks_status(); if (("on" eq $opt_ptr->{multi_thread}) &&(1 < $opt_ptr->{multi_thread_val}) &&(1 < ($#benchmark_names + 1))) { &multi_thread_run_fpga_spice_tasks($opt_ptr->{multi_thread_val}); } else { if ("on" eq $opt_ptr->{multi_thread}) { print "INFO: multi_thread is selected but only 1 processor can be used or 1 benchmark to run...\n"; print "INFO: switch to single thread mode.\n"; } &single_thread_run_fpga_spice_tasks(); } } sub gen_csv_rpt_one_tb_one_case($ $ $ $ $) { my ($RPTFH, $tbname_tag, $tb_leakage_tags, $tb_dynamic_tags, $case_tag) = @_; my (@leakage_tags) = split('\|', $tb_leakage_tags); my (@dynamic_tags) = split('\|', $tb_dynamic_tags); # Print Title line print $RPTFH "Benchmark,SimElapseTime,SimPeakMemUsed,"; foreach my $tag(@leakage_tags) { print $RPTFH "$tag,"; } if ("off" eq $opt_ptr->{sim_leakage_power_only}) { foreach my $tag(@dynamic_tags) { print $RPTFH "$tag,"; } } print $RPTFH "\n"; foreach my $benchmark(@benchmark_names) { print $RPTFH "$benchmark,"; print $RPTFH "$rpt_ptr->{$benchmark}->{$tbname_tag}->{total_elapsed_time},"; print $RPTFH "$rpt_ptr->{$benchmark}->{$tbname_tag}->{peak_mem_used},"; foreach my $tag(@leakage_tags) { print $RPTFH "$rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{$case_tag},"; } if ("off" eq $opt_ptr->{sim_leakage_power_only}) { foreach my $tag(@dynamic_tags) { print $RPTFH "$rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}->{$case_tag},"; } } print $RPTFH "\n"; } return; } sub gen_csv_rpt_one_case($ $) { my ($RPTFH, $case_tag) = @_; print $RPTFH "Monte Carlo case tag of the results: $case_tag\n"; if ("on" eq $opt_ptr->{parse_pb_mux_tb}) { print $RPTFH "***** pb_mux_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "pb_mux_tb", $conf_ptr->{csv_tags}->{pb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{pb_mux_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_cb_mux_tb}) { print $RPTFH "***** cb_mux_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "cb_mux_tb", $conf_ptr->{csv_tags}->{cb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{cb_mux_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_sb_mux_tb}) { print $RPTFH "***** sb_mux_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "sb_mux_tb", $conf_ptr->{csv_tags}->{sb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{sb_mux_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_lut_tb}) { print $RPTFH "***** lut_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "lut_tb", $conf_ptr->{csv_tags}->{lut_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{lut_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_hardlogic_tb}) { print $RPTFH "***** hardlogic_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "hardlogic_tb", $conf_ptr->{csv_tags}->{hardlogic_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{hardlogic_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_grid_tb}) { print $RPTFH "***** grid_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "grid_tb", $conf_ptr->{csv_tags}->{grid_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{grid_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_cb_tb}) { print $RPTFH "***** cb_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "cb_tb", $conf_ptr->{csv_tags}->{cb_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{cb_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_sb_tb}) { print $RPTFH "***** sb_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "sb_tb", $conf_ptr->{csv_tags}->{sb_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{sb_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } if ("on" eq $opt_ptr->{parse_top_tb}) { print $RPTFH "***** top_tb Results Table *****\n"; &gen_csv_rpt_one_tb_one_case($RPTFH, "top_tb", $conf_ptr->{csv_tags}->{top_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{top_tb_dynamic_power_tags}->{val}, $case_tag); print $RPTFH "\n"; } return; } sub process_mc_data_one_tag($) { my ($cur_mc_hash_ref) = @_; $cur_mc_hash_ref->{mc_total} = 0; $cur_mc_hash_ref->{mc_avg} = "na"; $cur_mc_hash_ref->{mc_max} = "na"; $cur_mc_hash_ref->{mc_min} = "na"; for (my $i = 1; $i < $cur_mc_hash_ref->{mc_cnt} + 1; $i++) { $cur_mc_hash_ref->{mc_total} += $cur_mc_hash_ref->{"mc".$i}; # Update max if (("na" eq $cur_mc_hash_ref->{mc_max}) ||($cur_mc_hash_ref->{mc_max} < $cur_mc_hash_ref->{"mc".$i})) { $cur_mc_hash_ref->{mc_max} = $cur_mc_hash_ref->{"mc".$i}; } # Update min if (("na" eq $cur_mc_hash_ref->{mc_min}) ||($cur_mc_hash_ref->{mc_min} > $cur_mc_hash_ref->{"mc".$i})) { $cur_mc_hash_ref->{mc_min} = $cur_mc_hash_ref->{"mc".$i}; } } # Get average if ( 0 < $cur_mc_hash_ref->{mc_cnt}) { $cur_mc_hash_ref->{mc_avg} = $cur_mc_hash_ref->{mc_total} / $cur_mc_hash_ref->{mc_cnt}; } return $cur_mc_hash_ref->{mc_cnt}; } # Count the number of Monte Carlo simulations, # and calculate the average, max and min sub process_mc_data_one_tb($ $ $) { my ($tbname_tag, $tb_leakage_tags, $tb_dynamic_tags) = @_; my (@leakage_tags) = split('\|', $tb_leakage_tags); my (@dynamic_tags) = split('\|', $tb_dynamic_tags); my ($mc_cnt, $mc_cnt_temp) = (0, 0); # Check each benchmark foreach my $benchmark(@benchmark_names) { foreach my $tag(@leakage_tags) { $mc_cnt_temp = &process_mc_data_one_tag($rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}); if (0 == $mc_cnt_temp) { print "Warning: zero results found in Monte Carlo simulations for $tag!\n"; } if (0 == $mc_cnt) { $mc_cnt = $mc_cnt_temp; } elsif ($mc_cnt != $mc_cnt_temp) { print "Warning: Inconsistent Monte Carlo Counter for (Benchmark:$benchmark; Testbench: $tbname_tag, Tag:$tag)\n"; } } # Only do the next part when sim_leakage is disabled if ("on" eq $opt_ptr->{sim_leakage_power_only}) { next; } foreach my $tag(@dynamic_tags) { $mc_cnt_temp = &process_mc_data_one_tag($rpt_ptr->{$benchmark}->{$tbname_tag}->{$tag}); if (0 == $mc_cnt) { $mc_cnt = $mc_cnt_temp; } elsif ($mc_cnt != $mc_cnt_temp) { print "Warning: Inconsistent Monte Carlo Counter for (Benchmark:$benchmark; Testbench: $tbname_tag, Tag:$tag)\n"; } } } return $mc_cnt; } sub process_mc_results() { my ($mc_cnt) = (0); if ("on" eq $opt_ptr->{parse_pb_mux_tb}) { $mc_cnt = &process_mc_data_one_tb("pb_mux_tb", $conf_ptr->{csv_tags}->{pb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{pb_mux_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_cb_mux_tb}) { $mc_cnt = &process_mc_data_one_tb("cb_mux_tb", $conf_ptr->{csv_tags}->{cb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{cb_mux_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_sb_mux_tb}) { $mc_cnt = &process_mc_data_one_tb("sb_mux_tb", $conf_ptr->{csv_tags}->{sb_mux_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{sb_mux_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_lut_tb}) { $mc_cnt = &process_mc_data_one_tb("lut_tb", $conf_ptr->{csv_tags}->{lut_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{lut_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_hardlogic_tb}) { $mc_cnt = &process_mc_data_one_tb("hardlogic_tb", $conf_ptr->{csv_tags}->{hardlogic_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{hardlogic_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_grid_tb}) { $mc_cnt = &process_mc_data_one_tb("grid_tb", $conf_ptr->{csv_tags}->{grid_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{grid_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_cb_tb}) { $mc_cnt = &process_mc_data_one_tb("cb_tb", $conf_ptr->{csv_tags}->{cb_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{cb_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_sb_tb}) { $mc_cnt = &process_mc_data_one_tb("sb_tb", $conf_ptr->{csv_tags}->{sb_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{sb_tb_dynamic_power_tags}->{val}); } if ("on" eq $opt_ptr->{parse_top_tb}) { $mc_cnt = &process_mc_data_one_tb("top_tb", $conf_ptr->{csv_tags}->{top_tb_leakage_power_tags}->{val}, $conf_ptr->{csv_tags}->{top_tb_dynamic_power_tags}->{val}); } return $mc_cnt; } sub gen_csv_rpt($) { my ($rpt_file) = @_; my ($RPTFH) = FileHandle->new; my ($mc_num) = (0); my ($rpt_dir_path, $rpt_filename) = &split_prog_path($rpt_file); &generate_path($rpt_dir_path); if ($RPTFH->open("> $rpt_file")) { print "INFO: print CVS report($rpt_file)...\n"; } else { die "ERROR: fail to create $rpt_file!\n"; } if ("on" eq $opt_ptr->{monte_carlo}) { # Preprocess the data $mc_num = &process_mc_results(); # Output starts if ("simple_rpt" eq $opt_ptr->{monte_carlo_val}) { &gen_csv_rpt_one_case($RPTFH, "mc_avg"); &gen_csv_rpt_one_case($RPTFH, "mc_max"); &gen_csv_rpt_one_case($RPTFH, "mc_min"); } else { # Output full report print $RPTFH "Number of Monte Carlo simulations: $mc_num\n\n"; &gen_csv_rpt_one_case($RPTFH, "mc_avg"); &gen_csv_rpt_one_case($RPTFH, "mc_max"); &gen_csv_rpt_one_case($RPTFH, "mc_min"); for (my $i = 1; $i < $mc_num + 1; $i++) { &gen_csv_rpt_one_case($RPTFH, "mc".$i); } } } else { &gen_csv_rpt_one_case($RPTFH, "avg"); } close($RPTFH); return; } # Main Program sub main() { &opts_read(); &read_conf(); &read_benchmarks(); &check_fpga_spice(); # Auto check the number tbs if ("on" eq $opt_ptr->{parse_results_only}) { &mark_all_fpga_spice_tasks_done(); &parse_all_tasks_results(); } else { &plan_run_tasks(); } &gen_csv_rpt($opt_ptr->{rpt_val}); } &main(); exit(0);