
/*
 * $Id: partition.c,v 1.1.1.1 2003/11/04 23:34:57 mjames Exp $   
 *
 * $Log: partition.c,v $
 * Revision 1.1.1.1  2003/11/04 23:34:57  mjames
 * Imported into local repositrory
 *
 * Revision 1.15  2003/01/02 21:37:16  mjames
 * Experiment on creating NOT_ROUTABLE_H and NOT_ROUTABLE_L
 * properties on the nets so that pin jumpers can be made without a problem.
 *
 * Still need to sort out pin assignments made to these not_routable nets
 * which will become legal in some cases so that pullups and pulldown
 * pins can be used on the FPGA.
 *
 * Revision 1.14  2002/09/30 13:20:29  MJAMES
 * Modified partition rules to include 'default assignment on declaration'
 * which maps to inputs being driven with default values on
 * productuion of a partition.
 *
 * signal c : std_logic := '0';
 *
 * becomes
 *
 * signal c: std_logic;
 *
 * begin
 *   c<= '0';
 *
 * Revision 1.13  2002/09/18 08:51:24  mjames
 * Removed unused variables
 *
 * Revision 1.12  2002/09/09 10:13:09  mjames
 * Moved pin remapping function to pin ident editing function from
 * sorting pin name routine.
 *
 * Revision 1.11  2002/01/16 11:22:47  mjames
 * database.h header file is read in first as it undefined DLL stuff irrelevant
 * to HPUX
 *
 * Revision 1.10  2001/12/13 22:17:28  mjames
 * Using #ident with header to identify file
 *
 * removed debug messages
 *
 * Revision 1.9  2001/10/31 22:20:11  mjames
 * Tidying up problematical comments caused by CVS
 * 'intelligent' comment guessing
 *
 * Revision 1.8  2001/10/31 16:21:46  mjames
 * Added a datastructure to hide regular expression information from programs.
 * Changed call to regexec to indicate 0 subexpressions to be matched
 * rather than a number dependent on strlen(string) which was wrong.
 * Altered diagnostics while debugging checked in because useful at other
 * times
 *
 * Revision 1.7  2001/10/10 20:18:23  mjames
 * Added a vert_regcomp function to compile regular expressions
 * with '^' (match start string) and  '$' (match end string) bracketing
 * this => wildcard must match entire string not just a part of it.
 *
 * Revision 1.6  2001/10/07 20:50:53  mjames
 * Added wildcard checking (warn user about
 * using wildcard '*' on the end of a string in stead of wildcard '.*')
 *
 * Revision 1.5  2001/09/25 23:24:18  mjames
 * Update because  wildcard behaviour amended
 *
 * Revision 1.4  2001/08/01 07:38:03  mjames
 * Removed multiple informational messages
 * replaced with summary unless debug level is higher
 *
 * Revision 1.3  2001/06/06 12:10:19  mjames
 * Move from HPUX
 *
 * Revision 1.2  2000/11/29 21:51:18  mjames
 * Fine tuning of software
 *
 * Revision 1.1.1.1  2000/10/19 21:58:38  mjames
 * Mike put it here
 *
 *
 * Revision 1.36  2000/10/12  15:32:31  15:32:31  mjames (Mike James)
 * Removed <cr>
 * 
 * Revision 1.35  2000/10/12  14:25:49  14:25:49  mjames (Mike James)
 * removed <cr>
 * 
 * Revision 1.34  2000/10/04  10:37:07  10:37:07  mjames (Mike James)
 * Modified for Vertical2 : support COMPONENTS and SIGNALS
 * 
 * Revision 1.34  2000/10/04  10:37:07  10:37:07  mjames (Mike James)
 * Part of Release PSAVAT01
 * 
 * Revision 1.33  2000/10/03  10:05:31  10:05:31  mjames (Mike James)
 * Added CONSTANTS and SIGNALS to architecture
 * 
 * Revision 1.31  2000/09/27  14:42:17  14:42:17  mjames (Mike James)
 * Part of Release Sep_27_ST_2000
 * 
 * Revision 1.30  2000/09/21  10:15:48  10:15:48  mjames (Mike James)
 * Part of Release Sep21Alpha
 * 
 * Revision 1.29  2000/08/25  09:57:13  09:57:13  mjames (Mike James)
 * Part of Release Aug25_alpha
 * 
 * Revision 1.28  2000/08/16  08:57:30  08:57:30  mjames (Mike James)
 * Part of Release CD01_Aug2000
 * 
 * Revision 1.27  2000/08/14  14:45:11  14:45:11  mjames (Mike James)
 * Part of Release Aug_14_2000
 * 
 * Revision 1.26  2000/08/11  08:30:32  08:30:32  mjames (Mike James)
 * Part of Release Aug_11_2000
 * 
 * Revision 1.25  2000/08/09  10:31:46  10:31:46  mjames (Mike James)
 * Part of Release Aug__9_2000
 * 
 * Revision 1.24  2000/05/31  11:42:56  11:42:56  mjames (Mike James)
 * Part of Release May_31_2000
 * 
 * Revision 1.23  2000/05/31  11:30:18  11:30:18  mjames (Mike James)
 * Added acfread.ini file reading
 * 
 * Revision 1.22  2000/05/08  17:01:37  17:01:37  mjames (Mike James)
 * Part of Release May__8_2000
 * 
 * Revision 1.21  2000/05/08  16:59:30  16:59:30  mjames (Mike James)
 * Part of Release May__8_2000
 * 
 * Revision 1.20  2000/05/08  16:57:07  16:57:07  mjames (Mike James)
 * Part of Release May__8_2000
 * 
 * Revision 1.19  2000/03/08  16:19:20  16:19:20  mjames (Mike James)
 * New version including PC
 * 
 * Revision 1.16  2000/01/20  15:58:47  15:58:47  mjames (Mike James)
 * Part of Release R22
 * 
 * Revision 1.15  99/12/22  11:15:27  11:15:27  mjames (Mike James)
 * Part of Release Dec_22_1999
 * 
 * Revision 1.14  99/11/23  13:52:12  13:52:12  mjames (Mike James)
 * Addded syntax to support special generics for Certify support
 * 
 * Revision 1.13  99/06/25  14:35:46  14:35:46  mjames (Mike James)
 * Added in reference to expression.h, but no changes made 
 * to the function of acfread yet.
 * 
 * Revision 1.12  99/06/18  09:25:37  09:25:37  mjames (Mike James)
 * Added generics exported upwards 
 * 
 * Revision 1.11  98/10/01  15:25:30  15:25:30  mjames (Mike James)
 * Added templates (name*) or * to set/del port
 * 
 * Revision 1.10  98/04/24  13:49:07  13:49:07  mjames (Mike James)
 * Added extra force_port flag to net for 'set port' command
 * 
 * Revision 1.9  98/02/11  11:26:53  11:26:53  mjames (Mike James)
 * Checked in for version 6.2a
 * 
 * Revision 1.8  97/04/23  08:43:20  08:43:20  mjames (Mike James)
 * CHecked in for release rel23041997
 * 
 * Revision 1.7  96/12/23  15:15:51  15:15:51  mjames (Mike James)
 * Altered because find_socket takes a reference
 * to the head-of-list-pointer noth the pointer itself.
 * 
 * Revision 1.6  96/07/19  14:38:47  14:38:47  mjames (Mike James)
 * Update to give to PRL
 * 
 * Revision 1.5  1996/05/23  10:32:26  mjames
 * Altered BUFFER and OUT pin rules to decide whether a
 * signal crosses a partition boundary.
 *
 * Revision 1.4  96/05/21  14:16:07  14:16:07  mjames (Mike James)
 * Added the concept ogf buffered pins to get some Shoamra2
 * partitions to work properly.
 * 
 * Revision 1.3  96/04/26  16:01:55  16:01:55  mjames (Mike James)
 * Altered inside/outside determination of signal directions
 * 
 * Revision 1.2  96/04/15  14:19:31  14:19:31  mjames (Mike James)
 * Checkin before datatype printing 
 * 
 * Revision 1.1  96/03/29  14:46:13  14:46:13  mjames (Mike James)
 * Initial revision
 * 
 *  */
