#! /usr/local/bin/perl

# creates an xy scatter plot of slack ratios
# from two sets of endpoint timing data

# Steve Golson -- Trilobyte Systems -- sgolson@trilobyte.com
# @(#)scatter_plot	1.5 11/28/00 21:25:53

require 5.004;
use Getopt::Long;
use POSIX;

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

$myname = `basename $0`;
chop $myname ;
$argstring = $myname . " " . join(" ",@ARGV) ;
$datestring = `date`;
chop $datestring ;

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

$number_of_files=0;
sub getfile {
	$number_of_files++ ;
	unless ($x_timing_report_file) {
		$x_timing_report_file = $_[0] ;
		return; 
		}
	unless ($y_timing_report_file) {
		$y_timing_report_file = $_[0] ;
		return; 
		}
	warn "Too many filenames";
	return 0 ;
	}

##### get arguments and options

$result = &GetOptions(
	"title=s",\$title,
	"number",\$title_number,
	"xlabel=s",\$xlabel,
	"ylabel=s",\$ylabel,
	"datafile=s",\$datafile,
	"type=s",\$plot_type,
	"style=s",\$plot_style,
	"zoom=i",\$zoom,
	"date",\$date,
	"<>",\&getfile) ;

##### set option defaults

$xlabel = $x_timing_report_file unless $xlabel ;
$ylabel = $y_timing_report_file unless $ylabel ;
$datafile = "/tmp/$myname.$$.dat" unless $datafile ;
$plot_type = "x" unless $plot_type ;
$plot_style = "dots" unless $plot_style ;
$zoom = 1 unless $zoom ;

##### print usage on error

unless ($result and
	($number_of_files==2) and
	($plot_type eq "ps" or $plot_type eq "eps" or $plot_type eq "x") and
	($plot_style eq "dots" or $plot_style eq "points")
	) {

	select STDERR ;
	print <<EOF;
Usage: $myname x_timing_report_file y_timing_report_file
  Options:
    -title    <string>      defaults to no title
    -number                 title shows number of endpoints
    -xlabel   <string>      defaults to x_timing_report_file
    -ylabel   <string>      defaults to y_timing_report_file
    -datafile <filename>    defaults to /tmp/$myname.\$\$.dat
    -type     <ps|eps|x>    defaults to x
    -style    <dots|points> defaults to dots
    -zoom     <integer>     defaults to 1
    -date                   prints time and date
EOF
	exit ;
	}

##### check file status

open (XFILE, "$x_timing_report_file") or die "Could not open file $x_timing_report_file : $!\n" ;
open (YFILE, "$y_timing_report_file") or die "Could not open file $y_timing_report_file : $!\n" ;
open (DATAFILE, ">$datafile") or die "Could not open file $datafile : $!\n" ;

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

##### process the x-axis timing report file

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

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

while (<XFILE>) {

	@line = split ;

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

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

	$slack_ratio = $slack / $required ;

	push @{ $xfile_ratio{$endpoint} }, [ $slack_ratio, $required ]  ;
	}

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

##### process the y-axis timing report file

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

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

while (<YFILE>) {

	@line = split ;

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

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

	$slack_ratio = $slack / $required ;

	push @{ $yfile_ratio{$endpoint} }, [ $slack_ratio, $required ] ;
	}

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

##### print the data file

select DATAFILE ;

##### first print a header

print <<EOF;
# data file
#   $datafile
# generated by
#   $argstring
# at
#   $datestring
#
EOF

##### now the data

$number_of_endpoints = 0 ;
$smallest_xratio = 1.0 ;
$smallest_yratio = 1.0 ;

for $endpoint (keys %yfile_ratio) {

	@xfile = @{ $xfile_ratio{$endpoint}} ;
	@yfile = @{ $yfile_ratio{$endpoint}} ;

	$number_of_xfile_paths = scalar @xfile ;
	$number_of_yfile_paths = scalar @yfile ;

	# Because of path grouping, it is possible to have >1 path to a given
	# endpoint. We need to have some way of sorting by required path value.
	# But for now, just print a warning.

	# print warning if >1 paths to a given endpoint
	# or if xfile and yfile have different numbers of paths to a given endpoint
	if ($number_of_xfile_paths > 1
	    or
	    $number_of_yfile_paths > 1
	    or
	    $number_of_yfile_paths != $number_of_xfile_paths) {
		printf("# xfile paths %d yfile paths %d for endpoint %s\n",
				$number_of_xfile_paths,
				$number_of_yfile_paths,
				$endpoint) ;
		next ;
		}

	@xfile_ratio = ($xfile[0][0]) ;
	@yfile_ratio = ($yfile[0][0]) ;

	# print the ratios

	for ($index = 0 ; $index < $number_of_yfile_paths ; $index++) {
		printf("%f\t%f\t%s\t%d\n",
			$xfile_ratio[$index],
			$yfile_ratio[$index],
			$endpoint,
			$index) ;
		$number_of_endpoints++ ;
		if ($xfile_ratio[$index] < $smallest_xratio) {
			$smallest_xratio = $xfile_ratio[$index] ;
			}
		if ($yfile_ratio[$index] < $smallest_yratio) {
			$smallest_yratio = $yfile_ratio[$index] ;
			}
		}

	}

##### what about keys that are only in xfile? need to print a message about them

print  "#\n" ;
print  "# $number_of_endpoints endpoints\n" ;
printf "# %f is smallest x ratio\n", $smallest_xratio ;
printf "# %f is smallest y ratio\n", $smallest_yratio ;

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

##### print the gnuplot script

select STDOUT ;

$xmax = 1.0 ;
$ymax = 1.0 ;

$xmin = floor($smallest_xratio) ;
$ymin = floor($smallest_yratio) ;

if ($xmin > -1.0) { $xmin = -1.0 ; }
if ($ymin > -1.0) { $ymin = -1.0 ; }

$orientation = "portrait" ;
if ($xmin < $ymin) { $orientation = "landscape" ; }

$xmin = sprintf("%.1f",$xmin) ;
$xmax = sprintf("%.1f",$xmax) ;
$ymin = sprintf("%.1f",$ymin) ;
$ymax = sprintf("%.1f",$ymax) ;

#????? need to use $zoom somehow

##### first print a header

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

set size ratio -1

set xlabel "$xlabel"
set ylabel "$ylabel"

set xrange [$xmin:$xmax]
set yrange [$ymin:$ymax]

set zeroaxis

EOF

# print the optional title

if ($title_number) {
	if ($title) { $title .= "\\n" ; }
	$title .= commify($number_of_endpoints)." endpoints" ;
	}

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
	}

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

if ($plot_type eq "x") {
	print <<EOF;
# output type is x
EOF
	}
elsif ($plot_type eq "ps") {
	print <<EOF;
# output type is ps
set terminal postscript $orientation
EOF
	}
else { # assume plot_type "eps"
	print <<EOF;
# output type is eps
set terminal postscript eps
EOF
	}

##### issue the plot command

print <<EOF;
plot \\
        x notitle with lines, \\
        "$datafile" using 1:2 notitle with $plot_style
EOF

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

sub commify {
	my $text = reverse $_[0] ;
	$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
	return scalar reverse $text ;
	}

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

