#ifndef lint
static char *SccsId = "@(#)main.c 4.54 (TU-Delft) 05/14/93";
#endif /* lint */
/**********************************************************

Name/Version      : space/4.54

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"
#include "aux/monitor.h"
#include "scan.h"
#include "determine.h"
#include <sys/time.h>





/* #include <malloc.h> /* only on hpux */

       char        * argv0;
static DM_CELL     * circuitKey;
static DM_PROJECT  * dmproject;
       double        meters;	/* this many meters per internal layout unit */

bool_t optFineNtw = FALSE,
       optTime    = FALSE,
       optBeeb    = FALSE,
       optTorPos  = FALSE,
       optNoDim   = FALSE,
       optDsConJoin = TRUE,
       optPlotCir = FALSE,
       optCap     = FALSE,
       optCoupCap = FALSE,
       optLatCap  = FALSE,
       optRes     = FALSE,
       optAllRes  = FALSE,
       optNoReduc = FALSE,
       optVerbose = FALSE,
       optInfo    = FALSE,
       optHisto   = FALSE,
       optMonitor = FALSE,
       optSpecial = FALSE,
       optResMesh = FALSE,
       optPrick   = FALSE,
       optInvertPrick   = FALSE,
       optSelectiveRes = FALSE,
       optAccWL   = FALSE,
       optFlat    = FALSE,
       optNoIncremental = FALSE,
       optCheckInQuit = FALSE,
       optNoPrepro = FALSE,
       optCap3D   = FALSE,
       optDisplay = FALSE,
       optPrintRecog = FALSE,
       optNoCore = FALSE,
       optBackInfo = FALSE,
       optGenNetTerm = FALSE,
       optReadNetTerm = FALSE,
       optOnlyPrePass = FALSE,
       optHier3D = FALSE,
       optPrintOnlyActions = FALSE;

int    optMaxDepth = MAXTYPE (int);
int    optMinDepth = ROOT_DEPTH; /* default: only cells named on command line */

bool_t    noSubnodeJoin = FALSE;
bool_t    alsoPrePass = FALSE;
bool_t    prePass1 = FALSE;
bool_t    prePass2 = FALSE;

static char * techFile = NULL;
static char * techDef = NULL;
static char * paramFile = NULL;
static char * paramDef = NULL;
static int    optAlarmInterval = 0;

coor_t bandWidth;

/* local operations */
private void prepass ();
private void extract ();
private void expand ();
private void notSupported ();
private void run ();


