#!/usr/local/bin/perl -w
#============================================================================
# $Id: lib_merge,v 1.10 1999/08/16 13:07:44 duane Exp $
#
# Created by Duane Galbi  <duaneg@iname.com> 
# Copyright 1999 STMicroelectronics, Inc.
# This Software is the property of STMicroelectronics, Inc. ("ST") which
# specifically grants the user the right to use, modify, reproduce, publish,
# display, and distribute this software provided this notice is not removed
# or altered.  All other rights are reserved by ST.
#
# THIS SOFTWARE IS BEING PROVIDED BY ST "AS IS" AND ANY USER OF THIS 
# SOFTWARE WILL DO SO AT ITS OWN RISK. ST MAKES NO PREPRESENTATION OR
# WARRENTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION
# ANY IMPLIED WARRANTY OF MERCHANTABILITY OF FITNESS FOR ANY PARTICULAR
# PURPOSE.  IN ADDTION, IN NO EVENT SHALL ST BE LIABLE FOR ANY DIRECT,
# INCIDENTAL, OR CONSEQUENTIAL DAMAGES IN CONNECTION WITH OR ARISING
# FROM THE FURNISHING, PERFORMANCE, OR USE OF THIS SOFTWARE.
#============================================================================
#   Merges the results from the spice simulation back into the
#   template file to create the final cell library file
#============================================================================
if (@ARGV == 0) {
HELPMSG:
    print <<EOUSAGE;
Version: 1.0
Usage: lib_merge <cell-template> <sim-result> [options]
     
    where options are:
       -o<file>    = file to write output (instead of STDOUT)
       -h<file>    = header files (output before anything else)  
       -el         = output end of library bracket line
       -h          = print out this message
       +ha=<value> = add <value> to all the HOLD and REMOVAL times

EOUSAGE
    exit(0);
}

$outfile = ">-";        # stdout file
$cellfile = "";
$simfile = "";
$headfile = "";
$endlibrary = "";
$hold_add_time = 0;

while ($arg = shift) {
    if ($arg eq "?" || $arg eq "-h"){
	goto HELPMSG;
    }elsif ($arg =~ /^-o(.+)/) {
	$outfile = $1;
    }elsif ($arg =~ /^-h(.+)/) {
	$headfile = $1;
    }elsif ($arg eq "-el"){
        $endlibrary = 1;
    }elsif ($arg =~ /^\+ha\=(.+)/){
        $hold_add_time = $1;
    }else{
	if (!$cellfile){
	    $cellfile = $arg;
        }elsif (!$simfile){
	    $simfile = $arg;
	}else{
	    die "ERROR: Bad input argument: $arg";
	}
    }
}

die "ERROR: No cell-template file was specified" if (!$cellfile);
die "ERROR: No simulation file was specified" if (!$simfile);

open(CELLFILE,$cellfile) || die "ERROR: Cannot open cell-input file: $cellfile";
open(SIMFILE,"$simfile") || die "ERROR: Cannot open sim-input file: $simfile";
open(OUTFILE,">$outfile") || die "ERROR: Cannot open output file: $outfile";

$debug = 0;     ## set to 1 for debug

if ($debug){
  print "cellfile=$cellfile  simfile=$simfile  outfile=$outfile\n";
}

##===============================================================================
##         Some useful functions
##===============================================================================

#
#  add and offset to the values present (skip -1 elements) 
#
sub add_delay {
    local($time_list,$hold_add_time) = @_;
    local(@tlist,$telm,$out_list);

    if ($time_list =~ /.*[,;].*/){
	die "ERROR: invalid time_list for add_delay function\n";
    }
    @tlist = split /\s+/, $time_list;
    $out_list = "";
    foreach $telm (@tlist){
	if ($telm != "-1"){
	  $telm = $telm + $hold_add_time;
        }
	if ($out_list ne ""){ $out_list .= " "; }
	$out_list .= "$telm";
    }
    return "$out_list";
}


