/*
 * This file is part of the Alliance CAD System
 * Copyright (C) Laboratoire LIP6 - Département ASIM
 * Universite Pierre et Marie Curie
 *
 * Home page          : http://www-asim.lip6.fr/alliance/
 * E-mail             : mailto:alliance-users@asim.lip6.fr
 *
 * This progam is  free software; you can redistribute it  and/or modify it
 * under the  terms of the GNU  General Public License as  published by the
 * Free Software Foundation;  either version 2 of the License,  or (at your
 * option) any later version.
 *
 * Alliance VLSI  CAD System  is distributed  in the hope  that it  will be
 * useful, but WITHOUT  ANY WARRANTY; without even the  implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy  of the GNU General Public License along
 * with the GNU C Library; see the  file COPYING. If not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*
 * Tool    : LooN - special netlist for timing dependancies
 * Date    : 2000
 * Author  : Francois Donnet
 */


#include <mut.h>
#include <mlo.h>
#include <abl.h>
#include <abe.h>
#include <aut.h>
#include "lon_lax_param.h"
#include "lon_lib_utils.h"
#include "lon_signal_utils.h"
#include "lon_signal_name.h"
#include "lon_signal_netlist.h"



/***************************************************************************/
/* increment(if 1) or decrement(if 0) the capacitance on signals branched  */
/* to loins from the values of its ports                                   */
/***************************************************************************/
extern void loins_capacitance(loins_list* loins, int increment)
{
   port_list *port;
   cell_list* cell;
   locon_list* locon;
   losig_list* losig;
   char* signame;

   cell=getCell(loins->FIGNAME);
   if (!cell) {
      fprintf(stderr,"library error: no model '%s.vbe' found\n",
      loins->FIGNAME);
      autexit(1);
   }
   
   for (locon=loins->LOCON; locon; locon=locon->NEXT) {
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' in figure '%s' isn't accepted\n",
         locon->NAME,loins->INSNAME);
         autexit(1);
      }
      /*seek port which match with netlist*/
      for (port=cell->PORT; port; port=port->NEXT) {
         if (port->NAME==locon->NAME) break;
      }
      if (!port) {
         fprintf(stderr,
         "loins_capacitance: connector discrepancy between '%s' and '%s'\n",
         cell->NAME,loins->FIGNAME);
         autexit(1);
      }
      
      losig=locon->SIG;
      if (!losig->NAMECHAIN) {
         fprintf(stderr,"loins_capacitance: no losig name\n");
         autexit(1);
      }
      signame=losig->NAMECHAIN->DATA;
      if (!isvdd(locon->NAME) && !isvss(locon->NAME) 
       && (locon->DIRECTION==IN || locon->DIRECTION==INOUT 
        || locon->DIRECTION==TRANSCV)) 
         inccapacitance(signame, increment!=0 ? port->C : -port->C );
   }
}


/***************************************************************************/
/* return the Time of a loins                                              */
/***************************************************************************/
extern double loins_max_T(loins_list* loins)
{
   cell_list* cell;
   char* ck=NULL;
   int flip_flop=0;
   double delay, max_delay=-1;
   ptype_list* ptype;
   port_list* port;
   locon_list* locon;
   biabl_list* biabl;
   
   if (!loins) {
      fprintf(stderr,"loins_max_T: NULL pointer\n");
      autexit(1);
   }
   
   /*cell properties*/
   cell=getCell(loins->FIGNAME);
   if (!cell) {
      fprintf(stderr,"library error: no cell '%s.vbe' found\n",
      loins->FIGNAME);
      autexit(1);
   }
   
   /*is-it a flip-flop?*/
   if (cell->BEFIG->BEREG) {
      for ( biabl = cell->BEFIG->BEREG->BIABL; biabl; biabl = biabl->NEXT )
      {
         ptype=getptype(biabl->USER,ABL_STABLE);
         if (ptype) 
         {
            flip_flop=1;
            ck=(char*) ptype->DATA;
            break;
         }
      }
   }
   
   /*eval delay*/
   for (locon=loins->LOCON; locon; locon=locon->NEXT) {
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' in figure '%s' isn't accepted\n",
         locon->NAME,loins->INSNAME);
         autexit(1);
      }
      /*eval delay on input dependancy*/
      if (isvdd(locon->NAME) || isvss(locon->NAME)) continue;
      if (locon->DIRECTION==OUT || locon->DIRECTION==TRISTATE) continue;
      /*only clock accepted for flip-flop*/
      if (flip_flop && ck!=locon->NAME) continue;
      
      /*seek port which match with netlist*/
      for (port=cell->PORT; port; port=port->NEXT) {
         if (port->NAME==locon->NAME) break;
      }
      if (!port) {
         fprintf(stderr,
         "loins_max_T: connector discrepancy between '%s' and '%s'\n",
         cell->NAME,loins->FIGNAME);
         autexit(1);
      }
      
      if (!locon->SIG->NAMECHAIN) {
         fprintf(stderr,"loins_max_T: no losig name\n");
         autexit(1);
      }
    //input=(char*) locon->SIG->NAMECHAIN->DATA;
      
      /*finaly eval delay*/
      delay=port->T;
      if (delay>max_delay) max_delay=delay;
   }

   return max_delay;
}


