#! /usr/local/bin/perl

# creates a plot of the percentage of paths
# that have slack ratio less than a given amount

# Steve Golson -- Trilobyte Systems -- sgolson@trilobyte.com
# @(#)ratio_plot	1.6 01/18/05 21:46:21

require 5.004;
use Getopt::Long;

# TODO
# 1. check font sizes of legend, yaxis
# 3. need to verify nworst and max_paths and period >0
# 4. need a way to reset nworst and max_paths to 'all'
# 5. need a way to reset period to read from file
# 6. need a better filter for bogus paths: required<epsilon?

###########################################################################
# check arguments

$myname = `basename $0`;
chop $myname ;
$argstring = $myname;
foreach $arg (@ARGV) {
    if ( split(" ",$arg) > 1) { $argstring .= " ".quotemeta($arg); }
    else                      { $argstring .= " ".$arg; }
}
$datestring = `date`;
chop $datestring ;

##### subroutine for handling non-option arguments

sub getfile {
    $label = $_[0] unless defined($label);

    push @thefiles,  $_[0];
    push @thelabels, $label;
    push @thenworst, $nworst;
    push @themax_paths, $max_paths;
    push @theperiods, $period;

    undef $label;

    return;
}

##### get arguments and options

$result = &GetOptions(
	"type=s",\$plot_type,
	"title=s",\$title,
	"date",\$date,
	"nworst=i",\$nworst,
	"max_paths=i",\$max_paths,
	"period=f",\$period,
	"label=s",\$label,
	"color!",\$color,
	"xmin=f",\$xmin,
	"xmax=f",\$xmax,
	"<>",\&getfile) ;

##### set option defaults

$plot_type = "x" unless $plot_type ;

##### print usage on error

unless ($result and
	(scalar(@thefiles)>0) and
	($plot_type eq "ps" or $plot_type eq "eps" or $plot_type eq "x")) {

	select STDERR ;
	print <<EOF;
Usage: $myname timing_report_file...

  Options:
    -title     <string>      plot title, defaults to no title
    -type      <ps|eps|x>    defaults to x
    -date                    prints time and date
    -nworst    <integer>     number of paths per endcell, defaults to show all
                             may be specified multiple times
                             all following files will use this value
    -max_paths <integer>     number of total paths, defaults to show all
                             may be specified multiple times
                             all following files will use this value
    -period    <real>        overrides "Path Required" values
    -label     <string>      label for the following timing_report_file,
                             defaults to filename
    -color                   plots solid color lines, default is colored dashed lines
    -nocolor                 plots monochrome dashed lines, default is colored dashed lines
    The following two options select the area to be plotted:
    -xmin     <real>         defaults to smallest slack ratio
    -xmax     <real>         defaults to 1.0
EOF
	exit 1 ;
	}

##### set misc variables depending on options

if (!defined($color)) {
  # default
  $style = "color dashed";
  @linetypes = (1, 2, 3, 4, 5, 7, 8, 9);
  }
elsif ($color == 1) {
  # color only
  $style = "color solid";
  @linetypes = (1, 2, 3, 4, 5, 7, 8, 9);
  }
else {
  # monochrome only
  $style = "monochrome dashed";
  @linetypes = (1, 2, 4, 5, 6, 7, 8, 9);
  }

###########################################################################

##### print the gnuplot script

select STDOUT ;

##### first print a header

print <<EOF;
# gnuplot script file to make a percentage plot of slack ratios
# generated by
#   $argstring
# at
#   $datestring

set xlabel "slack ratio"
set ylabel "paths below the given slack ratio"
set format y "%g%%"
set yzeroaxis
set key graph 0.80,0.50

EOF

# print the optional title

if ($title) {
	print <<EOF
set title "$title"

EOF
	}

# print the optional timestamp

if ($date) {
	print <<EOF
set timestamp "plotted: $datestring" 0,-3 "Helvetica,10"

EOF
	}

# print the optional xrange

if (defined($xmin) or defined($xmax)) {
	print <<EOF
set xrange [$xmin:$xmax]

EOF
	}

##### variable stuff depending on type of plot