int main (argc, argv)
int     argc;
char   *argv[];
{
    int c;
    int i;
    int optErr = 0;
    char optstring[100];
    int argcSave;
    char **argvSave;
    void   catchAlarm ();



    char ** cellNames = NULL;

    extern int optind;
    extern char *optarg;

    /* mallopt (M_KEEP, 0); */
#   ifdef sun
    /* malloc_debug (2); /* see malloc (3) */
#   endif /* sun */

    argv0 = strrchr (argv[0], '/') ? strrchr (argv[0], '/') + 1 : argv[0];

    argv[0] = argv0;




    strcpy (optstring, "cClrzFID:untdvikja:E:e:P:p:S:");

    /* TRANSITION !!! */
    strcat (optstring, "R");


    /* Save program arguments (used for master3d) */
    argvSave = NEW (char *, argc + 2); 
    for (i = 0; i < argc; i++) {
	argvSave[i] = NEW (char, strlen (argv[i]) + 1);
	strcpy (argvSave[i], argv[i]);
    }
    argcSave = argc;
    argvSave[i] = NULL;

    while ((c = getopt (argc, argv, optstring)) != EOF) {
	switch (c) {
	    case 'l': notSupported ("l"); break;
	    case 'C': notSupported ("C"); break;
	    case 'r': notSupported ("r"); break;
	    case 'z': notSupported ("z"); break;
	    case 'k': notSupported ("k"); break;
	    case 'j': notSupported ("j"); break;

	    case 'c': optCap     = TRUE;    break;
	    case 'F': optFlat    = TRUE;    break;

	    case 'n': optNoReduc = TRUE;    break;
	    case 't': optTorPos  = TRUE;    break;
	    case 'd': optNoDim   = TRUE;    break;
	    case 'i': optInfo    = TRUE;    break;
	    case 'u': optNoPrepro = TRUE;    break;
	    case 'v': optVerbose = TRUE;    break;
	    case 'E': techFile   = strsave (optarg); break;
	    case 'e': techDef    = strsave (optarg); break;
	    case 'P': paramFile  = strsave (optarg); break;
	    case 'p': paramDef   = strsave (optarg); break;
	    case 'S': paramSetOption (optarg);       break;
	    case 'a': optAlarmInterval = atoi (optarg); break;

	    case 'D': optMinDepth = atoi (optarg); break;
	    case 'I': optMinDepth = INF; break;

	    case 'X': notSupported ("X"); break;

	    case '3': notSupported ("3"); break;


	    /* special options */
	    case '%': optSpecial = TRUE;    break;
	    case 'R': optAllRes  = TRUE;    break;
	    case 'L': optMaxDepth = atoi (optarg); break;
	    case 's': optSelectiveRes = TRUE; break;
	    case 'w': optAccWL   = TRUE;    break;
	    case 'b': optBeeb = TRUE;       break;
	    case 'f': optFineNtw = TRUE;    break;
	    case 'g': optHisto = TRUE;      break;
	    case 'J': optDsConJoin = FALSE; break;
	    case 'T': optTime = TRUE;       break;
	    case 'O': optPlotCir = TRUE;    break;
	    case 'H': optHier3D = TRUE; break;
	    case 'N': optGenNetTerm = TRUE; break;
	    case 'M': optReadNetTerm = TRUE; break;
	    case 'q': optPrintRecog = TRUE; break;
	    case 'm': optMonitor = TRUE;    break;
	    case 'K': optNoCore = TRUE;     break;
	    case 'x': optBackInfo = TRUE; break;
	    case 'Z': optOnlyPrePass = TRUE; break;
	    case 'V': optPrintOnlyActions = TRUE; break;
	    case '?': optErr++;             break;
	}
    }

    cellNames = argv + optind;

    if (techDef && techFile) {
	say ("Use only one of -e and -E");
	optErr++;
    }

    if (paramDef && paramFile) {
	say ("Use only one of -p and -P");
	optErr++;
    }

    if (optCap3D && ! (optCap || optCoupCap)) {
	say ("Use -c or -C with -3");
	optErr++;
    }

    /* TRANSITION !!! */
    if (optAllRes && !optSpecial) {
	say ("option -R ignored, it does not exist anymore. Assuming -r.");
	optAllRes = FALSE;
	optRes = TRUE;
    }

    if (optPrick && optInvertPrick) {
	say ("Use only one of -k and -j");
	optErr++;
    }

    if (*cellNames == NULL && optDisplay == FALSE) {
	say ("You must specify one or more cellnames");
	optErr++;
    }

    if (optErr != 0) {
	fprintf (stderr, "Usage: %s ", argv0);
	if (optSpecial) fprintf (stderr, "[-%%RykswbfgJTMqom] ");
	fprintf (stderr, "[-cClrzFIuntdvikja] [-a time]");
	if (optSpecial) fprintf (stderr, "\n       [-%%L max_depth] ");
	fprintf (stderr, "\n       ");
	fprintf (stderr, 
		 "[-D min_depth] [-e def | -E file] ");
	fprintf (stderr, "\n       ");
	fprintf (stderr, 
		 "[-p def | -P file] [-S param=value] ");
	fprintf (stderr, "cell ...");
	fprintf (stderr, "\n");
	compileDate (argv0);
	die ();
    }



    /* implications
     */
    if (optInfo)    optVerbose = TRUE;
    if (optLatCap)  optCoupCap = TRUE;
    if (optCoupCap) optCap     = TRUE;
    if (optAllRes)  optRes     = TRUE;

    if (optResMesh) optRes = TRUE;
    if (optResMesh) alsoPrePass = TRUE;

    if (optSelectiveRes) optRes = TRUE;
    if (optSelectiveRes) alsoPrePass = TRUE;

    if (optInvertPrick) optPrick = TRUE;

    if (optPrick) optRes = TRUE;
    if (optPrick) alsoPrePass = TRUE;

    if (optDisplay) optFlat    = TRUE;
    if (optCap3D && !optHier3D)   optFlat    = TRUE;

    /* Version 430 of libddm or newer is required in function 'expand' */
#   if NCF_RELEASE >= 400 && NCF_RELEASE < 430
    optNoPrepro = TRUE;
    say ("automatic preprocessing not supported in this version");
#   endif

    /************ Real Program Starts Here *******************/

    if (optMonitor) monitorStart ();
    setAlarmInterval (optAlarmInterval, catchAlarm);
    if (optTime || optInfo) clockInit ();
    if (optTime) tick ("startup");

    if (optInfo) {
	verboseSetLevel (2);
	message ("%s ", argv0);
	for (i = 1; i < argc; i++) 
	    message ("%s ", argv[i]);
	message ("\n\n");
	mstat (M_INIT, (FILE *) NULL);
    }
    else if (optVerbose) {
	verboseSetLevel (1);
    }

    if (optVerbose)
	compileDate (argv0);

    if (optVerbose && optSpecial)
	paramSetVerbose (1);

    catchSignals ();

    dmInit (argv0);

    dmproject = dmOpenProject (DEFAULT_PROJECT, DEFAULT_MODE);

    getParameters (dmproject, paramDef, paramFile);

    optFlat = paramLookupB ("flat_extraction", (optFlat ? "on" : "off"));

    if (!optFlat && optHier3D) {
	
	/* Perform hierarchical (3D) capacitance extraction by running
	/* master3d. This program will call makeboxl, decom, space3d,
	/* merge etc.
	*/

	int window;
	char extraOpt[64];
	char **masterArgv;
	char *val;
	int j;

	dmCloseProject (dmproject, COMPLETE);
	dmQuit ();

        extraOpt[0] = '\0';
        if (val = paramLookupS ("fe_cap_window", (char *) NULL)) {
	    if (sscanf (val, "%d", &window) == 1) {
		if (argvSave[1][2] == '%')
		    sprintf (extraOpt, "-%%W %d", window); 
		else
		    sprintf (extraOpt, "-W %d", window); 
	    }
	}
	masterArgv = NEW (char *, argcSave + 2);
	i = 0;
	masterArgv[i++] = "master3d";
	if (extraOpt[0] != '\0')
	    masterArgv[i++] = extraOpt; 
	for (j = 1; j <= argcSave; j++)
	    masterArgv[i++] = argvSave[j];

	execv (masterArgv[0], masterArgv);
    }

    if (optFlat && optMinDepth != ROOT_DEPTH) {
	say ("Options -D and -I are ignored with -F");
	optMinDepth = ROOT_DEPTH;
    }

    getTechnology (dmproject, techDef, techFile);

    if (optTime) tock ("startup");

    if (optDisplay) {
        say ("display mode not supported");
	die ();
    }
    else while (*cellNames) {
	extractTree (*cellNames++);
    }

    dmCloseProject (dmproject, COMPLETE);
    dmQuit ();

    if (optTime) clockPrintAll (stdout);

    if (optInfo) {
	nodePointStatistics (stdout);

	scanPrintInfo (stdout);


	mstat (M_PRINT, stdout);
    }

    if (optInfo)
	clockPrintTime (stdout);

    if (optMonitor) monitorStop ();

    verbose ("%s: --- Finished ---\n", argv0);

    return (0);
}