/***************************************************************************/
/* return the RC of a loins. output is the name of the signal drived       */
/***************************************************************************/
extern double loins_max_RC(loins_list* loins, char* output)
{
   cell_list* cell;
   char* ck=NULL;
   int flip_flop=0;
   double delay, max_delay=-1;
   ptype_list* ptype;
   port_list* port;
   locon_list* locon;
   biabl_list* biabl;
   
   if (!loins || !output) {
      fprintf(stderr,"loins_max_RC: NULL pointer\n");
      autexit(1);
   }
   
   /*cell properties*/
   cell=getCell(loins->FIGNAME);
   if (!cell) {
      fprintf(stderr,"library error: no cell '%s.vbe' found\n",
      loins->FIGNAME);
      autexit(1);
   }
   
   /*is-it a flip-flop?*/
   if (cell->BEFIG->BEREG) {
      for ( biabl = cell->BEFIG->BEREG->BIABL; biabl; biabl = biabl->NEXT )
      {
         ptype=getptype(biabl->USER,ABL_STABLE);
         if (ptype) 
         {
            flip_flop=1;
            ck=(char*) ptype->DATA;
            break;
         }
      }
   }
   
   /*eval delay*/
   for (locon=loins->LOCON; locon; locon=locon->NEXT) {
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' in figure '%s' isn't accepted\n",
         locon->NAME,loins->INSNAME);
         autexit(1);
      }
      /*eval delay on input dependancy*/
      if (isvdd(locon->NAME) || isvss(locon->NAME)) continue;
      if (locon->DIRECTION==OUT || locon->DIRECTION==TRISTATE) continue;
      /*only clock accepted for flip-flop*/
      if (flip_flop && ck!=locon->NAME) continue;
      
      /*seek port which match with netlist*/
      for (port=cell->PORT; port; port=port->NEXT) {
         if (port->NAME==locon->NAME) break;
      }
      if (!port) {
         fprintf(stderr,
         "loins_max_RC: connector discrepancy between '%s' and '%s'\n",
         cell->NAME,loins->FIGNAME);
         autexit(1);
      }
      
      if (!locon->SIG->NAMECHAIN) {
         fprintf(stderr,"loins_max_RC: no losig name\n");
         autexit(1);
      }
    //input=(char*) locon->SIG->NAMECHAIN->DATA;
      
      /*finaly eval delay*/
      delay=port->R*getcapacitance(output);
      if (delay>max_delay) max_delay=delay;
   }

   return max_delay;
}


