magic/ext2sim/ext2sim.c

1854 lines
49 KiB
C
Raw Normal View History

/*
* 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
2025-07-18 13:46:39 +02:00
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);
2025-07-24 15:04:56 +02:00
bool simnAPHier(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 */
2025-07-24 15:04:56 +02:00
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;
2025-07-24 15:20:12 +02:00
extern const char esSpiceDefaultGnd[];
extern const char *esSpiceCapNode;
#endif
bool esDoSimExtResis = FALSE;
bool esNoAlias = TRUE;
bool esNoLabel = TRUE;
char simesDefaultOut[FNSIZE];
2025-07-24 15:20:12 +02:00
const char *simesOutName = simesDefaultOut;
char esDefaultAlias[FNSIZE], esDefaultLabel[FNSIZE];
2025-07-24 15:20:12 +02:00
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 */
char *defSubs ; /* The default substrate node */
} fetInfo[TT_MAXTYPES];
typedef struct {
TileTypeBitMask visitMask;
} nodeClient;
typedef struct {
2025-07-24 15:04:56 +02:00
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 = (ClientData) mallocMagic((unsigned) (sizeof(nodeClient))); \
TTMaskZero(&(( nodeClient *)(node)->efnode_client)->visitMask); \
}
#define initNodeClientHier(node) \
{ \
(node)->efnode_client = (ClientData) mallocMagic((unsigned) (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;
EFNode *g, *s, *d, *b;
Dev * dev;
int esFMIndex;
2025-07-24 15:04:56 +02:00
const HierName *hierName;
struct _devMerge *next;
} devMerge;
#ifdef EXT2SIM_AUTO
devMerge *devMergeList = NULL ;
#else
extern 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;
2024-10-21 10:19:15 +02:00
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;
char *inName;
FILE *f;
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;
char *devname;
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)
{
2025-07-24 15:04:56 +02:00
freeMagic((char *) EFArgTech);
EFArgTech = NULL;
}
if (EFSearchPath)
{
2025-07-24 15:04:56 +02:00
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)
{
devMerge *p;
EFVisitDevs(simmergeVisit, PTR2CD(NULL));
TxPrintf("Devices merged: %d\n", esDevsMerged);
esFMIndex = 0;
for (p = devMergeList; p != NULL; p = p->next)
freeMagic(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;
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) {
devMerge *p;
EFVisitDevs(simmergeVisit, PTR2CD(NULL));
TxPrintf("Devices merged: %d\n", esDevsMerged);
esFMIndex = 0;
for (p = devMergeList; p != NULL; p = p->next) freeMagic(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, *cp;
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': {
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': {
char *t;
if (( t = ArgStr(&argc, &argv, "cap-accuracy") ) == NULL)
goto usage;
TxPrintf("Cap accuracy option -y is deprecated.\n");
break;
}
case 'J':
{
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(
2025-07-24 15:04:56 +02:00
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 */
{
DevTerm *gate, *source, *drain, *term;
EFNode *subnode, *snode, *dnode;
int l, w;
Rect r;
char name[12];
bool is_subckt = FALSE;
2025-07-24 15:04:56 +02:00
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_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;
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(
2025-07-24 15:04:56 +02:00
const HierName *prefix,
const HierName *suffix,
int type,
float scale,
bool doAP,
FILE *outf)
{
HashEntry *he;
EFNodeName *nn;
2025-07-24 15:04:56 +02:00
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 = (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 == (ClientData) 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(
DevTerm *dterm,
2025-07-24 15:04:56 +02:00
const HierName *hierName,
int resClass,
float scale,
FILE *outf)
{
EFNode *node = dterm->dterm_node;
nodeClientHier *nc ;
int a, p;
if ( node->efnode_client == (ClientData) 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(
2025-07-24 15:04:56 +02:00
const HierName *prefix,
const HierName *suffix,
const char *name,
FILE *outf)
{
HashEntry *he;
EFNodeName *nn;
he = EFHNConcatLook(prefix, suffix, name);
if (he == NULL)
{
fprintf(outf, " GND");
return 0;
}
/* Canonical name */
nn = (EFNodeName *) HashGetValue(he);
(void) putc(' ', outf);
EFHNOut(nn->efnn_node->efnode_name->efnn_hier, outf);
if ( nn->efnn_node->efnode_client == (ClientData) 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) /* 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 */
{
EFNodeName *nn;
HierName *hierName;
bool isGlob;
const char *fmt;
EFAttr *ap;
if (esDevNodesOnly && node->efnode_client == (ClientData) NULL)
return 0;
hierName = (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,
EFNode *g,
EFNode *s,
EFNode *d,
EFNode *b,
2025-07-24 15:04:56 +02:00
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 */
{
DevTerm *gate, *source, *drain;
Dev *cf;
DevTerm *cg, *cs, *cd;
EFNode *subnode, *snode, *dnode, *gnode;
int pmode, l, w;
float m;
devMerge *fp, *cfp;
2025-07-24 15:04:56 +02:00
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) {
cf = cfp->dev;
cg = &cfp->dev->dev_terms[0];
cs = cd = &cfp->dev->dev_terms[1];
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];
}
m = esFMult[cfp->esFMIndex] + ((float)fp->w/(float)cfp->w);
setDevMult(fp->esFMIndex, DEV_KILLED);
setDevMult(cfp->esFMIndex, m);
esDevsMerged++;
freeMagic(fp);
return 0;
}
}
/* No parallel devs to it yet */
fp->next = devMergeList;
devMergeList = fp;
return 0;
}