#
#  checks for -1 in the time field 
#
sub check_time {   
    local ($time_str) = @_;
    local ($tline,@tlist,$telm,$quote);
    $quote = chr 34;  ## the quote character

    $time_str =~ s/[\n\\,$quote]/ /g;
    $time_str =~ s/[ ]+/ /g;
    if ($time_str =~ /.*\values[\s]*\((.*)\);.*values[\s]*\((.*)\);.*/){
	$tline = "$1 $2";
    }elsif ($time_str =~ /.*\values[\s]*\((.*)\);.*/){
	$tline = "$1";
    }else{
	die "ERROR: Can't find timing values in line ($time_str)\n";
    }

    $tline =~ s/[,;]+/ /g;
    $tline =~ s/^\s+//g;
    @tlist = split /\s+/, $tline;
    foreach $telm (@tlist){
	if ($telm eq "-1"){
	    die "ERROR: -1 time value\n";
	}
    }
}


sub build_matrix {
    local ($header,$dmatrix) = @_;
    local ($hspace,$cnt,$quote,$dline,$cnt2,$dval,$dval_f,$cline);

    if ($debug && 1==2){
	print "HEADER: $header\n";
        print "DATA: $dmatrix\n";
    }
    $hspace = "                 ";
    $cnt = 0;
    $quote = chr 34;  ## the quote character
    $dmatrix =~ s/^[\s]+//;
    foreach $dline (split /,[\s]*/, $dmatrix){
	$dline =~ s/^[\s]+//;
	$cnt = $cnt + 1;
	if ($cnt!=1){ $cline .= "${quote}, \\\n${hspace}";}
	$cline .= $quote;
	$cnt2 = 0;
	foreach $dval (split /[\s]+/,$dline){
	    $cnt2 = $cnt2 + 1;
	    if ($cnt2!=1){ $cline .= ", ";}
            $dval_f = sprintf "%-7s", $dval;
            $dval_f =~ s/ /0/g;
	    $cline .= $dval_f;
	}
    }
    $cline .= $quote." );\n      }\n";
    return "$header$cline";
}

##===============================================================================
##                        Main Loop
##===============================================================================

if ($headfile){
  open(HEADFILE,"$headfile") || die "ERROR: Cannot open head-input file: $headfile";
  while( <HEADFILE> ){
    print OUTFILE;
  }
  close HEADFILE;
}