extractTree (cellName)
char * cellName;
{
    void   prepass ();

    extern do_t * findCandidates ();
    do_t * list = NULL;
    int i;

    if (!optFlat) verbose ("extract hierarchy of %s\n", cellName);

    list = findCandidates (dmproject, cellName);

    for (i = 0; list != NULL && list[i].name != NULL; i++) {
	Debug (fprintf (stderr,
	    "%s, status %#o\n", list[i].name, list[i].status));
	if (list[i].status & DEVMOD) {
	    verbose ("%s skipped, device model present\n", list[i].name);
	}
	else if (list[i].status & ISMACRO) {
	    verbose ("%s skipped, has macro status\n", list[i].name);
	}
	else if (list[i].status & DO_SPACE) {
	    if (alsoPrePass) {
		if (optResMesh) {
		    prePass1 = TRUE;
		    prepass (list + i);
		    prePass1 = FALSE;
		}
		if (optSelectiveRes || optPrick) {
		    prePass2 = TRUE;
		    prepass (list + i);
		    prePass2 = FALSE;
		}
	    }

	    if (!optOnlyPrePass)
		extract (list + i);
	}
	else {
	    verbose ("%s already extracted\n", list[i].name);
	}
    }
}

private void prepass (dObject)
do_t * dObject;
{
    bool_t optResSave;
    bool_t optCapSave;
    bool_t optCoupCapSave;
    bool_t optLatCapSave;

    /* optAllRes is not turned off
     * since it may be used during mesh refinement
     */

    optResSave = optRes, optRes = FALSE;
    optCapSave = optCap, optCap = FALSE;
    optCoupCapSave = optCoupCap, optCoupCap = FALSE;
    optLatCapSave = optLatCap, optLatCap = FALSE;

    extract (dObject);


    optRes = optResSave;
    optCap = optCapSave;
    optCoupCap = optCoupCapSave;
    optLatCap = optLatCapSave;
}


