/*
 * ext2sim.c --
 *
 * Program to flatten hierarchical .ext files and produce
 * a .sim file, suitable for use as input to simulators
 * such as esim and crystal.
 *
 * Flattens the tree rooted at file.ext, reading in additional .ext
 * files as specified by "use" lines in file.ext.  The output is left
 * in file.sim, unless '-o esSimFile' is specified, in which case the
 * output is left in 'esSimFile'.
 *
 */

#ifndef lint
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/ext2sim/ext2sim.c,v 1.2 2008/12/03 14:12:09 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>	/* for atof() */
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <math.h>	/* for sqrt() in bipolar L,W calculation */

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "windows/windows.h"
#include "textio/textio.h"
#include "dbwind/dbwind.h"	/* for DBWclientID */
#include "commands/commands.h"  /* for module auto-load */
#include "textio/txcommands.h"
#include "extract/extract.h"	/* for extDevTable */
#include "extflat/extflat.h"
#include "extflat/EFint.h"
#include "utils/runstats.h"
#include "utils/malloc.h"

/* C99 compat */
#include "extflat/extflat.h"

/* Forward declarations */
void CmdExtToSim(MagWindow *w, TxCommand *cmd);
bool simnAP(EFNode *node, int resClass, float scale, FILE *outf);
bool simnAPHier(const DevTerm *dterm, const HierName *hierName, int resClass, float scale, FILE *outf);
bool simParseArgs(int *pargc, char ***pargv, ClientData cdata); /* @typedef cb_extflat_args_t (UNUSED) */
int simdevVisit(Dev *dev, HierContext *hc, float scale, Transform *trans, ClientData cdata); /* @typedef cb_extflat_visitdevs_t (UNUSED) */
int simresistVisit(const HierName *hierName1, const HierName *hierName2, float res, ClientData cdata); /* @typedef cb_extflat_visitresists_t (UNUSED) */
int simcapVisit(const HierName *hierName1, const HierName *hierName2, double cap, ClientData cdata); /* @typedef cb_extflat_visitcaps_t (UNUSED) */
int simnodeVisit(EFNode *node, int res, double cap, ClientData cdata); /* @typedef cb_extflat_visitnodes_t (UNUSED) */
int simmergeVisit(Dev *dev, HierContext *hc, float scale, Transform *trans, ClientData cdata); /* @typedef cb_extflat_visitdevs_t (UNUSED) */

/* C99 compat */
int simdevOutNode(const HierName *prefix, const HierName *suffix, const char *name, FILE *outf);
int simdevSubstrate(const HierName *prefix, const HierName *suffix, int type, float scale, bool doAP, FILE *outf);

/* Options specific to ext2sim */
#ifdef EXT2SIM_AUTO
bool esDevNodesOnly = FALSE;
bool esNoAttrs = FALSE;
bool esHierAP = FALSE;
bool esMergeDevsA = FALSE;	/* merge devices of equal length */
bool esMergeDevsC = FALSE;	/* merge devices of equal length & width */

#else
extern bool esDevNodesOnly;
extern bool esNoAttrs;
extern bool esHierAP;
extern bool esMergeDevsA;
extern bool esMergeDevsC;
extern const char esSpiceDefaultGnd[];
extern const char *esSpiceCapNode;
#endif

bool esDoSimExtResis = FALSE;
bool esNoAlias = TRUE;
bool esNoLabel = TRUE;
char simesDefaultOut[FNSIZE];
const char *simesOutName = simesDefaultOut;
char esDefaultAlias[FNSIZE], esDefaultLabel[FNSIZE];
const char *esAliasName = esDefaultAlias;
const char *esLabelName = esDefaultLabel;
char esCapFormat[FNSIZE];
FILE *esSimF = NULL;
FILE *esAliasF = NULL;
FILE *esLabF = NULL;


#define	MIT 0
#define	LBL 1
#define	SU  2

static unsigned short esFormat = MIT;

struct {
    short resClassSource;   /* The resistance class of the source of the dev */
    short resClassDrain;    /* The resistance class of the drain of the dev */
    short resClassSub;	    /* The resistance class of the substrate of the dev */
    TileType devType;	    /* Magic tile type of the device */
    const char *defSubs;    /* The default substrate node */
} fetInfo[TT_MAXTYPES];

typedef struct {
    TileTypeBitMask visitMask;
} nodeClient;

typedef struct {
    const HierName *lastPrefix;
    TileTypeBitMask visitMask;
} nodeClientHier;

#define NO_RESCLASS	-1

#define markVisited(client, rclass) \
  { TTMaskSetType(&((client)->visitMask), rclass); }

#define clearVisited(client) \
  { TTMaskZero(&((client)->visitMask)); }

#define beenVisited(client, rclass)  \
  (TTMaskHasType(&((client)->visitMask), rclass))

#define initNodeClient(node) \
{ \
	(node)->efnode_client = PTR2CD(mallocMagic(sizeof(nodeClient))); \
	TTMaskZero(&((nodeClient *)(node)->efnode_client)->visitMask); \
}


#define initNodeClientHier(node) \
{ \
	(node)->efnode_client = PTR2CD(mallocMagic(sizeof(nodeClientHier))); \
	TTMaskZero(&((nodeClientHier *)(node)->efnode_client)->visitMask); \
}


/* device merging */

#define devIsKilled(n) (esFMult[(n)] <= (float)0.0)
#define DEV_KILLED ((float) -1.0)
#define FMULT_SIZE (1<<10)
static float *esFMult = NULL;
static int    esFMIndex = 0;
static int    esFMSize = FMULT_SIZE;
int  esDevsMerged;

/* macro to add a dev's multiplier to the table and grow it if necessary */
#define addDevMult(f) \
{  \
        if (esFMult == NULL) { \
          esFMult = (float *) mallocMagic((unsigned) (esFMSize*sizeof(float)));  \
        } else if (esFMIndex >= esFMSize) {  \
          int i;  \
          float *op = esFMult;  \
          esFMult = (float *) mallocMagic((unsigned) ((esFMSize = esFMSize*2)*sizeof(float))); \
          for (i = 0; i < esFMSize/2; i++) esFMult[i] = op[i]; \
          if (op) freeMagic(op); \
        }  \
        esFMult[esFMIndex++] = (float)(f); \
}

#define setDevMult(i,f) { esFMult[(i)] = (float)(f); }

#define getCurDevMult() ((esFMult) ? esFMult[esFMIndex-1] : (float)1.0)

/* cache list used to find parallel devs */
typedef struct _devMerge {
        int     l, w;
        const EFNode *g, *s, *d, *b;
        Dev * dev;
        int       esFMIndex;
        const HierName *hierName;
        const struct _devMerge *next;
} devMerge;

#ifdef EXT2SIM_AUTO
const devMerge *devMergeList = NULL;
#else
extern const devMerge *devMergeList;
#endif

/* attributes controlling the Area/Perimeter extraction of fet terminals */
#define ATTR_FLATAP	"*[Ee][Xx][Tt]:[Aa][Pp][Ff]*"
#define ATTR_HIERAP	"*[Ee][Xx][Tt]:[Aa][Pp][Hh]*"
#define ATTR_SUBSAP	"*[Ee][Xx][Tt]:[Aa][Pp][Ss]*"

#define        atoCap(s)       ((EFCapValue)atof(s))

#ifdef MAGIC_WRAPPER

#ifdef EXT2SIM_AUTO
/*
 * ----------------------------------------------------------------------------
 *
 * Tcl package initialization function
 *
 * ----------------------------------------------------------------------------
 */