/* The Partition module. This combines VHDL for the purposes of creating
   EPLDs */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>

#include "vertcl_main.h" 
#include "expression.h"
#include "generic.h"
#include "database.h"


#include "partition.h"
#include "cmdparse.h"
#include "cmdlog.h"
#include "acf_yacc.h"
#include "vertcl_main.h"
#include "chck_names.h"
#include "sorting.h"

#include "routing.h"

#ident "@(#)$Header: c:\\cygwin\\cvsroot/Vert03/vertlib/partition.c,v 1.1.1.1 2003/11/04 23:34:57 mjames Exp $"



/* nets created as a result of partition are ripped up when a new partition
   is created */
   
void clear_partition_nets(void)
  {
  net_t * net = named_list; /* only interested in nets carrying a signal */
  while(net){
    net->leaves_partition = 0; /* not leaving partition */
    net->inside_partition = 0; /* not used inside partition */

#if defined BLOB
    if(!net->type_defined && net->vhdltype) {
      if(net->vhdltype->basetype) {
        /* ought to free it */  
        free(net->vhdltype->basetype);       
        net->vhdltype->basetype = NULL;
        }
      if(net->vhdltype->expr) {
        net->vhdltype = NULL;
        free_expression(net->vhdltype->expr);
        }

      }
#endif
    net = net->next;
  	}
  }