private void extract (dObject)
do_t * dObject;
{
    char * cellname = dObject -> name;
    DM_CELL * layoutKey = dObject -> lkey;
    DM_STREAM * info3;
    int outScale = SCALE;
    int inScale = SCALE;	  /* for reading term files */
    extern double physBandWidth;  /* this one is set in getParams */
    extern double Z_bot, Z_top;	  /* these are set in getTechnology */
    extern double ceil ();
    extern maskinfo_t * masktable;
    extern int nrOfMasks;
    char *modulename;
    char *help;
    int i;
    static double old_meters = 1.0;


    ASSERT (layoutKey);

    if (optTime) tick (mprintf ("extract %s", cellname));


    if ((optResMesh && !prePass1)
	|| (optReadNetTerm && optNoCore)) {
        /* we are going to read the 'mesh_gln' stream as well */
        masktable[nrOfMasks].name = "mesh";
        masktable[nrOfMasks].terminal = 0;
        masktable[nrOfMasks].color = 0;
        masktable[nrOfMasks].conductor = -1;
        nrOfMasks++;
    }

    if (dObject -> status & DO_EXP 
	&& (!alsoPrePass || prePass1 || (!optResMesh && prePass2))) {
	ASSERT (!optNoPrepro);
	expand (dObject);
	layoutKey = dObject -> lkey;
    }

    modulename = strsave (cellname);
    /* remove possible trailing versionnumber */
    for (i = 0; modulename[i] != '#' && modulename[i] != '\0'; i++);
    modulename[i] = '\0';

    if (optNoCore) {      /* add the character 'K' before modulename */
	help = NEW (char, strlen (modulename) + 2);
	for (i = strlen (modulename) + 1; i > 0; i--) {
	    help[i] = modulename[i - 1];
	}
	help[i] = 'K';
	modulename = help;
    }

    if (!alsoPrePass || prePass1 || (!optResMesh && prePass2)) {
        Debug (fprintf (stderr, "dmCheckOut %s CIRCUIT UPDATE\n", modulename));
        circuitKey = dmCheckOut
         (dmproject, cktName (modulename), DERIVED, DONTCARE, CIRCUIT, UPDATE);
	/* check out circuitKey only during the first pass;
	 * during a (possible) second pass, circuitKey has received its
	 * correct value during the previous call to the extract routine
	 * (first pass !).
	 */
    }

    ASSERT (layoutKey);
    ASSERT (circuitKey);

    if ((prePass1 && !optSelectiveRes && !optPrick)
	|| (!optResMesh && prePass2))
	verbose ("prepassing %s\n", cellname);
    else if (prePass1)
	verbose ("prepassing (1) %s\n", cellname);
    else if (prePass2)
	verbose ("prepassing (2) %s\n", cellname);
    else
	verbose ("extracting %s\n", cellname);

    readTid (layoutKey, circuitKey);

    info3 = dmOpenStream (layoutKey, "info3", "r");
    dmGetDesignData (info3, GEO_INFO3);
    dmCloseStream (info3, COMPLETE);

    if (ginfo3.nr_samples != 0) {
	outScale = ginfo3.nr_samples;
	inScale = 1;
    }

    meters = (1e-6 * layoutKey -> dmproject -> lambda) / outScale;

    /* tell XR value to CS module */
    setXr ((coor_t) (outScale * ginfo3.bxr));

    setContext (cellname, outScale, 
		    (coor_t) (outScale * ginfo3.bxl),
		    (coor_t) (outScale * ginfo3.bxr));

    /* convert bandWidth from param file to coor_t */
    bandWidth = ceil (physBandWidth * outScale / 
		      layoutKey -> dmproject -> lambda);


    initExtract (circuitKey, layoutKey, outScale);

    /*
     * Properly scale the Z-window, used for DISPLAY as well as CAP3D
     * This has to be done before worldCoordinates below.
     * For safety, also done before cap3dInit.
     */
    Z_bot *= old_meters / meters;
    Z_top *= old_meters / meters;
    old_meters = meters;


    openInput (layoutKey, inScale);


    scan ();

    closeInput ();

    endExtract ();


    disposeTid ();

    if ((optResMesh && !prePass1)
	|| (optReadNetTerm && optNoCore)) {
	nrOfMasks--;
    }

    if ((!prePass1 && !prePass2) 
	|| (optOnlyPrePass && !(prePass1 && (optSelectiveRes || optPrick)))) {
	dmCheckIn (layoutKey, COMPLETE);

	if (optCheckInQuit) {
	    dmCheckIn (circuitKey, QUIT);
	}
	else {
	    dmCheckIn (circuitKey, COMPLETE);
	    _dmAddCellEquivalence (dmproject, modulename, LAYOUT, 
					      modulename, CIRCUIT);
	}
    }
    

    if (optTime) tock (mprintf ("extract %s", cellname));
}