int
Exttosim_Init(
    Tcl_Interp *interp)
{
    /* Sanity checks! */
    if (interp == NULL) return TCL_ERROR;
    if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL)
	return TCL_ERROR;
    if (Tcl_InitStubs(interp, Tclmagic_InitStubsVersion, 0) == NULL) return TCL_ERROR;

    TxPrintf("Auto-loading EXTTOSIM module\n");
    TxFlushOut();

    /* Replace the auto-load function with the one defined in	*/
    /* this package in the command functions list.		*/

    if (WindReplaceCommand(DBWclientID, "exttosim", CmdExtToSim) < 0)
	return TCL_ERROR;

    /* ext2sim is an alias for exttosim */
    if (WindReplaceCommand(DBWclientID, "ext2sim", CmdExtToSim) < 0)
	return TCL_ERROR;

    Tcl_PkgProvide(interp, "Exttosim", MAGIC_VERSION);
    return TCL_OK;
}

#endif /* EXT2SIM_AUTO */
#endif /* MAGIC_WRAPPER */

/*
 * ----------------------------------------------------------------------------
 *
 * Main Tcl callback for command "magic::exttosim"
 *
 * ----------------------------------------------------------------------------
 */

#define EXTTOSIM_RUN		0
#define EXTTOSIM_ALIAS		1
#define EXTTOSIM_LABELS		2
#define EXTTOSIM_DEFAULT	3
#define EXTTOSIM_FORMAT		4
#define EXTTOSIM_RTHRESH	5
#define EXTTOSIM_CTHRESH	6
#define EXTTOSIM_MERGE		7
#define EXTTOSIM_EXTRESIST	8
#define EXTTOSIM_HELP		9

void
CmdExtToSim(
    MagWindow *w,
    TxCommand *cmd)
{
    int i,flatFlags;
    const char *inName;

    int value;
    int option = EXTTOSIM_RUN;
    int argc = cmd->tx_argc;
    char **argv = cmd->tx_argv;
    const char * const *msg;
    bool err_result;

    short s_rclass, d_rclass, sub_rclass;
    const char *devname;
    const char *subname;
    TileType devtype;
    int idx;

    static EFCapValue LocCapThreshold = 2;
    static int LocResistThreshold = 10;

    static const char * const cmdExtToSimOption[] = {
	"[run] [options]	run exttosim on current cell\n"
	"			use \"run -help\" to get standard options",
	"alias on|off		enable/disable alias (.al) file",
	"labels on|off		enable/disable labels (.nodes) file",
	"default		reset to default values",
	"format MIT|SU|LBL	set output format",
	"rthresh [value]	set resistance threshold value",
	"cthresh [value]	set capacitance threshold value",
	"merge [option]		merge parallel transistors",
	"extresist on|off	incorporate extresist output",
        "help			print help information",
	NULL
    };

    static const char * const sim_formats[] = {
	"MIT",
	"LBL",
	"SU",
	NULL
    };

    static const char * const yesno[] = {
	"yes",
	"true",
	"on",
	"no",
	"false",
	"off",
	NULL
    };

    static const char * const cmdMergeTypes[] = {
	"none			don't merge parallel devices",
	"conservative		merge devices with same L, W",
	"aggressive		merge devices with same L"
    };

    if (cmd->tx_argc > 1)
    {
	option = Lookup(cmd->tx_argv[1], cmdExtToSimOption);
	if (option < 0) option = EXTTOSIM_RUN;
	else argv++;
    }

    switch (option)
    {
	case EXTTOSIM_EXTRESIST:
	    if (cmd->tx_argc == 2)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_SetResult(magicinterp, (esDoSimExtResis) ? "on" : "off", NULL);
#else
		TxPrintf("Extresist: %s\n", (esDoSimExtResis) ? "on" : "off");
#endif
		return;
	    }
	    else if (cmd->tx_argc != 3)
		goto usage;
	    idx = Lookup(cmd->tx_argv[2], yesno);
	    if (idx < 0) goto usage;
	    else if (idx < 3) esDoSimExtResis = TRUE;
	    else esDoSimExtResis = FALSE;
	    break;
	case EXTTOSIM_ALIAS:
	    if (cmd->tx_argc == 2)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_SetResult(magicinterp, (esNoAlias) ? "off" : "on", NULL);
#else
		TxPrintf("Aliases: %s\n", (esNoAlias) ? "off" : "on");
#endif
		return;
	    }
	    else if (cmd->tx_argc != 3)
		goto usage;
	    idx = Lookup(cmd->tx_argv[2], yesno);
	    if (idx < 0) goto usage;
	    else if (idx < 3) esNoAlias = FALSE;
	    else esNoAlias = TRUE;
	    break;
	case EXTTOSIM_LABELS:
	    if (cmd->tx_argc == 2)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_SetResult(magicinterp, (esNoLabel) ? "off" : "on", NULL);
#else
		TxPrintf("Labels: %s\n", (esNoLabel) ? "off" : "on");
#endif
		return;
	    }
	    else if (cmd->tx_argc != 3)
		goto usage;
	    idx = Lookup(cmd->tx_argv[2], yesno);
	    if (idx < 0) goto usage;
	    else if (idx < 3) esNoLabel = FALSE;
	    else esNoLabel = TRUE;
	    break;
	case EXTTOSIM_CTHRESH:
	    if (cmd->tx_argc == 2)
	    {
		if (!IS_FINITE_F(LocCapThreshold))
#ifdef MAGIC_WRAPPER
		    Tcl_SetResult(magicinterp, "infinite", NULL);
#else
		    TxPrintf("Capacitance threshold: infinite\n");
#endif
		else
#ifdef MAGIC_WRAPPER
		    Tcl_SetObjResult(magicinterp,
			Tcl_NewDoubleObj((double)LocCapThreshold));
#else
		    TxPrintf("Capacitance threshold: %lf\n", (double)LocCapThreshold);
#endif
		return;
	    }
	    else if (cmd->tx_argc < 3) goto usage;
	    if (!strncmp(cmd->tx_argv[2], "inf", 3))
		LocCapThreshold = INFINITE_THRESHOLD_F;
	    else if (StrIsNumeric(cmd->tx_argv[2]))
		LocCapThreshold = atoCap(cmd->tx_argv[2]);
	    else
		TxError("exttosim: numeric value or \"infinite\" expected.\n");
	    break;
	case EXTTOSIM_RTHRESH:
	    if (cmd->tx_argc == 2)
	    {
		if (LocResistThreshold == INFINITE_THRESHOLD)
#ifdef MAGIC_WRAPPER
		    Tcl_SetResult(magicinterp, "infinite", NULL);
#else
		    TxPrintf("Resistance threshold: infinite\n");
#endif
		else
#ifdef MAGIC_WRAPPER
		    Tcl_SetObjResult(magicinterp,
			Tcl_NewIntObj(LocResistThreshold));
#else
		    TxPrintf("Resistance threshold: %lf\n", (double)LocResistThreshold);
#endif
		return;
	    }
	    else if (cmd->tx_argc < 3) goto usage;
	    if (StrIsInt(cmd->tx_argv[2]))
		LocResistThreshold = atoi(cmd->tx_argv[2]);
	    else if (!strncmp(cmd->tx_argv[2], "inf", 3))
		LocResistThreshold = INFINITE_THRESHOLD;
	    else
		TxError("exttosim: integer value or \"infinite\" expected.\n");
	    break;
	case EXTTOSIM_FORMAT:
	    if (cmd->tx_argc == 2)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_SetResult(magicinterp, (char*)sim_formats[esFormat], TCL_STATIC);
#else
		TxPrintf("Format: %s\n", sim_formats[esFormat]);
