1862 lines
49 KiB
C
1862 lines
49 KiB
C
/*
|
|
* 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 "extflat/extflat.h"
|
|
#include "extflat/EFint.h"
|
|
#include "extract/extract.h" /* for extDevTable */
|
|
#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;
|
|
for (p = devMergeList; p != NULL; p = p->next)
|
|
freeMagic((char *)p);
|
|
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;
|
|
for (p = devMergeList; p != NULL; p = p->next) freeMagic((char *)p);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|