#ifndef lint
static char *SccsId = "@(#)hierarchy.c 4.7 (TU-Delft) 04/20/93";
#endif /* lint */
/**********************************************************

Name/Version      : space/4.7

Language          : C
Operating system  : UNIX SYSTEM V
Host machine      : HP9000

Author(s)         : A.J. van Genderen
		  : N.P. van der Meijs
Creation date     : 15-Mar-1988
Modified by       :
Modification date :


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 786234

        COPYRIGHT (C) 1988. All rights reserved.
**********************************************************/
#include <stdio.h>
#include "dmincl.h"
#include "include/config.h"
#include "include/type.h"
#include "aux/aux.h"

extern bool_t prePass1;
extern bool_t prePass2;
extern bool_t optOnlyPrePass;
extern bool_t optFlat;
extern bool_t optReadNetTerm;
extern bool_t optGenNetTerm;
extern bool_t optTorPos;

extern bool_t existDmStream ();

#define INIT_TERM_SIZE   1000
terminal_t **TERM  = NULL;
static int TERMSize = 0;

int nrOfTerminals = 0;
int maxTermNumber = 0;

#define INIT_INAME_SIZE  100
char ** instNames = NULL;
static int instNameSize = 0;

static int i_instNames = 0;

/*
 * private functions
 */
private void addInstance ();
private void addTerminal ();

/*  Read the 'tid' file.

    The 'tid' files assigns a number to each terminal
    of the current cell and its first-level subcells.
    The t_mask_bxx files then contain the terminal coordinates.

    This routine handles the hierarchy,
    i.e. it writes the 'term' and 'mc' streams in the
    circuit view.
*/

void readTid (layoutKey, circuitKey)
DM_CELL * layoutKey, * circuitKey;
{
    int i_nx, i_ny, t_nx, t_ny;
    int instX, instY, termX, termY;
    char *termName, *cellName, *instName;
    int instNumber = 0;
    terminal_t * t;
    long xl, yb;
    char *attr;
    char attr_buf[32];

    DM_STREAM * tidStream,
	      * mcStream,
	      * tidposStream,
	      * netmcStream,
	      * termStream,
	      * flagStream;

    i_instNames = 0;
    nrOfTerminals = 0;
    maxTermNumber = 0;

    if (optReadNetTerm) {
	tidStream   = dmOpenStream (layoutKey, "nettid",   "r");
    }
    else {
	tidStream   = dmOpenStream (layoutKey, "tid",   "r");
    }
    tidposStream = NULL;
    if (optTorPos && existDmStream (layoutKey, "tidpos")) 
	tidposStream   = dmOpenStream (layoutKey, "tidpos",   "r");
    if (optReadNetTerm)
	netmcStream = dmOpenStream (circuitKey, "netmc",   "w");
    mcStream    = dmOpenStream (circuitKey, "mc",   "w");
    termStream  = dmOpenStream (circuitKey, "term", "w");

    if (optFlat) {
	flagStream = dmOpenStream (circuitKey, "flat", "w");
	dmCloseStream (flagStream, COMPLETE);
    }
#   if NCF_RELEASE < 400
    /*
     * In Rel 3 flat-stream has to be removed explicitly,
     * since it survives update transactions (mode for
     * circuitKey == UPDATE).
     */
    else {
	if (existDmStream (circuitKey, "flat")) {
	    dmUnlink (circuitKey, "flat");
	}
    }
#   endif /* NCF_RELEASE */

    if (tidStream == NULL) {
	say ("cell '%s' not expandend, run exp -g first", layoutKey -> cell);
	die ();
    }

#   if NCF_RELEASE >= 400
    /* Have they gone crazy: not to raise an error when a file
     * that I want open for reading does not exist?
     */
    if (tidStream -> dmfp == NULL) {
	say ("cell '%s' not expandend, run exp -g first", layoutKey -> cell);
	die ();
    }
#   endif /* NCF_RELEASE */

    while (dmGetDesignData (tidStream, GEO_TID) > 0) {
	if (gtid.term_offset == -1) {
	    cellName = gtid.cell_name;
	    if (strsame (gtid.inst_name, "$"))
		instName = NULL;
	    else if (strsame (gtid.inst_name, "."))
		instName = strsave (mprintf ("_I%d", instNumber++));
	    else
		instName = strsave (gtid.inst_name);
	    i_nx = gtid.m_nx, i_ny = gtid.m_ny;

	    if (instName) {
		if (optTorPos && tidposStream) {
		    fscanf (tidposStream -> dmfp, "%ld %ld", &xl, &yb);
		    sprintf (attr_buf, "x=%ld;y=%ld", xl, yb);
		    attr = attr_buf;
		}
		else
		    attr = NULL;
		if (optReadNetTerm)
		    addInstance (netmcStream, cellName, instName, 
							  i_nx, i_ny, attr);
		else
		    addInstance (mcStream, cellName, instName, 
							  i_nx, i_ny, attr);

		/* To be able to clean up,
		 * remember the names of the instances
		 */
		if (i_instNames >= instNameSize) {
		   unsigned to = instNameSize == 0 ? INIT_INAME_SIZE 
					           : 2 * instNameSize;
		   instNames = GROW (char *, instNames, instNameSize, to);
		   instNameSize = to;

		}
		instNames[i_instNames++] = instName;
	    }
	}
	else {
	    ASSERT (nrOfTerminals == gtid.term_offset);
	    t_nx = gtid.t_nx, t_ny = gtid.t_ny;
	    termName = strsave (gtid.term_name);

	    for (instX = 0; instX <= i_nx; instX++) {
	     for (instY = 0; instY <= i_ny; instY++) {
	      for (termX = 0; termX <= t_nx; termX++) {
	       for (termY = 0; termY <= t_ny; termY++) {

		   if (nrOfTerminals >= TERMSize) {
		       unsigned to = TERMSize == 0 ? INIT_TERM_SIZE 
						   : 2 * TERMSize;
		       TERM = GROW (terminal_t *, TERM, TERMSize, to);
		       TERMSize = to;
		   }

		   t = TERM[nrOfTerminals++] = NEW (terminal_t, 1);
		   t -> conductor = 0, t -> next = NULL;
		   t -> x = t -> y = MAXINT;		/* to cover
							erroneous tech file */
		   t -> termName = termName;
		   t -> instName = instName;
		   t -> instX = i_nx > 0 ? instX : -1;
		   t -> instY = i_ny > 0 ? instY : -1;
		   t -> termX = t_nx > 0 ? termX : -1;
		   t -> termY = t_ny > 0 ? termY : -1;
	       }
	      }
	     }
	    }

	    if (! instName)
	       addTerminal (termStream, termName, t_nx, t_ny);
	}
    }

    dmCloseStream (tidStream, COMPLETE);
    if (optTorPos && tidposStream)
	dmCloseStream (tidposStream, COMPLETE);
    dmCloseStream (mcStream, COMPLETE);
    if (optReadNetTerm)
	dmCloseStream (netmcStream, COMPLETE);
    dmCloseStream (termStream, COMPLETE);
}