#endif
		return;
	    }
	    else if (cmd->tx_argc < 3) goto usage;
	    value = Lookup(cmd->tx_argv[2], sim_formats);
	    if (value < 0)
		TxError("exttosim: output formats are MIT, LBL, or SU\n");
	    else
		esFormat = value;
	    break;
	case EXTTOSIM_MERGE:
	    if (cmd->tx_argc == 2)
	    {
		if (esMergeDevsA)
#ifdef MAGIC_WRAPPER
		    Tcl_SetResult(magicinterp, "aggressive", NULL);
#else
		    TxPrintf("Merge: aggressive\n");
#endif
		else if (esMergeDevsC)
#ifdef MAGIC_WRAPPER
		    Tcl_SetResult(magicinterp, "conservative", NULL);
#else
		    TxPrintf("Merge: conservative\n");
#endif
		else
#ifdef MAGIC_WRAPPER
		    Tcl_SetResult(magicinterp, "none", NULL);
#else
		    TxPrintf("Merge: none\n");
#endif
		return;
	    }
	    else if (cmd->tx_argc < 3) goto usage;
	    value = Lookup(cmd->tx_argv[2], cmdMergeTypes);
	    if (value < 0)
	    {
		TxError("Merge types are:\n");
		for (msg = &(cmdMergeTypes[0]); *msg != NULL; msg++)
		    TxPrintf("    %s\n", *msg);
	    }
	    else switch (value) {
		case 0:
		    esMergeDevsA = FALSE;
		    esMergeDevsC = FALSE;
		    break;
		case 1:
		    esMergeDevsA = FALSE;
		    esMergeDevsC = TRUE;
		    break;
		case 2:
		    esMergeDevsA = TRUE;
		    esMergeDevsC = FALSE;
		    break;
	    }
	    break;

	case EXTTOSIM_DEFAULT:
	    LocCapThreshold = 2;
	    LocResistThreshold = 10;
	    EFOutputFlags = 0;
	    EFScale = 0.0;
	    if (EFArgTech)
	    {
		freeMagic((char *) EFArgTech);
		EFArgTech = NULL;
	    }
	    if (EFSearchPath)
	    {
		freeMagic((char *) EFSearchPath);
		EFSearchPath = NULL;
	    }
	    break;
	case EXTTOSIM_RUN:
	    goto runexttosim;
	    break;
	case EXTTOSIM_HELP:
usage:
	    for (msg = &(cmdExtToSimOption[0]); *msg != NULL; msg++)
	    {
		TxPrintf("    %s\n", *msg);
	    }
	    break;
    }
    return;

runexttosim:

    esDevsMerged = 0;
    esFMIndex = 0;

    EFInit();

    /* Set local values for capacitor and resistor thresholds */
    EFCapThreshold = LocCapThreshold;
    EFResistThreshold = LocResistThreshold;

    /* Process command line arguments */
    inName = EFArgs(argc, argv, &err_result, simParseArgs, PTR2CD(NULL));

    if (err_result == TRUE)
    {
	EFDone(NULL);
        return /* TCL_ERROR */;
    }

    if (inName == NULL)
    {
	/* Assume that we want to do exttospice on the currently loaded cell */

	if (w == (MagWindow *) NULL)
	    windCheckOnlyWindow(&w, DBWclientID);

	if (w == (MagWindow *) NULL)
	{
	    TxError("Point to a window or specify a cell name.\n");
	    EFDone(NULL);
	    return /* TCL_ERROR */;
	}
	inName = ((CellUse *) w->w_surfaceID)->cu_def->cd_name;
    }

    /*
     * Initializations specific to this program.
     * Make output, alias, and label names be of the form
     * inName.suffix if they weren't explicitly specified,
     * where suffix is .sim, .al, or .nodes.
     */

    /* Addendum:  Because extresis depends on ".sim" output, we	*/
    /* add ".res" in the output name (the same as used for the	*/
    /* ".res.ext" output) so that this output won't be grabbed	*/
    /* the next time "extresist" is run.			*/

    if (simesOutName == simesDefaultOut)
	(void) sprintf(simesDefaultOut, "%s%s.sim", inName,
		((esDoSimExtResis) ? ".ext" : ""));
    if (esAliasName == esDefaultAlias)
	(void) sprintf(esDefaultAlias, "%s%s.al", inName,
		((esDoSimExtResis) ? ".ext" : ""));
    if (esLabelName == esDefaultLabel)
	(void) sprintf(esDefaultLabel, "%s%s.nodes", inName,
		((esDoSimExtResis) ? ".ext" : ""));
    if ((esSimF = fopen(simesOutName, "w")) == NULL)
    {
#ifdef MAGIC_WRAPPER
	char *tclres = Tcl_Alloc(128);
	sprintf(tclres, "exttosim: Unable to open file %s for writing\n",
		simesOutName);
	Tcl_SetResult(magicinterp, tclres, TCL_DYNAMIC);
#else
	TxError("exttosim: Unable to open file %s for writing\n", simesOutName);
#endif
	EFDone(NULL);
	return /* TCL_ERROR */;
    }
    if (!esNoAlias && (esAliasF = fopen(esAliasName, "w")) == NULL)
    {
#ifdef MAGIC_WRAPPER
	char *tclres = Tcl_Alloc(128);
	sprintf(tclres, "exttosim: Unable to open file %s for writing\n",
		esAliasName);
	Tcl_SetResult(magicinterp, tclres, TCL_DYNAMIC);
#else
	TxError("exttosim: Unable to open file %s for writing\n", esAliasName);
#endif
	EFDone(NULL);
	return /* TCL_ERROR */;
    }
    if (!esNoLabel && (esLabF = fopen(esLabelName, "w")) == NULL)
    {
#ifdef MAGIC_WRAPPER
	char *tclres = Tcl_Alloc(128);
	sprintf(tclres, "exttosim: Unable to open file %s for writing\n",
		esLabelName);
	Tcl_SetResult(magicinterp, tclres, TCL_DYNAMIC);
#else
	TxError("exttosim: Unable to open file %s for writing\n", esLabelName);
#endif
	return /* TCL_ERROR */;
    }

    /* Read the hierarchical description of the input circuit */
    if (EFReadFile(inName, FALSE, esDoSimExtResis, FALSE, FALSE) == FALSE)
    {
	EFDone(NULL);
	return /* TCL_ERROR */;
    }

    if ((EFStyle == NULL) && (esFormat == SU))
    {
	TxError("Warning:  Current extraction style does not match .ext file!\n");
	TxError("Area/Perimeter values will be zero.\n");
    }

    /* create default fetinfo entries (MOSIS) which can be overriden by
       the command line arguments */

    for (i = 0; i < TT_MAXTYPES; i++)
    {
	fetInfo[i].resClassSource = NO_RESCLASS;
	fetInfo[i].resClassDrain = NO_RESCLASS;
	fetInfo[i].resClassSub = NO_RESCLASS;
	fetInfo[i].defSubs = NULL;
	fetInfo[i].devType = TT_SPACE;
    }

    /* Get fetInfo information from the current extraction style 	 */
    /* (this works only for the Tcl version with the embedded exttospice */
    /* command)								 */

    idx = 0;
    while (ExtGetDevInfo(idx++, &devname, &devtype, &s_rclass, &d_rclass,
		&sub_rclass, &subname))
    {
	if (idx == TT_MAXTYPES)
	{
	    TxError("Error:  Ran out of space for device types!\n");
	    break;
	}
	i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, devname);

	if (EFStyle != NULL)
	{
	    fetInfo[i].resClassSource = s_rclass;
	    fetInfo[i].resClassDrain = d_rclass;
	    fetInfo[i].resClassSub = sub_rclass;
	    fetInfo[i].defSubs = subname;
	    fetInfo[i].devType = devtype;
	}
    }

    /* Write the output file */

    fprintf(esSimF, "| units: %g tech: %s format: %s\n", EFScale, EFTech,
		   (esFormat == MIT) ? "MIT" :
		      ((esFormat == LBL) ? "LBL" : "SU"));

    /* Convert the hierarchical description to a flat one */
    flatFlags = EF_FLATNODES;
    if (IS_FINITE_F(LocCapThreshold)) flatFlags |= EF_FLATCAPS;
    if (LocResistThreshold != INFINITE_THRESHOLD) flatFlags |= EF_FLATRESISTS;
    EFFlatBuild(inName, flatFlags);

    if (esMergeDevsA || esMergeDevsC)
    {
	const devMerge *p;

	EFVisitDevs(simmergeVisit, PTR2CD(NULL));
	TxPrintf("Devices merged: %d\n", esDevsMerged);
	esFMIndex = 0;
	free_magic1_t mm1 = freeMagic1_init();
	for (p = devMergeList; p != NULL; p = p->next)
	    freeMagic1(&mm1, (char *)p);
	freeMagic1_end(&mm1);
	devMergeList = NULL;
    }

    EFVisitDevs(simdevVisit, PTR2CD(NULL));
    if (flatFlags & EF_FLATCAPS) {
	EFVisitCaps(simcapVisit, PTR2CD(NULL));
    }
    EFVisitResists(simresistVisit, PTR2CD(NULL));
    esSpiceCapNode = esSpiceDefaultGnd;
    EFVisitNodes(simnodeVisit, PTR2CD(NULL));

    EFFlatDone(NULL);
    EFDone(NULL);

    if (esSimF) fclose(esSimF);
    if (esLabF) fclose(esLabF);
    if (esAliasF) fclose(esAliasF);

    TxPrintf("exttosim finished.\n");
}