/***************************************************************************/
/* return the delay of a loins. output is the name of the signal drived    */
/***************************************************************************/
extern double loins_delay(loins_list* loins, char* output)
{
   cell_list* cell;
   char* ck=NULL;
   int flip_flop=0;
   double delay, max_delay=-1;
   ptype_list* ptype;
   port_list* port;
   locon_list* locon;
   char* input;
   biabl_list* biabl;
   
   if (!loins || !output) {
      fprintf(stderr,"loins_delay: NULL pointer\n");
      autexit(1);
   }
   
   /*cell properties*/
   cell=getCell(loins->FIGNAME);
   if (!cell) {
      fprintf(stderr,"library error: no cell '%s.vbe' found\n",
      loins->FIGNAME);
      autexit(1);
   }
   
   /*is-it a flip-flop?*/
   if (cell->BEFIG->BEREG) {
      for ( biabl = cell->BEFIG->BEREG->BIABL; biabl; biabl = biabl->NEXT )
      {
         ptype=getptype(biabl->USER,ABL_STABLE);
         if (ptype) 
         {
            flip_flop=1;
            ck=(char*) ptype->DATA;
            break;
         }
      }
   }
   
   /*eval delay*/
   for (locon=loins->LOCON; locon; locon=locon->NEXT) {
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' in figure '%s' isn't accepted\n",
         locon->NAME,loins->INSNAME);
         autexit(1);
      }
      /*eval delay on input dependancy*/
      if (isvdd(locon->NAME) || isvss(locon->NAME)) continue;
      if (locon->DIRECTION==OUT || locon->DIRECTION==TRISTATE) continue;
      /*only clock accepted for flip-flop*/
      if (flip_flop && ck!=locon->NAME) continue;
      
      /*seek port which match with netlist*/
      for (port=cell->PORT; port; port=port->NEXT) {
         if (port->NAME==locon->NAME) break;
      }
      if (!port) {
         fprintf(stderr,
         "loins_delay: connector discrepancy between '%s' and '%s'\n",
         cell->NAME,loins->FIGNAME);
         autexit(1);
      }
      
      if (!locon->SIG) {
         fprintf(stderr,"loins_delay: no losig\n");
         autexit(1);
      }
      if (!locon->SIG->NAMECHAIN) {
         fprintf(stderr,"loins_delay: no losig name\n");
         autexit(1);
      }
      input=(char*) locon->SIG->NAMECHAIN->DATA;
      
      /*finaly eval delay*/
      delay=getdelay(input)+port->T+port->R*getcapacitance(output);
      if (delay>max_delay) max_delay=delay;
   }

   return max_delay;
}


/***************************************************************************/
/* return the delay of  a losig                                            */
/***************************************************************************/
extern double losig_delay(losig_list* losig)
{
   ptype_list* ptype;
   loins_list* loins;
   locon_list* locon;
   double delay, max_delay=-1;
   char* signame;
   chain_list* lofigchain;

   if (!losig) {
      fprintf(stderr,"losig_delay: NULL pointer\n");
      autexit(1);
   }
   
   if (!losig->NAMECHAIN) {
      fprintf(stderr,"losig_delay: no losig name\n");
      autexit(1);
   }
   signame=losig->NAMECHAIN->DATA;

   ptype=getptype(losig->USER,LOFIGCHAIN);
   if (!ptype) {
      fprintf(stderr,"losig_delay: no lofigchain on signal '%s'\n",
      signame);
      autexit(1);
   }
   
   /*search drivers*/
   for (lofigchain=ptype->DATA; lofigchain; lofigchain=lofigchain->NEXT) {
      locon= (locon_list*) lofigchain->DATA;
      if (!locon) {
         fprintf(stderr,"losig_delay: NULL pointer in lofigchain\n");
      }
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' isn't accepted\n",
         locon->NAME);
         autexit(1);
      }
      /*real inputs*/
      if (isvdd(locon->NAME) || isvss(locon->NAME)) continue;
      if (locon->DIRECTION==IN) continue;
      if (locon->TYPE!=EXTERNAL) {
         loins=locon->ROOT;
         delay=loins_delay(loins,signame);
         if (delay>max_delay) max_delay=delay;
      }
   }   

   return max_delay;
}


/***************************************************************************/
/* propagate modification of a loins for delay                             */
/***************************************************************************/
extern void propagate_loins_delay(loins_list *loins)
{
   locon_list* locon;
   
   /*input first*/
   for (locon=loins->LOCON; locon; locon=locon->NEXT) {
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' in figure '%s' isn't accepted\n",
         locon->NAME,loins->INSNAME);
         autexit(1);
      }
      if (locon->DIRECTION==IN || locon->DIRECTION==INOUT || locon->DIRECTION==TRANSCV)
         propagate_losig_delay(locon->SIG);
   }

   /*then output*/
   for (locon=loins->LOCON; locon; locon=locon->NEXT) {
      if (locon->DIRECTION==IN) continue;
      propagate_losig_delay(locon->SIG);
   }
}