# define MKBOX    "makeboxl"
# define HIER     "-H"
# define FLAT     NULL
# define MKGLN    "makegln"

/* Warning: can modify dObject -> lkey.
 */
private void expand (dObject)
do_t * dObject;
{
    char * cellname = dObject -> name;
    DM_CELL * layoutKey = dObject -> lkey;

#   if NCF_RELEASE >= 400
    int versionnumber = dObject -> versionnumber;
    char *p;
    char buf[132];
    int checkoutmode;
#   endif /* NCF_RELEASE >= 400 */

#   if NCF_RELEASE < 430
#   ifdef DEBUG
    extern bool_t existDmStream ();
    if (existDmStream (layoutKey, "DoNotMkgln")) {
	verbose ("cannot preprocess %s\n", cellname);
	return;
    }
#   endif /* DEBUG */
#   endif /* NCF_RELEASE */

    verbose ("preprocessing %s\n", cellname);

#   if NCF_RELEASE >= 400

    /* runtime assertion, because optNoPrepro should be true
     * to prevent entering this routine.
     * We could compile, however.
     */
    ASSERT (NCF_RELEASE >= 430);

    /* We must checkin first, since the access rights obtained
     * by Space cannot be transfered to makebox and makegln.
     * At exit of this routine, we checkout again and return the
     * new key.
     */
    dmCheckIn (layoutKey, COMPLETE);

    /* add versionnumber to cellname, if it has not one already */
    for (p = cellname; *p != '#' && *p != '\0'; p++);

    if (*p == '\0') {
	sprintf (buf, "%s#%d", cellname, versionnumber);
	cellname = buf;
    }
#   endif

    run (MKBOX, cellname, optFlat ? FLAT : HIER);

#   if NCF_RELEASE < 400
    if (paramLookupB ("delete_bxx", "on"))
	run (MKGLN, cellname, "-D"); /* delete the bxx and nxx files */
    else
	run (MKGLN, cellname, (char *) NULL); /* do not delete */
#   else /* NCF_RELEASE >= 400 */
    run (MKGLN, cellname, (char *) NULL);
#   endif

#   if NCF_RELEASE >= 400
    checkoutmode = (((alsoPrePass || optOnlyPrePass)
		      && (optResMesh || optSelectiveRes || optPrick))
		    || optGenNetTerm) ? ATTACH : READONLY;

    /* We checkout with version number attached to cellname,
     * to be sure that we have the same one as we just checked in.
     * Verstion status does not really care.
     */
    layoutKey = dmCheckOut (dmproject, cellname, ACTUAL, 
				 DONTCARE, LAYOUT, checkoutmode);
    dObject -> lkey = layoutKey;

#   endif /* NCF_RELEASE */

    return;
}

int dmError (s)
char *s;
{
    dmPerror (s);
    die ();
}

void die ()
{
    dmQuit ();

    if (optMonitor) monitorStop ();

    exit (1);
}

void quit ()
{
    dmQuit ();

    if (optMonitor) monitorStop ();

    (void) exit (0);
}

private void notSupported (arg)
char * arg;
{
    say ("option '-%s' not supported in this version of %s, ignored",
	arg, argv0);
}

extern char icdpath[];
private void run (command, cellname, option)
char * command;
char * cellname;
char * option;
{
    char path[1000];
    sprintf (path, "%s/bin/%s", icdpath, command);
    if (option) {
	Debug (fprintf (stderr, "%s %s %s\n", path, option, cellname));
	if (_dmRun (path, option, cellname, (char *) NULL)) {
	    say ("error in '%s %s %s'", command, option, cellname);
	    die ();
	}
    }
    else {
	Debug (fprintf (stderr, "%s %s\n", path, cellname));
	if (_dmRun (path, cellname, (char *) NULL)) {
	    say ("error in '%s %s'", command, cellname);
	    die ();
	}
    }
}