#ifdef EXT2SIM_MAIN	/* Independent program "ext2sim" deprecated */

/*
 * ----------------------------------------------------------------------------
 *
 * main --
 *
 * Top level of ext2sim (non-Tcl version only)
 *
 * ----------------------------------------------------------------------------
 */

int
main(
    int argc,
    char *argv[])
{

    int i,flatFlags;
    const char *inName;
    FILE *f;

    esDevsMerged = 0;

    EFInit();
    /* create default fetinfo entries (MOSIS) which can be overriden by
       the command line arguments */
    for (i = 0; i < TT_MAXTYPES; i++) {
	fetInfo[i].resClassSource = NO_RESCLASS;
	fetInfo[i].resClassDrain = NO_RESCLASS;
	fetInfo[i].resClassSub = NO_RESCLASS;
	fetInfo[i].defSubs = NULL;
	fetInfo[i].devType = TT_SPACE;
    }
    i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "nfet");
    fetInfo[i].resClassSource = fetInfo[i].resClassDrain = 0;
    fetInfo[i].resClassSub = NO_RESCLASS;
    fetInfo[i].defSubs = "Gnd!";
    i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "pfet");
    fetInfo[i].resClassSource = fetInfo[i].resClassDrain = 1;
    fetInfo[i].resClassSub = 6;
    fetInfo[i].defSubs = "Vdd!";
    i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "nmos");
    fetInfo[i].resClassSource = fetInfo[i].resClassDrain = 0;
    fetInfo[i].resClassSub = NO_RESCLASS;
    fetInfo[i].defSubs = "Gnd!";
    i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "pmos");
    fetInfo[i].resClassSource = fetInfo[i].resClassDrain = 1;
    fetInfo[i].resClassSub = 6;
    fetInfo[i].defSubs = "Vdd!";
    /* Process command line arguments */
    inName = EFArgs(argc, argv, NULL, simParseArgs, PTR2CD(NULL));
    if (inName == NULL)
	exit (1);

    /*
     * Initializations specific to this program.
     * Make output, alias, and label names be of the form
     * inName.suffix if they weren't explicitly specified,
     * where suffix is .sim, .al, or .nodes.
     */

    if (simesOutName == simesDefaultOut)
	(void) sprintf(simesDefaultOut, "%s.sim", inName);
    if (esAliasName == esDefaultAlias)
	(void) sprintf(esDefaultAlias, "%s.al", inName);
    if (esLabelName == esDefaultLabel)
	(void) sprintf(esDefaultLabel, "%s.nodes", inName);
    if ((esSimF = fopen(simesOutName, "w")) == NULL)
    {
	perror(simesOutName);
	exit (1);
    }
    if (!esNoAlias && (esAliasF = fopen(esAliasName, "w")) == NULL)
    {
	perror(esAliasName);
	exit (1);
    }
    if (!esNoLabel && (esLabF = fopen(esLabelName, "w")) == NULL)
    {
	perror(esLabelName);
	exit (1);
    }

    /* Read the hierarchical description of the input circuit */
    if (EFReadFile(inName, FALSE, esDoSimExtResis, FALSE, FALSE) == FALSE)
    {
	exit(1);
    }

    fprintf(esSimF, "| units: %g tech: %s format: %s\n", EFScale, EFTech,
		   (esFormat == MIT) ? "MIT" :
		      ((esFormat == LBL) ? "LBL" : "SU"));

    /* Convert the hierarchical description to a flat one */
    flatFlags = EF_FLATNODES;
    if (IS_FINITE_F(EFCapThreshold)) flatFlags |= EF_FLATCAPS;
    if (EFResistThreshold != INFINITE_THRESHOLD) flatFlags |= EF_FLATRESISTS;
    EFFlatBuild(inName, flatFlags);

    if (esMergeDevsA || esMergeDevsC) {
	const devMerge *p;

	EFVisitDevs(simmergeVisit, PTR2CD(NULL));
	TxPrintf("Devices merged: %d\n", esDevsMerged);
	esFMIndex = 0;
	free_magic1_t mm1 = freeMagic1_init();
	for (p = devMergeList; p != NULL; p = p->next)
	    freeMagic1(&mm1, (char *)p);
	freeMagic1_end(&mm1);
    }

    EFVisitDevs(simdevVisit, PTR2CD(NULL));
    if (flatFlags & EF_FLATCAPS)
	EFVisitCaps(simcapVisit, PTR2CD(NULL));
    EFVisitResists(simresistVisit, PTR2CD(NULL));
    esSpiceCapNode = esSpiceDefaultGnd;
    EFVisitNodes(simnodeVisit, PTR2CD(NULL));

    EFFlatDone(NULL);
    EFDone(NULL);

    if (esSimF) fclose(esSimF);
    if (esLabF) fclose(esLabF);
    if (esAliasF) fclose(esAliasF);

    TxPrintf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL));
    exit(0);
}

#endif /* EXT2SIM_MAIN Deprecated */

/*
 * ----------------------------------------------------------------------------
 *
 * simParseArgs --
 *
 * Process those arguments that are specific to ext2sim.
 * Assumes that *pargv[0][0] is '-', indicating a flag
 * argument.
 *
 * Results:
 *	None.  TCL version returns False if an error is encountered
 *	while parsing arguments, True otherwise.
 *
 * Side effects:
 *	After processing an argument, updates *pargc and *pargv
 *	to point to after the argument.
 *
 *	May initialize various global variables based on the
 *	arguments given to us.
 *
 *	Exits in the event of an improper argument.
 *
 * ----------------------------------------------------------------------------
 */