/***************************************************************************/
/* dispatch delay on losig modification                                   */
/***************************************************************************/
static void loc_propagate_losig_delay(loins_list *loins)
{
   locon_list* locon;

   if (!loins) {
      fprintf(stderr,"propagate_loins_delay: NULL pointer\n");
      autexit(1);
   }
   
   for (locon=loins->LOCON; locon; locon=locon->NEXT) {
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' in figure '%s' isn't accepted\n",
         locon->NAME,loins->INSNAME);
         autexit(1);
      }
      if (isvdd(locon->NAME) || isvss(locon->NAME)) continue;
      if (locon->DIRECTION==IN) continue;
      if (!locon->SIG) {fprintf(stdout,"\n+++++%s %s\n",loins->INSNAME,loins->FIGNAME);locon->SIG->NAMECHAIN=NULL;}
      propagate_losig_delay(locon->SIG); 
   }

}


/***************************************************************************/
/* propagate modification of a losig for delay                             */
/***************************************************************************/
extern void propagate_losig_delay(losig_list *losig)
{
   ptype_list* ptype;
   loins_list* loins;
   locon_list* locon;
   double delay, old_delay;
   char* signame;
   chain_list* lofigchain;

   if (!losig) {
      fprintf(stderr,"propagate_losig_delay: NULL pointer\n");
      autexit(1);
   }
  
   
   if (!losig->NAMECHAIN) {
      fprintf(stderr,"propagate_losig_delay: no losig name\n");
      autexit(1);
   }
   signame=losig->NAMECHAIN->DATA;
   delay=losig_delay(losig);
   
   if (losig->TYPE==EXTERNAL) {
      /*delay for output (memorize in special ident)*/
      putdelay(output_name(signame),delay);
      /*delay for input - T and R user  (return 0 for signame)*/
      delay=getdelaylax(signame)
           +getimpedancelax(signame)*getcapacitance(signame);
   }
   
   /*no use to propagate if unchanged*/
   old_delay=getdelay(signame);
   if (old_delay==delay) return;
   putdelay(signame,delay);

   /*propagate new delay on loins connected*/
   ptype=getptype(losig->USER,LOFIGCHAIN);
   if (!ptype) {
      fprintf(stderr,"propagate_losig_delay: no lofigchain on signal '%s'\n",
      signame);
      autexit(1);
   }
   for (lofigchain=ptype->DATA; lofigchain; lofigchain=lofigchain->NEXT) {
      locon= (locon_list*) lofigchain->DATA;
      if (locon->DIRECTION==UNKNOWN) {
         fprintf(stderr,"BEH: 'linkage %s' isn't accepted\n",
         locon->NAME);
         autexit(1);
      }
      if (isvdd(locon->NAME) || isvss(locon->NAME)) continue; 
      if (locon->DIRECTION==OUT || locon->DIRECTION==TRISTATE) continue;
      if (locon->TYPE==EXTERNAL) continue;
      loins=locon->ROOT;
      loc_propagate_losig_delay(loins);
   }   
   
}


/***************************************************************************/
/* set all capacitances of signals                                         */
/***************************************************************************/
extern void set_circuit_capacitance(lofig_list *lofig)
{
   loins_list* loins;
   losig_list* losig;
   char* signame;

   for (losig=lofig->LOSIG; losig; losig=losig->NEXT) {
      if (!losig->NAMECHAIN) {
         fprintf(stderr,"set_circuit_capacitance: no losig name\n");
         autexit(1);
      }
      /*
      ** LUDO: to ensure name unicity !
      */
      signame=getnameindex(losig->NAMECHAIN->DATA);
      losig->NAMECHAIN->DATA = signame;
      /*for circuit output take user propertie if defined else return 0*/
      putcapacitance(signame,getcapacitancelax(signame));
      putdelay(signame,0);
   }
   
   for (loins=lofig->LOINS; loins; loins=loins->NEXT) {
      loins_capacitance(loins,1);
   }
}   
   
   
/***************************************************************************/
/*set all circuit delays with recursiv mode                                */
/***************************************************************************/
extern void set_circuit_delay(lofig_list* lofig)
{
   losig_list* losig;

   for (losig=lofig->LOSIG; losig; losig=losig->NEXT) {
      propagate_losig_delay(losig);
   }
}