/* Add a new instance to the circuit
*/
private void addInstance (stream, cellName, instName, nx, ny, attr)
DM_STREAM * stream;
char * cellName, * instName;
int nx, ny;
char *attr;
{
    DM_PROJECT * dmProject = stream -> dmkey -> dmproject;

    static long inst_lower[2], inst_upper[2]; /* initial zero */

    strcpy (cmc.cell_name, cellName);
    strcpy (cmc.inst_name, instName);

    /* Check if the cell is local or imported and set the
     * 'imported' field in the mc record.
     * If the cell is imported, we don't know wether the cell
     * is also imported in the circuit view.
     * The designer should eventually import the cell himself
     */
    if (dmGetMetaDesignData (EXISTCELL, dmProject, cellName, LAYOUT) == 0)
	cmc.imported = IMPORTED;
    else
	cmc.imported = LOCAL;

    cmc.inst_attribute = attr;
    inst_lower[0] = 0, inst_lower[1] = 0;
    cmc.inst_dim = 0;
    if (nx > 0)
       inst_upper[cmc.inst_dim++] = nx;
    if (ny > 0)
       inst_upper[cmc.inst_dim++] = ny;
    
    cmc.inst_lower = inst_lower, cmc.inst_upper = inst_upper;

    if ((!prePass1 && !prePass2) || optOnlyPrePass)
	dmPutDesignData (stream, CIR_MC);
}


/* Add a new terminal to the circuit
*/
private void addTerminal (stream, termName, nx, ny)
DM_STREAM * stream;
char * termName;
int nx, ny;
{
    static long term_lower[2], term_upper[2]; /* initial zero */

    strcpy (cterm.term_name, termName);
    cterm.term_attribute = NULL;
    cterm.term_dim = 0;
    if (nx > 0)
       term_upper[cterm.term_dim++] = nx;
    if (ny > 0)
       term_upper[cterm.term_dim++] = ny;
    
    cterm.term_lower = term_lower, cterm.term_upper = term_upper;

    if (nx <= 0 && ny <= 0) {
	int i;

	for (i = strlen (termName) - 1; i >= 0; i--) {
	    if (termName[i] < '0' || termName[i] > '9')
		break;
	}
	if (i < 0)
	    if (atoi (termName) > maxTermNumber)
		maxTermNumber = atoi (termName);
    }

    if ((!prePass1 && !prePass2) || optOnlyPrePass)
	dmPutDesignData (stream, CIR_TERM);
}

void disposeTid ()
{
    int i;
    terminal_t * t;

    for (i = 0; i < nrOfTerminals; i++) {
	t = TERM [i];
	if (t -> instX <= 0 && t -> instY <= 0
	 && t -> termX <= 0 && t -> termY <= 0) {
	    DISPOSE (t -> termName);
	}
	DISPOSE (t);
    }

    while (--i_instNames >= 0) DISPOSE (instNames[i_instNames]);
}