/* @typedef cb_extflat_args_t */
bool
simParseArgs(
    int *pargc,
    char ***pargv,
    ClientData cdata)	/* unused */
{
    char **argv = *pargv;
    int argc = *pargc;

    switch (argv[0][1])
    {
	case 'A':
	    esNoAlias = TRUE;
	    break;
	case 'B':
	    esNoAttrs = TRUE;
	    break;
	case 'F':
	    esDevNodesOnly = TRUE;
	    break;
	case 'L':
	    esNoLabel = TRUE;
	    break;
	case 'M':
	    esMergeDevsA = TRUE;
	    break;
	case 'm':
	    esMergeDevsC = TRUE;
	    break;
	case 'a':
	    if ((esAliasName = ArgStr(&argc, &argv, "filename")) == NULL)
		goto usage;
	    break;
	case 'l':
	    if ((esLabelName = ArgStr(&argc, &argv, "filename")) == NULL)
		goto usage;
	    break;
	case 'o':
	    if ((simesOutName = ArgStr(&argc, &argv, "filename")) == NULL)
		goto usage;
	    break;
	case 'f': {
	     const char *ftmp;

	     if ((ftmp = ArgStr(&argc, &argv, "format")) == NULL)
		goto usage;
	     if (strcasecmp(ftmp,"MIT") == 0)
	        esFormat = MIT;
	     else if (strcasecmp(ftmp,"LBL") == 0)
		esFormat = LBL;
	     else if (strcasecmp(ftmp,"SU") == 0)
		esFormat = SU;
	     else goto usage;
	     break;
	     }
	case 'y': {
	      const char *t;

	      if ((t = ArgStr(&argc, &argv, "cap-accuracy")) == NULL)
		goto usage;
	      TxPrintf("Cap accuracy option -y is deprecated.\n");
	      break;
	      }
	case 'J':
	     {
	     const char *ftmp;

	     if ((ftmp = ArgStr(&argc, &argv, "hierAP_SD")) == NULL)
		goto usage;
	     if (strcasecmp(ftmp, "HIER") == 0)
		esHierAP = TRUE;
	     else if (strcasecmp(ftmp, "FLAT") == 0)
		esHierAP = FALSE;
	     else goto usage;

	     break;
	     }
	default:
	    TxError("Unrecognized flag: %s\n", argv[0]);
	    goto usage;
    }

    *pargv = argv;
    *pargc = argc;
    return FALSE;

usage:
    TxError("Usage: ext2sim [-a aliasfile] [-A] [-B] [-l labelfile] [-L]\n"
		"[-o simfile] [-J flat|hier] [-y cap_digits]\n"
		"[-f mit|lbl|su] "
		"[file]\n"
		);

    return TRUE;
}


/*
 * ----------------------------------------------------------------------------
 *
 * SimGetNode --
 *
 * function to find a node given its hierarchical prefix and suffix
 *
 * Results:
 *  a pointer to the node struct or NULL
 *
 * ----------------------------------------------------------------------------
 */
EFNode *
SimGetNode(
    const HierName *prefix,
    const HierName *suffix)
{
	HashEntry *he;

	he = EFHNConcatLook(prefix, suffix, "output");
	return(((EFNodeName *) HashGetValue(he))->efnn_node);
}


/*
 * ----------------------------------------------------------------------------
 *
 * simdevVisit --
 *
 * Procedure to output a single dev to the .sim file.
 * Called by EFVisitDevs().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSimF.
 *
 * Format of a .sim dev line:
 *
 *	type gate source drain l w x y g= s= d=
 *
 * where
 *	type is a name identifying this type of transistor
 *	gate, source, and drain are the nodes to which these three
 *		terminals connect
 *	l, w are the length and width of the channel
 *	x, y are the x, y coordinates of a point within the channel.
 *	g=, s=, d= are the (optional) attributes; if present, each
 *		is followed by a comma-separated list of attributes.
 *
 * ----------------------------------------------------------------------------
 */