%cap_val = ();
%time_val = ();
%ff_val = ();
$lnum = 0;
while (<SIMFILE>) {
   $lnum = $lnum + 1;
   ($comment,$type,$table) = split;
   if (/^\/\/C/){
       if ($debug){print "Found cap table = $_\n";} 
       $_ = <SIMFILE> || die "ERROR: Cap Line ($lnum) doesn't exit\n";
       s/^[\s]+//;
       @ppair = split /,[\s]*/;
       foreach $ppair (@ppair){
           if ($debug) {print "Cap Pair: $ppair\n";}
	   ($pin,$pcap) = split /[\s]+/,$ppair;
           if (defined $cap_val{$pin}){
	     die "ERROR: Multiple capacitance entries for pin: $pin\n";
           }
	   $cap_val{$pin} = $pcap;
       }
   }elsif (/^\/\/F/){
       if ($debug){print "Found ff table ($type) \n";} 
       $_ = <SIMFILE> || die "ERROR: Timing Line ($lnum) doesn't exist\n";

       s/^[\s]+//;
       ($cell_delay, $tran_time, $edge) = split /;[\s]+/;
       if (!defined $cell_delay){ $cell_delay = ""; }       
       if (!defined $tran_time){ $tran_time = ""; }       
       if ($tran_time eq "" && $cell_delay eq ""){
	   die "ERROR (line $lnum): tran-time and cell-delay both empty\n";
       }
       $type =~ s/[\s]+//g;
       ($tpin,$tvalue,$tmtype) = split /_/,$type;
       if ($tvalue ne "1" && $tvalue ne "0"){
	   $tmtype = "";
       }
       $tpin = "";

       if ($tmtype ne ""){
           if ($debug){ print "Handling constraint $tmtype\n"; }
           # handling a constraint value
           if ($tmtype ne "HOLD" && $tmtype ne "SETUP" && 
               $tmtype ne "REM" && $tmtype ne "REC"){
	       die "ERROR (line $lnum): bad result type ($tmtype) in .sim_stuf file\n";
           }
           if ($tmtype eq "SETUP"){
               #looking at asserting edge
	       if ($tvalue eq "0"){ $edge = "F";
	       }else{ $edge = "R"; }
           }else{
	       if ($tvalue eq "0"){ $edge = "R";
	       }else{ $edge = "F"; }
	   }
           die "ERROR: internal ff tran-time error\n" if ($tran_time ne "");
	   if ($edge =~ /R/){
	       $chead = "      rise_constraint($table) {\n        values ( ";
           }else{
	       $chead = "      fall_constraint($table) {\n        values ( ";
           }
	   if ($tmtype eq "HOLD" || $tmtype eq "REM"){
	       $cell_delay = &add_delay($cell_delay,$hold_add_time);
	   }
       }else{ 
	   if ($edge =~ /R/){
	       $thead = "      rise_transition($table) {\n        values ( ";
	       $chead = "      cell_rise($table) {\n        values ( ";
	   }else{
	       $thead = "      fall_transition($table) {\n        values ( ";
	       $chead = "      cell_fall($table) {\n        values ( ";
	   }
       }
       $cline = $tline = "";
       if ($cell_delay ne ""){ $cline = &build_matrix($chead,$cell_delay); } 
       if ($tran_time ne ""){ $tline = &build_matrix($thead,$tran_time); }

       if (defined $ff_val{$type}{TIME}){
	   die "ERROR: Multiple timing entries for: $type\n";
       }
       $ff_val{$type}{TIME} = "${cline}${tline}";
       $ff_val{$type}{TABLE} = $table;
       if ($debug && 1==2){
	   print "FF $type LINE: $ff_val{$type}{TIME}\n";
       }
   }elsif (/^\/\/T/){
       if ($debug){print "Found time table = $_\n";} 
       $_ = <SIMFILE> || die "ERROR: FF Timing Line ($lnum) doesn't exist\n";

       s/^[\s]+//;
       ($cell_delay, $tran_time, $edge) = split /;[\s]+/;
       if ($edge =~ /R/){
	   $thead = "      rise_transition($table) {\n        values ( ";
	   $chead = "      cell_rise($table) {\n        values ( ";
       }else{
	   $thead = "      fall_transition($table) {\n        values ( ";
	   $chead = "      cell_fall($table) {\n        values ( ";
       }
       $cline = &build_matrix($chead,$cell_delay); 
       $tline = &build_matrix($thead,$tran_time); 
       if (defined $time_val{$type}{TIME}){
	   die "ERROR: Multiple timing entries for: $type\n";
       }
       $time_val{$type}{TIME} = "${cline}${tline}";
       $time_val{$type}{TABLE} = $table;
   }elsif (/^\/\/=/){
       if ($debug){print "Found comment\n"}; 
   }elsif (!(/[\s]*/)){
       die "ERROR: Bad cell input line($lnum): $_\n";
   }
}
close SIMFILE;

if ($debug) { print "Starting to process template file\n";}   			
$did_cap_flag = 0;
while(<CELLFILE>){
    $iline = $_;
    s/^[\s]+//;
    if ($did_cap_flag && /^capacitance[\s]*:/){ 
      next;     ## skip capacitance line following cap value insertions 
    } 
    $did_cap_flag = 0;
    ($comment,$pin) = split;
    if (!defined $comment){ $comment = ""; }
    if (!defined $pin){ $pin = ""; }

    $pin =~ s/,[\s]*//;
    $pin =~ tr/[a-z]/[A-Z]/;
    if ($comment eq "/*G:" || $comment eq "/*g"){
	if ($debug) {print "Process time timing for: $pin\n";}
	print OUTFILE $iline;
	$tline = $time_val{$pin}{TIME};
	check_time($tline);
	print OUTFILE $tline;
    }elsif ($comment eq "/*C:"){
	if ($debug) {print "Process cap for pin: $pin\n";}
        $did_cap_flag = 1;
	print OUTFILE $iline;
        $pcap = $cap_val{$pin};
	print OUTFILE "    capacitance : $pcap ;\n";
    }elsif ($comment eq "/*F:" || $comment eq "/*f"){
	if ($debug) {print "Process FF timing for: $pin\n";}
        $iline =~ s!=\*/!=-/!g;        # fixup wildcard lines 
        $iline =~ s!=(.)/\*!=$1/-!g;   # fixup wildcard lines
	print OUTFILE $iline;
	$tline = $ff_val{$pin}{TIME};
	check_time($tline);
	print OUTFILE $tline;
    }else{
	print OUTFILE $iline;
    }
}
close CELLFILE;

if ($endlibrary){
    print OUTFILE "} /* end of library */\n";
}
close OUTFILE;