if ($plot_type eq "x") {
	print <<EOF;
# output type is x
set size 1,1
EOF
	}
elsif ($plot_type eq "ps") {
	print <<EOF;
# output type is ps
set terminal postscript landscape $style "Helvetica" 22
set size 1,1
EOF
	}
else { # assume plot_type "eps"
	print <<EOF;
# output type is eps
set terminal postscript eps $style "Helvetica" 22
set size 1.3,1.1
EOF
	}

##### issue the plot command

print <<EOF;
plot \\
EOF

    while (@thelabels) {
	$label = shift @thelabels;
	$lt = shift @linetypes;
	push @linetypes, $lt;
	print "        \"-\" using 1:2 title \"$label\" with lines lt $lt";
	if (@thelabels == 0) { print "\n"; }
	else { print ", \\\n"; }
    }

###########################################################################

##### process the timing report files

while (@thefiles) {

$timing_report_file = shift @thefiles;
$nworst = shift @thenworst;
$max_paths = shift @themax_paths;
$period = shift @theperiods;

# check file status

open (FILE, "$timing_report_file") or die "Could not open file $timing_report_file : $!\n" ;

# get the slack ratios

$min_slack_ratio = 200 ;
$total_paths = 0 ;
$total_paths_plotted = 0 ;
undef %bins;
undef %endcellnames;
undef %endpointnames;
$max_paths_to_endcell = 0;
$endcell_with_most_paths = "";

# skip the preamble
while (<FILE>) {
	if ($_ =~ /^Endpoint\s+Path Delay\s+Path Required\s+Slack/) { last ; }
	}

# skip one more line
$_ = <FILE> ;

while (<FILE>) {

	@line = split ;

	$endpoint = $line[0] ;
	$celltype = $line[1] ;
	$delay = $line[2] ;
	$risefall = $line[3] ;
	$required = $line[4] ;
	$slack = $line[5] ;

	$required = $period if (defined($period));

	if ($required==0) { next ; }

	$endpointnames{$endpoint}++;

	if ($celltype eq "(out)") {
	    $endcellname = $endpoint;
	    $endpinname = "";
	} else {
	    ($endcellname, $endpinname) = ($endpoint =~ m|^(.*)/([^/]*)$| );
	}

	$endcellnames{$endcellname}++;
	$num_of_paths = $endcellnames{$endcellname};

	if ($num_of_paths > $max_paths_to_endcell) {
	    $max_paths_to_endcell = $num_of_paths;
	    $endcell_with_most_paths = $endcellname;
	}

	$total_paths++ ;

	next if (defined($nworst)    and ($num_of_paths > $nworst));
	next if (defined($max_paths) and ($total_paths > $max_paths));

	$slack_ratio = int(100 * $slack / $required) ;

	if ($min_slack_ratio > $slack_ratio) {
		$min_slack_ratio = $slack_ratio ;
		}

	$bins{$slack_ratio}++ ;

	$total_paths_plotted++ ;
	}

$total_endpoints = keys %endpointnames;
$total_endcells = keys %endcellnames;

##### print out the data

print "# datafile $timing_report_file\n" ;
if (defined($nworst))    { print "#   -nworst is $nworst\n" ; }
else                     { print "#   -nworst is all\n" ; }
if (defined($max_paths)) { print "#   -max_paths is $max_paths\n" ; }
else                     { print "#   -max_paths is all\n" ; }
print "# total endcells in file $total_endcells\n" ;
print "# total endpoints in file $total_endpoints\n" ;
print "# total paths in file $total_paths\n" ;
print "# total paths plotted $total_paths_plotted\n" ;
print "# max paths to one cell is $max_paths_to_endcell paths to cell $endcell_with_most_paths\n" ;

$sum = 0 ;
printf("%f\t%f\n",
       $min_slack_ratio / 100,
       0 ) ;
for ($index = $min_slack_ratio ; $index <= 100 ; $index++ ) {
	$sum += $bins{$index} ;
	printf("%f\t%f\n",
		$index / 100,
		100 * $sum / $total_paths_plotted ) ;
	}

print "e\n" ;

close (FILE);

}

###########################################################################