/* ARGSUSED */
/* @typedef cb_extflat_visitdevs_t (UNUSED) */
int
simdevVisit(
    Dev *dev,		/* Device being output */
    HierContext *hc,	/* Hierarchical context down to this device */
    float scale,	/* Scale transform for output */
    Transform *trans,	/* Coordinate transform */
    ClientData cdata)	/* unused */
{
    const DevTerm *gate, *source, *drain, *term;
    const EFNode *subnode;
    EFNode *snode, *dnode;
    int l, w;
    Rect r;
    char name[12];
    bool is_subckt = FALSE;
    const HierName *hierName = hc->hc_hierName;

    sprintf(name, "output");

    /* If no terminals, can't do much of anything */
    if (dev->dev_nterm < 1)
	return 0;

    /* If one terminal, can't do much of anything, either, 	*/
    /* except maybe with a subcircuit device.  That's not	*/
    /* supported by ext2sim, though. . .			*/

    if (dev->dev_nterm < 2)
	return 0;

    /* Merged devices */
    if ((esMergeDevsA || esMergeDevsC) && devIsKilled(esFMIndex++))
	return 0;

    /* Computation of length and width has been moved from efVisitDevs */
    EFGetLengthAndWidth(dev, &l, &w);
    if (esMergeDevsA || esMergeDevsC) w *= getCurDevMult();

    /* If only two terminals, connect the source to the drain */
    gate = &dev->dev_terms[0];
    if (dev->dev_nterm >= 2)
	source = drain = &dev->dev_terms[1];
    if (dev->dev_nterm >= 3)
	drain = &dev->dev_terms[2];
    subnode = dev->dev_subsnode;

    /* Kludge for .sim: transistor types can only be one character */
    switch(dev->dev_class)
    {
	/* "sim" types are fixed according to the device class */
	case DEV_BJT:
	    fprintf(esSimF, "b");	/* sim format extension */
	    break;
	case DEV_DIODE:
	case DEV_PDIODE:
	case DEV_NDIODE:
	    is_subckt = TRUE;
	    fprintf(esSimF, "x");	/* no class for diodes, use subckt */
	    break;
	case DEV_RES:
	    fprintf(esSimF, "r");
	    break;
	case DEV_CAP:
	case DEV_CAPREV:
	    fprintf(esSimF, "c");	/* sim format extension */
	    break;
	case DEV_FET:
	case DEV_MOSFET:
	case DEV_ASYMMETRIC:
	    /* The sim file format only understands "n" and "p" for FETs.   */
	    /* The extraction method says nothing about which is which.	    */
	    /* The EFDevTypes[] should ideally start with "n" or "p".  If   */
	    /* it doesn't, then dev->dev_type should.  If neither does,	    */
	    /* then use EFDevTypes[] but flag an error.			    */

	    if (EFDevTypes[dev->dev_type][0] == 'n' ||
		    EFDevTypes[dev->dev_type][0] == 'p')
	    {
		fprintf(esSimF, "%c", EFDevTypes[dev->dev_type][0]);
	    }
	    else
	    {
		TileType ttype = fetInfo[dev->dev_type].devType;

		if (DBTypeLongNameTbl[ttype][0] == 'n' ||
			    DBTypeLongNameTbl[ttype][0] == 'p')
		{
		    fprintf(esSimF, "%c", DBTypeLongNameTbl[ttype][0]);
		}
		else
		{
		    TxError("Error: MOSFET device type \"%s\" does not start with "
			    "\"n\" or \"p\" as required for the .sim format\n",
			    EFDevTypes[dev->dev_type]);

		    /* Default to "n" */
		    fprintf(esSimF, "n");
		}
	    }
	    break;
	case DEV_MSUBCKT:
	case DEV_CSUBCKT:
	case DEV_DSUBCKT:
	case DEV_RSUBCKT:
	case DEV_SUBCKT:
	    /* Use the 'x' type in .sim format.  This is implemented in the */
	    /* IRSIM "user subcircuit" package, so it has a valid syntax.   */
	    /* It is used by the extresist code in magic as a way to work   */
	    /* around the lack of substrate and lack of device names in the */
	    /* .sim format.						    */
	    is_subckt = TRUE;
	    fprintf(esSimF, "x");
	    break;

	case DEV_VERILOGA:
	    /* Treat verilog-A models as subcircuits (for now, anyway)	*/
	    /* since IRSIM has no concept of verilog-A models.		*/
	    is_subckt = TRUE;
	    fprintf(esSimF, "x");
	    break;

	default:
	    fprintf(esSimF, "%c", EFDevTypes[dev->dev_type][0]);
	    break;
    }

    /* Output gate node name.  Resistor devices don't generate this node. */
    if (dev->dev_class != DEV_RES)
       simdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier, name, esSimF);

    /* Output source and drain node names */
    if (dev->dev_nterm > 1)
	simdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier, name, esSimF);

    /* Hack for BiCMOS---see scmos.tech27 hack:  Tim, 7/16/96 */
    /* Second hack, using dev_class 2/20/03 */
    if (EFDevTypes[dev->dev_type][0] == 'b')
	dev->dev_class = DEV_BJT;

    if (dev->dev_class == DEV_BJT && subnode)
    {
        sprintf(name, "fet");
        simdevOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSimF);
    }
    else if ((dev->dev_class == DEV_DIODE || dev->dev_class == DEV_PDIODE ||
		dev->dev_class == DEV_NDIODE) && dev->dev_nterm == 1 && subnode)
    {
        sprintf(name, "fet");
        simdevOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSimF);
    }
    else if (dev->dev_nterm > 2)
        simdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier, name, esSimF);

    if (dev->dev_nterm > 3)	/* For subcircuit support ('x' device) */
    {
	int i;

        sprintf(name, "subckt");
	for (i = 3; i < dev->dev_nterm; i++)
	{
	    term = &dev->dev_terms[i];
	    simdevOutNode(hierName, term->dterm_node->efnode_name->efnn_hier,
			name, esSimF);
	}
    }

    if (dev->dev_class != DEV_DIODE && dev->dev_class != DEV_NDIODE &&
		dev->dev_class != DEV_PDIODE)
    {
	if (is_subckt && subnode)
	{
	    /* As a general policy on subcircuits supporting extresist,	*/
	    /* output the subcircuit node as the last port of the	*/
	    /* subcircuit definition, *except* for the use of the 'x'	*/
	    /* device type for diodes.					*/

	    putc(' ', esSimF);
	    simdevSubstrate(hierName, subnode->efnode_name->efnn_hier,
	             dev->dev_type, 0.0, FALSE, esSimF);
	}

	/* Support gemini's substrate comparison */
	else if (esFormat == LBL && subnode)
	{
	    putc(' ', esSimF);
	    simdevSubstrate(hierName, subnode->efnode_name->efnn_hier,
	             dev->dev_type, 0.0, FALSE, esSimF);
	}
    }

    GeoTransRect(trans, &dev->dev_rect, &r);

    if (dev->dev_class == DEV_BJT || EFDevTypes[dev->dev_type][0] == 'b')
    {
       /* Bipolar sim format:  We don't have the length and width
	* of the collector well, but we can get it from the area
	* and perimeter measurements; hopefully any strict netlist
	* comparator will deal with any problem arising from
	* swapping L and W.
	*/

	int n;
	double cl, cw;
	double chp = 0.0;
	double ca = 0.0;

	for (n = 0; n < efNumResistClasses; n++) {
	    ca += (double)(subnode->efnode_pa[n].pa_area);
	    chp += 0.5 * (double)(subnode->efnode_pa[n].pa_perim);
	}

	cl = 0.5 * (chp + sqrt(chp * chp - 4 * ca));
	cw = ca / cl;

	fprintf(esSimF, " %d %d %g %g", (int)cl, (int)cw,
		r.r_xbot * scale, r.r_ybot * scale);
    }
    else if (dev->dev_class == DEV_RES) {	/* generate a resistor */
       fprintf(esSimF, " %f", (double)(dev->dev_res));
    }
    else if (dev->dev_class == DEV_CAP) {	/* generate a capacitor */
       fprintf(esSimF, " %f", (double)(dev->dev_cap));
    }
    else if (dev->dev_class == DEV_CAPREV) {	/* generate a capacitor */
       fprintf(esSimF, " %f", (double)(dev->dev_cap));
    }
    else if (is_subckt)
    {
	/* Output source and drain attributes */
	if (source->dterm_attrs)
	    fprintf(esSimF, " s=%s", source->dterm_attrs);
	if ((source != drain) && drain->dterm_attrs)
	       fprintf(esSimF, " d=%s", drain->dterm_attrs);

	/* Output length, width, and position as attributes */
        fprintf(esSimF, " l=%g w=%g x=%g y=%g",
		l * scale, w * scale, r.r_xbot * scale, r.r_ybot * scale);
    }
    else if ((dev->dev_class != DEV_DIODE) && (dev->dev_class != DEV_PDIODE)
		&& (dev->dev_class != DEV_NDIODE)) {

       /*
        * Scale L and W appropriately by the same amount as distance
        * values in the transform.  The transform will have a scale
        * different from 1 only in the case when the scale factors of
        * some of the .ext files differed, making it necessary to scale
        * all dimensions explicitly instead of having a single scale
        * factor at the beginning of the .sim file.
        */

       fprintf(esSimF, " %g %g %g %g",
		l * scale, w * scale, r.r_xbot * scale, r.r_ybot * scale);

       /* Attributes, if present */
       if (!esNoAttrs)
       {
	   bool subAP= FALSE, hierS = esHierAP, hierD = esHierAP;

	   if (gate->dterm_attrs)
	       fprintf(esSimF, " g=%s", gate->dterm_attrs);
	   if (esFormat == SU) {
	       if (gate->dterm_attrs) {
	         subAP = Match(ATTR_SUBSAP, gate->dterm_attrs);
	         fprintf(esSimF, ",");
	       } else
	         fprintf(esSimF, " g=");
	       simdevSubstrate(hierName, subnode->efnode_name->efnn_hier,
		         dev->dev_type, scale, subAP, esSimF);
	   }
	   if (source->dterm_attrs) {
	       fprintf(esSimF, " s=%s", source->dterm_attrs);
	       if (Match(ATTR_HIERAP, source->dterm_attrs) != FALSE)
		   hierS = TRUE;
	       else if (Match(ATTR_FLATAP, source->dterm_attrs) != FALSE)
		   hierS = FALSE;
	   }
	   if (esFormat == SU) {
	       fprintf(esSimF, "%s", (source->dterm_attrs) ? "," : " s=");
	       if (hierS)
	         simnAPHier(source, hierName, fetInfo[dev->dev_type].resClassSource,
		      scale, esSimF);
	       else {
	         snode= SimGetNode(hierName,
			     source->dterm_node->efnode_name->efnn_hier);
	         simnAP(snode, fetInfo[dev->dev_type].resClassSource, scale, esSimF);
	       }
	   }
	   if (drain->dterm_attrs) {
	       fprintf(esSimF, " d=%s", drain->dterm_attrs);
	       if  (Match(ATTR_HIERAP, drain->dterm_attrs) != FALSE)
		   hierD = TRUE;
	       else if (Match(ATTR_FLATAP, drain->dterm_attrs) != FALSE)
		   hierD = FALSE;
	   }
	   if (esFormat == SU) {
	       fprintf(esSimF, "%s", (drain->dterm_attrs) ? "," : " d=");
	       if (hierD)
	         simnAPHier(drain, hierName, fetInfo[dev->dev_type].resClassDrain,
		      scale, esSimF);
	       else {
	         dnode = SimGetNode(hierName,
			      drain->dterm_node->efnode_name->efnn_hier);
	         simnAP(dnode, fetInfo[dev->dev_type].resClassDrain,
		      scale, esSimF);
	       }
	   }
       }
    }

    if (is_subckt)
    {
	/* Last token on a subcircuit 'x' line is the subcircuit name */
	fprintf(esSimF, " %s", EFDevTypes[dev->dev_type]);
    }

    fprintf(esSimF, "\n");

    return 0;
}