/* all nets that are in the routed list are named and placed in the named list */

void name_routed_nets(void)
  {
  net_t * link;
  net_t * net = routed_list; /* check each net in turn */
/* this code is here : shoould it be ? */
  while(net) {
    noderef_t *  noderefs = net->nodes;
    link = net->next;
    if(IS_ROUTABLE(noderefs->net->how_routed)) {
      if (ISNULLSTR(net->name))
        net->name = net->identifier;
      while(noderefs){

/*      noderefs->node->in_use = 1; */
        noderefs->node->name = net->name;
        noderefs = noderefs->next;
        }
	  combine_routed_and_unrouted (net, net);
	  transfer_net_to_named (&routed_list, net);
      }
    net = link;
    }      
}



int set_clear_partition(char * expr,int set_clear)
{
  int rc;
  socket_t * skt = socket_head;
  int found = 0;
  int process_count = 0;

  /* compile regular expression */
  vert_regex_t * preg;

  

  rc = vert_regcomp(&preg,expr);

  if (rc != 0 ) 
    {
    char errbuff[100];
    regerror(rc,preg->preg,errbuff,100);
    Log(LOG_ERROR,"-- Problem (rc=%d) %s with '%s' as regular expression\n",rc,errbuff,expr);
    
    return /*TCL_ERROR*/;
    }
  else 
    {
    Log(LOG_GENERAL,"-- Using '%s' as match pattern\n",expr);
    }

  while(skt) {
    found  = regexec(preg->preg,skt->identifier,0,preg->regpatt,0);
    if(!found) 
      {
      if (skt && ISNULLSTR(skt->name)) 
        {
        skt->name = skt->identifier;
        }
      skt->selected = set_clear;  
      process_count++;
      }
    skt = skt->next;
    }

  vert_regfree(&preg);
  Log(LOG_GENERAL,"-- Processed %d sockets\n",process_count++);  
  return OKCMD;
  }



int add_all_to_partition(void)
{
  
  return set_clear_partition(".*",1);
}


/* printout the sockets that are selected inside the partition */
void list_partition(FILE * f)
{
  socket_t * skt = socket_head;
  fprintf(f,"Partition includes the following blocks:\n");
  while(skt){
    if(skt->selected) /* selected */
      fprintf(f,"  '%s'\n",skt->name);
    skt = skt->next;
  }
  fprintf(f,"-- End list -- \n");
}

/* the partitioning algorithm is implemented here */
/*
     for each net on the named list -- Made it to routing
     if the only nodes on the list that are 'in use' only connect
       to 'selected' sockets and there is an output or buffer pin inside the          then the net is inside the partition.
           if the net is all inputs the the net has to leave the
           partition as an input.
*/
void perform_partition(void)
{
  net_t * net = named_list; /* check each net in turn */
  int ports = 0;
  int port_wires = 0;
  int signals = 0;
  int signal_wires = 0;

  while(net) {
    noderef_t *  noderefs = net->nodes;
    char      used_outside = 0; /* set if the net goes outside */
    char      used_inside = 0;  /* set if the net used inside */
    char      inputs_on_net = 0;
    char      outputs_on_net = 0;
    char      buffers_on_net = 0; /* another pin type that we must handle */
    char      bidirs_on_net = 0;
    char      default_drive = 0; /* Was this net defined with a default value ? */
    
    int width = 0;              /* how many wires in the bundle */
    int high  = 0;
    int low   = 1000;

    expression_t * exp,* variable_seen,
            *high_ref, * low_ref,  * high_exp, *low_exp, * highest_exp, * lowest_exp ;

    highest_exp = NULL;
    lowest_exp  = NULL;
    high_ref = NULL; /* reference to expression containing highest expr */
    low_ref  = NULL; 
    variable_seen = NULL;
    net->ext_dir = NONE; /* not yet known */
    
    /* set flag if the net was defined as "signal x : type := value;" */
    default_drive = net->type_defined && net->vhdltype && net->vhdltype->default_expr;
    
    
    /* predefined net type definition, can already know upper ansd lower bounds */
#if defined DEBUG_EXPRESSION
    printf("net %s \n",net->identifier);
#endif
    while(noderefs){

      node_t * n = noderefs->node;
      
      if(n->in_use) {
        int h,l;
        vhdl_t * vhdl;
        vhdl = noderefs->vhdltype;
        if(vhdl && vhdl->expr) 
          exp = vhdl->expr;
        else
          exp = NULL;

#if defined DEBUG_EXPRESSION
        if(exp) {
          printf("expression vhdl%p expr%p",vhdl,exp);
          print_range_expression(stdout,exp,0);
          printf("\n");
          }
#endif   
        /* if we have a variable defining width,
           remember what the top level variable is called,
           then keep expanding it until we get an expression
           rather than a variable */
        if(exp && exp->opcode == EXP_VARIABLE) {
           variable_seen = exp;
           while(exp->opcode == EXP_VARIABLE)
             exp = exp->left.g->expr;
           }
        if(exp) {
          if (exp->opcode == TO) {
            high_exp = exp->right.e;
            low_exp  = exp->left.e;
            }
          else if (exp->opcode == DOWNTO) {
            high_exp = exp->left.e;
            low_exp  = exp->right.e;
            }
          else 
            high_exp = low_exp = exp; /* not a range, just a single value */
          
          l = eval_expression(low_exp, NULL);   
          h = eval_expression(high_exp, NULL);
        /* sort out the highest and lowest bound of any logic vector pins
             on the net (high and low are zero for single bits so calculation
             applies here) */
        if (h > high) {
          highest_exp = high_exp;
          high_ref    = exp;
          high = h;
          }
        if (l  < low){
          lowest_exp = low_exp;
          low_ref    = exp;
          low  = l;
           }
         }   
        else {
          high=0;
          low=0;
          };
        




        /* discover it is routed outside - it links to an unselected socket */
        /* but if it only links to unselected sockets,
           used_inside wont be set as well */
        if(!n->socket->selected)
          used_outside = 1;
        else{
          used_inside = 1;
          if (noderefs->pindir == INPUT)
            inputs_on_net = 1;
          if (noderefs->pindir == OUTPUT)
            outputs_on_net = 1;
          if (noderefs->pindir == BIDIR)
            bidirs_on_net = 1;
          if (noderefs->pindir == BUFFER)
            buffers_on_net = 1;

          /* I was  using the ordinal value of the pindir_t type
        if  (noderefs->pindir >= net->ext_dir ) ||  */
        
           /* now June 1999 takes on the type of the first node ,
             or if one of the nodes has a bus connection use that : still a 
             guess. Alternatively if the net was declared so it has a
             type defined then keep that as it is more correct...  */


        if(!net->type_defined && ( !net->vhdltype || !net->vhdltype->basetype ||
              (!net->vhdltype->is_vector && vhdl->is_vector))) {
            /* copy over base VHDL type and vector/single signal attribute*/
           
            net->vhdltype = copy_vhdl(vhdl,NULL);           
            
            }
        }      
      }
    noderefs = noderefs->next;
    }    




/* nets with a VHDL type will simply use this type as the definition of the expression */
#if defined IGNORE_DEFINED
    if(net->type_defined && net->vhdltype ) {
      exp = net->vhdltype->expr;
      if(exp && exp->opcode == EXP_VARIABLE)
         exp = exp->left.g->expr;
      if(exp) {
        if (exp->opcode == TO) {
          highest_exp = exp->right.e;
          lowest_exp  = exp->left.e;
          }
        else if (exp->opcode == DOWNTO) {
          highest_exp = exp->left.e;
          lowest_exp  = exp->right.e;
          }
        else 
          highest_exp = lowest_exp = exp->left.e;
        low = eval_expression(lowest_exp,NULL);   
        high = eval_expression(highest_exp,NULL);
        }
      }
#endif

    if ( !net->type_defined && net->vhdltype && net->vhdltype->is_vector) {
      if(variable_seen) 
        net->vhdltype->expr  = variable_seen; 

      else if(highest_exp && lowest_exp) {
         if (high_ref && (high_ref == low_ref))
           net->vhdltype->expr = high_ref;
         else
           net->vhdltype->expr = compile_expression(DOWNTO,highest_exp,lowest_exp);
         }
      else
         net->vhdltype->expr = compile_constant(0);
      }


   




    /* pin will appear on port list if : */
                            /* routed inside & outside */
    net->leaves_partition = (used_inside && used_outside) ||
                            /* inputs but not any sort of outputs, or default drive (a kind of output) */ 
                            (inputs_on_net && !( default_drive || outputs_on_net || buffers_on_net)) ||
                            /* any sort of outputs but not inputs */ 
                            ((outputs_on_net || buffers_on_net) && !inputs_on_net) ||
                            /* any bidirectional pins */
                            bidirs_on_net ||
                            /* net is forced as a port */
                            net->force_port;


  

    net->needs_buff_sig   = inputs_on_net &&
                            (outputs_on_net || buffers_on_net) &&
                            net->leaves_partition;                          

    /* sort out the pin directions */
    /* any bidirectional pins */
    if(bidirs_on_net)
      net->ext_dir  = BIDIR;
    /* inputs but not any sort of outputs */  
    else if(inputs_on_net && !(outputs_on_net || buffers_on_net)) 
      net->ext_dir  = INPUT; 
    /* any sort of outputs but not inputs */ 
    else if (outputs_on_net || buffers_on_net)
      net->ext_dir  = OUTPUT;
    else 
      net->ext_dir  = NONE;
    /* net will appear in local signal definition if : */
                            /* it is used inside  */
                            /* and either only inside or it is a BUFFER 
                               port in which
                               case a temporary signal is provided by
                               printout.c */
    net->inside_partition =  used_inside && (
                               (!net->leaves_partition) ||
                               (net->needs_buff_sig));

        
    width = high-low+1;
    if(level & 1) 
      {                               
      printf("Partition : Net %-20s Inside %d Outside %d Crossing Dir %d width %d %c%c%c%c%c\n",
            net->name,net->inside_partition,net->leaves_partition,
            net->ext_dir,width,
            outputs_on_net?'O':'o',
            inputs_on_net ?'I':'i',
            bidirs_on_net ?'B':'b',
            buffers_on_net?'U':'u',
            default_drive ?'D':'d');
      }


    /* compute the bus width unless it is already defined  */
 
    if(net->leaves_partition) {
      port_wires+= width;     
      ports++;
      };
    if(net->inside_partition) {
      signal_wires+= width;
      signals++;
      };
    net = net->next;
  }
  Log(LOG_GENERAL,"-- %5d Nets    (%5d wires) leaving partition\n"
                  "-- %5d Signals (%5d wires) inside partition\n",
                  ports,port_wires,signals,signal_wires);
}