int
simdevSubstrate(
    const HierName *prefix,
    const HierName *suffix,
    int type,
    float scale,
    bool doAP,
    FILE *outf)
{
    HashEntry *he;
    const EFNodeName *nn;
    char *suf;
    int l;
    EFNode *subnode;

    suf = EFHNToStr(suffix);
    if (fetInfo[type].defSubs && strcasecmp(suf,fetInfo[type].defSubs) == 0) {
    	l = strlen(suf) - 1;
	if (((EFOutputFlags & EF_TRIMGLOB) && suf[l] =='!') ||
	    ((EFOutputFlags & EF_TRIMLOCAL) && suf[l] == '#'))
	      suf[l] = '\0';
	if (esFormat == SU)
		fprintf(outf, "S_");
	fprintf(outf, "%s", suf);
    }
    else {
    	he = EFHNConcatLook(prefix, suffix, "substrate");
    	if (he == NULL)
    	{
		fprintf(outf, "errGnd!");
		return 0;
    	}
    	/* Canonical name */
	nn = (const EFNodeName *) HashGetValue(he);
	subnode = nn->efnn_node;
	if (esFormat == SU) {
	  if (doAP) {
	    if (fetInfo[type].resClassSub < 0) {
	     TxError("Error: subap for devtype %d required but not "
			"specified on command line\n", type);
	     fprintf(outf,"A_0,P_0,");
	    }
	    else {
		simnAP(subnode, fetInfo[type].resClassSub, scale, outf);
		putc(',', outf);
	    }
	  }
	  fprintf(outf, "S_");
	}
    	EFHNOut(nn->efnn_node->efnode_name->efnn_hier, outf);
   }
   return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * simnAP, simnAPHier --
 *
 * Output the area perimeter of the node with type type if it has not
 * been visited.
 * The simnAPHier version outputs the area and perimeter only within the
 * local subcell with hierarchical name hierName.
 *
 * Side effects:
 *     Set the visited flags so that the node A/P will not be output multiple
 *     times
 *
 * ----------------------------------------------------------------------------
 */

bool
simnAP(
    EFNode *node,
    int resClass,
    float scale,
    FILE *outf)
{
	int a, p;

	if (node->efnode_client == PTR2CD(NULL))
		initNodeClient(node);
	if (resClass == NO_RESCLASS ||
	     beenVisited((nodeClient *)node->efnode_client, resClass)) {
		fprintf(outf,"A_0,P_0");
		return FALSE;
	}
	markVisited((nodeClient *)node->efnode_client, resClass);
	a = (int)(node->efnode_pa[resClass].pa_area*scale*scale);
	p = (int)(node->efnode_pa[resClass].pa_perim*scale);
	if (a < 0) a = 0;
	if (p < 0) p = 0;
	fprintf(outf,"A_%d,P_%d", a, p);
	return TRUE;
}

bool
simnAPHier(
    const DevTerm *dterm,
    const HierName *hierName,
    int resClass,
    float scale,
    FILE *outf)
{
	EFNode *node = dterm->dterm_node;
	nodeClientHier *nc;
	int a, p;

	if (node->efnode_client == PTR2CD(NULL))
		initNodeClientHier(node);
	nc = (nodeClientHier *)node->efnode_client;
	if (nc->lastPrefix != hierName) {
		TTMaskZero(&(nc->visitMask));
		nc->lastPrefix = hierName;
	}
	if (resClass == NO_RESCLASS ||
	    beenVisited((nodeClientHier *)node->efnode_client, resClass)) {
		fprintf(outf,"A_0,P_0");
		return FALSE;
	}
	markVisited((nodeClientHier *)node->efnode_client, resClass);
	a = (int)(node->efnode_pa[resClass].pa_area*scale*scale);
	p = (int)(node->efnode_pa[resClass].pa_perim*scale);
	if (a < 0) a = 0;
	if (p < 0) p = 0;
	fprintf(outf,"A_%d,P_%d", a, p);
	return TRUE;
}


/*
 * ----------------------------------------------------------------------------
 *
 * simdevOutNode --
 *
 * Output the name of the node whose hierarchical prefix down to this
 * point is 'prefix' and whose name from the end of suffix down to the
 * leaves is 'suffix', just as in the arguments to EFHNConcat().
 *
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes to the file 'outf'.
 *	Sets the efnode_client field as described above.
 *
 * ----------------------------------------------------------------------------
 */

int
simdevOutNode(
    const HierName *prefix,
    const HierName *suffix,
    const char *name,
    FILE *outf)
{
    HashEntry *he;
    const EFNodeName *nn;

    he = EFHNConcatLook(prefix, suffix, name);
    if (he == NULL)
    {
	fprintf(outf, " GND");
	return 0;
    }

    /* Canonical name */
    nn = (const EFNodeName *) HashGetValue(he);
    (void) putc(' ', outf);
    EFHNOut(nn->efnn_node->efnode_name->efnn_hier, outf);
    if (nn->efnn_node->efnode_client == PTR2CD(NULL))
	initNodeClient(nn->efnn_node);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * simcapVisit --
 *
 * Procedure to output a single capacitor to the .sim file.
 * Called by EFVisitCaps().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSimF.
 *
 * Format of a .sim cap line:
 *
 *	C node1 node2 cap
 *
 * where
 *	node1, node2 are the terminals of the capacitor
 *	cap is the capacitance in femtofarads (NOT attofarads).
 *
 * ----------------------------------------------------------------------------
 */

/* ARGSUSED */
/* @typedef cb_extflat_visitcaps_t (UNUSED) */
int
simcapVisit(
    const HierName *hierName1,
    const HierName *hierName2,
    double cap,
    ClientData cdata)	/* unused */
{
    cap = cap / 1000;
    if (cap <= EFCapThreshold)
	return 0;

    fprintf(esSimF, "C ");
    EFHNOut(hierName1, esSimF);
    fprintf(esSimF, " ");
    EFHNOut(hierName2, esSimF);
    fprintf(esSimF, " %.1lf\n", cap);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * simresistVisit --
 *
 * Procedure to output a single resistor to the .sim file.
 * Called by EFVisitResists().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSimF.
 *
 * Format of a .sim resistor line:
 *
 *	r node1 node2 res
 *
 * where
 *	node1, node2 are the terminals of the resistor
 *	res is the resistance in ohms (NOT milliohms)
 *
 *
 * ----------------------------------------------------------------------------
 */

/* ARGSUSED */
/* @typedef cb_extflat_visitresists_t (UNUSED) */
int
simresistVisit(
    const HierName *hierName1,
    const HierName *hierName2,
    float res,
    ClientData cdata)	/* unused */
{
    fprintf(esSimF, "r ");
    EFHNOut(hierName1, esSimF);
    fprintf(esSimF, " ");
    EFHNOut(hierName2, esSimF);
    fprintf(esSimF, " %g\n", res / 1000.);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * simnodeVisit --
 *
 * Procedure to output a single node to the .sim file, along with
 * its aliases to the .al file and its location to the .nodes file.
 * Called by EFVisitNodes().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the files esSimF, esAliasF, and esLabF.
 *
 * ----------------------------------------------------------------------------
 */

/* ARGSUSED */
/* @typedef cb_extflat_visitnodes_t (UNUSED) */
int
simnodeVisit(
    EFNode *node,
    int res,
    double cap,
    ClientData cdata) /* unused */
{
    const EFNodeName *nn;
    const HierName *hierName;
    bool isGlob;
    const char *fmt;
    const EFAttr *ap;

    if (esDevNodesOnly && node->efnode_client == PTR2CD(NULL))
	return 0;

    hierName = node->efnode_name->efnn_hier;
    cap = cap  / 1000;
    res = (res + 500) / 1000;
    if (cap > EFCapThreshold)
    {
	fprintf(esSimF, "C ");
	EFHNOut(hierName, esSimF);
	fprintf(esSimF, "%s ", esSpiceCapNode);
	fprintf(esSimF, "%.1f\n", cap);
    }
    if (res > EFResistThreshold)
    {
	fprintf(esSimF, "R ");
	EFHNOut(hierName, esSimF);
	fprintf(esSimF, " %d\n", res);
    }
    if (node->efnode_attrs && !esNoAttrs)
    {
	fprintf(esSimF, "A ");
	EFHNOut(hierName, esSimF);
	for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
	{
	    fprintf(esSimF, fmt, ap->efa_text);
	    fmt = ",%s";
	}
	putc('\n', esSimF);
    }

    /* Write aliases.  If the "ext2sim alias on" option was issued, then
     * write to the alias file only (<name>.al).  Otherwise write to the
     * <name>.sim file.
     */
    isGlob = EFHNIsGlob(hierName);
    for (nn = node->efnode_name->efnn_next; nn; nn = nn->efnn_next)
    {
	if (isGlob && EFHNIsGlob(nn->efnn_hier))
	    continue;

	if (esAliasF)
	{
	    fprintf(esAliasF, "= ");
	    EFHNOut(hierName, esAliasF);
	    fprintf(esAliasF, " ");
	    EFHNOut(nn->efnn_hier, esAliasF);
	    fprintf(esAliasF, "\n");
	}
	else
	{
	    fprintf(esSimF, "= ");
	    EFHNOut(hierName, esSimF);
	    fprintf(esSimF, " ");
	    EFHNOut(nn->efnn_hier, esSimF);
	    fprintf(esSimF, "\n");
	}
    }

    if (esLabF)
    {
	EFHNOut(hierName, esLabF);
	fprintf(esLabF, " %d %d %s\n",
		    node->efnode_loc.r_xbot, node->efnode_loc.r_ybot,
		    EFLayerNames[node->efnode_type]);
    }

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * simmkDevMerge --
 * Create a new devMerge structure.
 *
 * Results:
 *    Obvious
 *
 * Side effects:
 *	Allocates memory and sets the fields of the structure.
 *
 * ----------------------------------------------------------------------------
 */
devMerge *
simmkDevMerge(
    int l,
    int w,
    const EFNode *g,
    const EFNode *s,
    const EFNode *d,
    const EFNode *b,
    const HierName *hn,
    Dev *dev)
{
	devMerge *fp;

	fp = (devMerge *) mallocMagic((unsigned) (sizeof(devMerge)));
	fp->l = l; fp->w = w;
	fp->g = g; fp->s = s;
	fp->d = d; fp->b = b;
	fp->dev = dev;
	fp->esFMIndex = esFMIndex;
	fp->hierName = hn;
	fp->next = NULL;
	addDevMult(1.0);

	return fp;
}

/*
 * ----------------------------------------------------------------------------
 *
 * parallelDevs --
 * Macro to look if two devs are in parallel
 *
 * Results:
 *    NOT_PARALLEL  if not in parallel
 *    PARALLEL      if s==s d==d and g==g and bulk=bulk
 *    FLIP_PARALLEL if s==d d==s --->>----------------
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */
#define	NOT_PARALLEL	0
#define	PARALLEL	1
#define	PARALLEL_R	2

#define parallelDevs(f1, f2) \
( \
	((f1)->g == (f2)->g && (f1)->b == (f2)->b && (f1)->l == (f2)->l && \
	  (esMergeDevsA || (f1)->w == (f2)->w))  ? \
	   (((f1)->d == (f2)->d && (f1)->s == (f2)->s) ? \
	       PARALLEL : \
	      (((f1)->s == (f2)->d && (f1)->d == (f2)->s) ? PARALLEL_R : NOT_PARALLEL)) \
	  : NOT_PARALLEL  \
)

/*
 * ----------------------------------------------------------------------------
 *
 * simmergeVisit --
 * Visits each dev throu EFVisitDevs and finds if it is in parallel with
 * any previously visited dev.
 *
 * Results:
 *  0 always to keep the caller going.
 *
 * Side effects:
 *  Numerous.
 *
 * ----------------------------------------------------------------------------
 */

/* ARGSUSED */
/* @typedef cb_extflat_visitdevs_t (UNUSED) */
int
simmergeVisit(
    Dev *dev,		/* Dev to examine */
    HierContext *hc,	/* Hierarchical context down to this dev */
    float scale,	/* Scale transform */
    Transform *trans,	/* Coordinate transform (not used) */
    ClientData cdata)	/* unused */
{
	const DevTerm *gate, *source, *drain;
	const EFNode *subnode, *snode, *dnode, *gnode;
	int      pmode, l, w;
	float	 m;
	devMerge *fp;
	const devMerge *cfp;
	const HierName *hierName = hc->hc_hierName;

	if (dev->dev_nterm < 2) {
		TxError("outPremature\n");
		return 0;
	}

	gate = &dev->dev_terms[0];
	source = drain = &dev->dev_terms[1];
	if (dev->dev_nterm >= 3)
		drain = &dev->dev_terms[2];
	subnode = dev->dev_subsnode;

	gnode = SimGetNode (hierName, gate->dterm_node->efnode_name->efnn_hier);
	snode = SimGetNode (hierName, source->dterm_node->efnode_name->efnn_hier);
	dnode = SimGetNode (hierName, drain->dterm_node->efnode_name->efnn_hier);

	EFGetLengthAndWidth(dev, &l, &w);
	fp = simmkDevMerge((int)(l*scale), (int)(w*scale), gnode, snode,
			dnode, subnode, hierName, dev);

	/*
	 * run the list of devs. compare the current one with
	 * each one in the list. if they fullfill the matching requirements
	 * merge them.
	 */

	for (cfp = devMergeList; cfp != NULL; cfp = cfp->next) {
	  if ((pmode = parallelDevs(fp, cfp)) != NOT_PARALLEL) {
#if 0 /* -Wunused-but-set-variable cf, cs, cg */
		Dev *cf = cfp->dev;
		DevTerm *cg = &cfp->dev->dev_terms[0];
		DevTerm *cs = &cfp->dev->dev_terms[1];
		DevTerm *cd = cs;
		if (cfp->dev->dev_nterm >= 3) {
			if (pmode == PARALLEL)
				cd = &cfp->dev->dev_terms[2];
			else if (pmode == PARALLEL_R)
				cs = &cfp->dev->dev_terms[2];
		}
#endif
		m = esFMult[cfp->esFMIndex] + ((float)fp->w/(float)cfp->w);
		setDevMult(fp->esFMIndex, DEV_KILLED);
		setDevMult(cfp->esFMIndex, m);
		esDevsMerged++;
		freeMagic((char *)fp);
		return 0;
	  }
	}
	/* No parallel devs to it yet */
	fp->next = devMergeList;
	devMergeList = fp;
	return 0;
}