/* remove a port from the partition as I dont want it outside */
void delete_port(char * template)
{
  int rc;
  net_t * net = named_list;
  int found = 0;
  int net_count = 0;
  /* compile regular expression */
  vert_regex_t  * preg;


  rc = vert_regcomp(&preg,template);

  if (rc != 0 ) 
    {
    char errbuff[100];
    regerror(rc,preg->preg,errbuff,100);
    Log(LOG_ERROR,"-- Problem (rc=%d) %s with '%s' as regular expression\n",rc,errbuff,template);
    
    return /*TCL_ERROR */;
    }



  Log(LOG_GENERAL,"-- Beginning removing ports : prefix '%s' --\n",template );

  while(net) 
    {
    found  = regexec(preg->preg,net->name,0,preg->regpatt,0);
    if(found && found!=REG_NOMATCH)
      {
      char errbuff[100];
      regerror(found,preg->preg,errbuff,100);
      Log(LOG_ERROR,"-- Problem (rc=%d) %s with '%s' as regular expression\n",found,errbuff,template);
    
      }

      if (level & 1)
        {
        Log(LOG_GENERAL,"-- Net '%s'%s",net->name,found?"\n":""); 
        }

    if(!found)
      {
      if (level & 1)
        {
        Log(LOG_GENERAL,"inside=%d leaves=%d buffer=%d force_port=%d\n",
            net->inside_partition, 
            net->leaves_partition,
            net->needs_buff_sig,
            net->force_port);
        }
   
      net->force_port = 0;
   /* now to delete the port the following is needed to be done */
    /* Is it going outside ? If not , forget it */
    /* otherwise ensure its used inside, not a port and its not needing a buffer */
      
       if( net->leaves_partition  || net->needs_buff_sig ) 
         {
         net->leaves_partition = 0;
         net->inside_partition = 1;
         net->needs_buff_sig = 0;
         if (level & 1)
           Log(LOG_GENERAL,"-- Removed port '%s' from partion\n",net->name);
         net_count++;
         }
      }
  net = net->next;
  }





   Log(LOG_GENERAL,"-- Removed %d ports from partiton\n",net_count);
   Log(LOG_GENERAL,"-- Done removing ports \n");
   vert_regfree(&preg);
}
   
/* force a net to be a port on the partition as I want it outside */
void force_port(char * template)
{
  int rc;
  net_t * net = named_list;
  int found ;
  int net_count = 0;
  /* compile regular expression */
  vert_regex_t * preg;

  rc = vert_regcomp(&preg,template);

  if (rc != 0 ) 
    {
    char errbuff[100];
    regerror(rc,preg->preg,errbuff,100);
    Log(LOG_ERROR,"-- Problem (rc=%d) %s with '%s' as regular expression\n",rc,errbuff,template);
    
    return /*TCL_ERROR */;
    }

  Log(LOG_GENERAL,"-- Beginning setting ports : prefix '%s' --\n",template );

  while(net) {
    found  = regexec(preg->preg,net->name,0,preg->regpatt,0);

    if(!found) {
      if(level &1)
        Log(LOG_GENERAL,"-- Set '%s' as a port if it is inside partition\n",net->name);
      net->force_port = 1;
      net_count++;
      }
    net = net->next;
    }
   Log(LOG_GENERAL,"-- Forced %d ports on partion\n",net_count);
   Log(LOG_GENERAL,"-- Port forcing done\n");
  vert_regfree(&preg);
}
   
  
