2017-04-25 14:41:48 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* ext2spice.c --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Program to flatten hierarchical .ext files and produce
|
|
|
|
|
|
* a .spice file, suitable for use as input to simulators
|
|
|
|
|
|
* such as spice and hspice.
|
|
|
|
|
|
*
|
|
|
|
|
|
* 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.spice, unless '-o esSpiceFile' is specified, in which case the
|
|
|
|
|
|
* output is left in 'esSpiceFile'.
|
|
|
|
|
|
*
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/ext2spice/ext2spice.c,v 1.8 2010/08/25 17:33:56 tim Exp $";
|
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
#include <stdlib.h> /* for atof() */
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
|
#include "utils/dqueue.h"
|
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
#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"
|
|
|
|
|
|
#endif
|
|
|
|
|
|
#include "extflat/extflat.h"
|
|
|
|
|
|
#include "extflat/EFint.h"
|
|
|
|
|
|
#include "extract/extract.h" /* for extDevTable */
|
|
|
|
|
|
#include "utils/runstats.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "ext2spice/ext2spice.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* Options specific to ext2spice */
|
|
|
|
|
|
bool esDoExtResis = FALSE;
|
|
|
|
|
|
bool esDoPorts = TRUE;
|
|
|
|
|
|
bool esDoHierarchy = FALSE;
|
2017-08-02 04:14:42 +02:00
|
|
|
|
bool esDoBlackBox = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
bool esDoRenumber = FALSE;
|
|
|
|
|
|
bool esDoResistorTee = FALSE;
|
|
|
|
|
|
int esDoSubckt = AUTO;
|
|
|
|
|
|
bool esDevNodesOnly = FALSE;
|
|
|
|
|
|
bool esMergeNames = TRUE;
|
|
|
|
|
|
bool esNoAttrs = FALSE;
|
|
|
|
|
|
bool esHierAP = FALSE;
|
|
|
|
|
|
char spcesDefaultOut[FNSIZE];
|
|
|
|
|
|
int esCapAccuracy = 2;
|
|
|
|
|
|
char esSpiceCapFormat[FNSIZE];
|
|
|
|
|
|
char *spcesOutName = spcesDefaultOut;
|
|
|
|
|
|
FILE *esSpiceF = NULL;
|
|
|
|
|
|
float esScale = -1.0 ; /* negative if hspice the EFScale/100 otherwise */
|
|
|
|
|
|
|
|
|
|
|
|
unsigned short esFormat = SPICE3 ;
|
|
|
|
|
|
|
|
|
|
|
|
int esCapNum, esDevNum, esResNum, esDiodeNum;
|
|
|
|
|
|
int esNodeNum; /* just in case we're extracting spice2 */
|
|
|
|
|
|
int esSbckNum; /* used in hspice node name shortening */
|
|
|
|
|
|
int esNoModelType; /* index for device type "None" (model-less device) */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* The following hash table and associated functions are used only if
|
|
|
|
|
|
* the format is hspice, to keep the translation between the hierarchical
|
|
|
|
|
|
* prefix of a node and the x num that we use to output valid hspice
|
|
|
|
|
|
* which also are meaningful.
|
|
|
|
|
|
*/
|
|
|
|
|
|
HashTable subcktNameTable ; /* the hash table itself */
|
|
|
|
|
|
DQueue subcktNameQueue ; /* q used to print it sorted at the end*/
|
|
|
|
|
|
|
|
|
|
|
|
fetInfoList esFetInfo[MAXDEVTYPES];
|
|
|
|
|
|
|
|
|
|
|
|
/* Record for keeping a list of global names */
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct GLL *globalListPtr;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct GLL {
|
|
|
|
|
|
globalListPtr gll_next;
|
|
|
|
|
|
char *gll_name;
|
|
|
|
|
|
} globalList;
|
|
|
|
|
|
|
|
|
|
|
|
unsigned long initMask = 0;
|
|
|
|
|
|
|
|
|
|
|
|
bool esMergeDevsA = FALSE; /* aggressive merging of devs L1=L2 merge them */
|
|
|
|
|
|
bool esMergeDevsC = FALSE; /* conservative merging of devs L1=L2 and W1=W2 */
|
|
|
|
|
|
/* used with the hspice multiplier */
|
|
|
|
|
|
bool esDistrJunct = FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
*---------------------------------------------------------
|
|
|
|
|
|
* Variables & macros used for merging parallel devs
|
|
|
|
|
|
* The merging of devs is based on the fact that spcdevVisit
|
|
|
|
|
|
* visits the devs in the same order all the time so the
|
|
|
|
|
|
* value of esFMult[i] keeps the multiplier for the ith dev
|
|
|
|
|
|
*---------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
float *esFMult = NULL; /* the array itself */
|
|
|
|
|
|
int esFMIndex = 0; /* current index to it */
|
|
|
|
|
|
int esFMSize = FMULT_SIZE ; /* its current size (growable) */
|
|
|
|
|
|
int esSpiceDevsMerged;
|
|
|
|
|
|
|
|
|
|
|
|
devMerge *devMergeList = NULL ;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* Apply modifications to a global node name and output to file "outf"
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Output to file
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
esFormatSubs(outf, suf)
|
|
|
|
|
|
FILE *outf;
|
|
|
|
|
|
char *suf;
|
|
|
|
|
|
{
|
|
|
|
|
|
char *specchar;
|
|
|
|
|
|
int l;
|
|
|
|
|
|
|
|
|
|
|
|
if (outf)
|
|
|
|
|
|
{
|
|
|
|
|
|
l = strlen(suf) - 1;
|
|
|
|
|
|
if ((EFTrimFlags & EF_TRIMGLOB ) && suf[l] == '!' ||
|
|
|
|
|
|
(EFTrimFlags & EF_TRIMLOCAL) && suf[l] == '#')
|
|
|
|
|
|
suf[l] = '\0' ;
|
2019-10-15 03:46:07 +02:00
|
|
|
|
if (EFTrimFlags & EF_CONVERTCOMMA)
|
2017-04-25 14:41:48 +02:00
|
|
|
|
while ((specchar = strchr(suf, ',')) != NULL)
|
2019-10-15 03:46:07 +02:00
|
|
|
|
*specchar = '|';
|
|
|
|
|
|
if (EFTrimFlags & EF_CONVERTBRACKETS)
|
|
|
|
|
|
{
|
|
|
|
|
|
while ((specchar = strchr(suf, '[')) != NULL)
|
|
|
|
|
|
*specchar = '_';
|
|
|
|
|
|
while ((specchar = strchr(suf, ']')) != NULL)
|
|
|
|
|
|
*specchar = '_';
|
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
if (EFTrimFlags & EF_CONVERTEQUAL)
|
|
|
|
|
|
while ((specchar = strchr(suf, '=')) != NULL)
|
|
|
|
|
|
*specchar = ':';
|
|
|
|
|
|
fprintf(outf, "%s", suf);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef EXT2SPICE_AUTO
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* Tcl package initialization function
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
Exttospice_Init(interp)
|
|
|
|
|
|
Tcl_Interp *interp;
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Sanity checks! */
|
|
|
|
|
|
if (interp == NULL) return TCL_ERROR;
|
|
|
|
|
|
if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL)
|
|
|
|
|
|
return TCL_ERROR;
|
2017-10-13 16:28:07 +02:00
|
|
|
|
if (Tcl_InitStubs(interp, "8.5", 0) == NULL) return TCL_ERROR;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
TxPrintf("Auto-loading EXTTOSPICE module\n");
|
|
|
|
|
|
TxFlushOut();
|
|
|
|
|
|
|
|
|
|
|
|
/* Replace the auto-load function with the one defined in */
|
|
|
|
|
|
/* this package in the command functions list. */
|
|
|
|
|
|
|
|
|
|
|
|
if (WindReplaceCommand(DBWclientID, "exttospice", CmdExtToSpice) < 0)
|
|
|
|
|
|
return TCL_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
/* Add "ext2spice" as an alias for "exttospice" */
|
|
|
|
|
|
if (WindReplaceCommand(DBWclientID, "ext2spice", CmdExtToSpice) < 0)
|
|
|
|
|
|
return TCL_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
Tcl_PkgProvide(interp, "Exttospice", MAGIC_VERSION);
|
|
|
|
|
|
return TCL_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif /* EXT2SPICE_AUTO */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* Main callback for command "magic::exttospice"
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define EXTTOSPC_RUN 0
|
|
|
|
|
|
#define EXTTOSPC_DEFAULT 1
|
|
|
|
|
|
#define EXTTOSPC_FORMAT 2
|
|
|
|
|
|
#define EXTTOSPC_RTHRESH 3
|
|
|
|
|
|
#define EXTTOSPC_CTHRESH 4
|
|
|
|
|
|
#define EXTTOSPC_MERGE 5
|
|
|
|
|
|
#define EXTTOSPC_EXTRESIST 6
|
|
|
|
|
|
#define EXTTOSPC_RESISTORTEE 7
|
|
|
|
|
|
#define EXTTOSPC_SCALE 8
|
|
|
|
|
|
#define EXTTOSPC_SUBCIRCUITS 9
|
|
|
|
|
|
#define EXTTOSPC_HIERARCHY 10
|
2017-08-02 04:14:42 +02:00
|
|
|
|
#define EXTTOSPC_BLACKBOX 11
|
|
|
|
|
|
#define EXTTOSPC_RENUMBER 12
|
|
|
|
|
|
#define EXTTOSPC_MERGENAMES 13
|
2018-10-31 19:33:24 +01:00
|
|
|
|
#define EXTTOSPC_LVS 14
|
|
|
|
|
|
#define EXTTOSPC_HELP 15
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
CmdExtToSpice(w, cmd)
|
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
|
{
|
|
|
|
|
|
int i,flatFlags;
|
|
|
|
|
|
char *inName;
|
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
|
|
int value;
|
|
|
|
|
|
int option = EXTTOSPC_RUN;
|
|
|
|
|
|
int argc = cmd->tx_argc;
|
|
|
|
|
|
char **argv = cmd->tx_argv;
|
|
|
|
|
|
char **msg;
|
|
|
|
|
|
char *resstr = NULL;
|
|
|
|
|
|
char *substr = NULL;
|
|
|
|
|
|
bool err_result, locDoSubckt;
|
|
|
|
|
|
|
|
|
|
|
|
short sd_rclass;
|
|
|
|
|
|
short sub_rclass;
|
|
|
|
|
|
char *devname;
|
|
|
|
|
|
char *subname;
|
|
|
|
|
|
int idx, idx2;
|
|
|
|
|
|
globalList *glist = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
static EFCapValue LocCapThreshold = 2;
|
|
|
|
|
|
static int LocResistThreshold = INFINITE_THRESHOLD;
|
|
|
|
|
|
|
|
|
|
|
|
static char *spiceFormats[] = {
|
|
|
|
|
|
"SPICE2", "SPICE3", "HSPICE", "NGSPICE", NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static char *cmdExtToSpcOption[] = {
|
|
|
|
|
|
"[run] [options] run exttospice on current cell\n"
|
|
|
|
|
|
" use \"run -help\" to get standard options",
|
|
|
|
|
|
"default reset to default values",
|
|
|
|
|
|
"format [<type>] set output format",
|
|
|
|
|
|
"rthresh [<value>] set resistance threshold value",
|
|
|
|
|
|
"cthresh [<value>] set capacitance threshold value",
|
|
|
|
|
|
"merge [<type>] merge parallel transistors",
|
|
|
|
|
|
"extresist [on|off] incorporate information from extresist",
|
|
|
|
|
|
"resistor tee [on|off] model resistor capacitance as a T-network",
|
|
|
|
|
|
"scale [on|off] use .option card for scaling",
|
2019-10-23 15:33:37 +02:00
|
|
|
|
"subcircuits [top|descend] [on|off|auto]\n"
|
|
|
|
|
|
" standard cells become subcircuit calls",
|
2017-04-25 14:41:48 +02:00
|
|
|
|
"hierarchy [on|off] output hierarchical spice for LVS",
|
2017-08-02 04:14:42 +02:00
|
|
|
|
"blackbox [on|off] output abstract views as black-box entries",
|
2017-04-25 14:41:48 +02:00
|
|
|
|
"renumber [on|off] on = number instances X1, X2, etc.\n"
|
|
|
|
|
|
" off = keep instance ID names",
|
|
|
|
|
|
"global [on|off] on = merge unconnected global nets by name",
|
2018-10-31 19:33:24 +01:00
|
|
|
|
"lvs apply typical default settings for LVS",
|
2017-04-25 14:41:48 +02:00
|
|
|
|
"help print help information",
|
|
|
|
|
|
NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static char *cmdMergeTypes[] = {
|
|
|
|
|
|
"none don't merge parallel devices",
|
|
|
|
|
|
"conservative merge devices with same L, W",
|
|
|
|
|
|
"aggressive merge devices with same L",
|
|
|
|
|
|
NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static char *cmdExtToSpcFormat[] = {
|
|
|
|
|
|
"spice2",
|
|
|
|
|
|
"spice3",
|
|
|
|
|
|
"hspice",
|
|
|
|
|
|
"ngspice",
|
|
|
|
|
|
NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static char *yesno[] = {
|
|
|
|
|
|
"yes",
|
|
|
|
|
|
"true",
|
|
|
|
|
|
"on",
|
|
|
|
|
|
"no",
|
|
|
|
|
|
"false",
|
|
|
|
|
|
"off",
|
|
|
|
|
|
NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static char *subcktopts[] = {
|
|
|
|
|
|
"yes",
|
|
|
|
|
|
"true",
|
|
|
|
|
|
"on",
|
|
|
|
|
|
"no",
|
|
|
|
|
|
"false",
|
|
|
|
|
|
"off",
|
|
|
|
|
|
"automatic",
|
|
|
|
|
|
"top",
|
|
|
|
|
|
"descend",
|
|
|
|
|
|
NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
|
IDX_YES, IDX_TRUE, IDX_ON, IDX_NO, IDX_FALSE, IDX_OFF,
|
|
|
|
|
|
IDX_AUTO, IDX_TOP, IDX_DESCEND
|
|
|
|
|
|
} yesnoType;
|
|
|
|
|
|
|
|
|
|
|
|
esNoModelType = -1;
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc > 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
option = Lookup(cmd->tx_argv[1], cmdExtToSpcOption);
|
|
|
|
|
|
if (option < 0) option = EXTTOSPC_RUN;
|
|
|
|
|
|
else argv++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (option)
|
|
|
|
|
|
{
|
|
|
|
|
|
case EXTTOSPC_EXTRESIST:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esDoExtResis) ? "on" : "off", NULL);
|
|
|
|
|
|
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) esDoExtResis = TRUE;
|
|
|
|
|
|
else esDoExtResis = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_RESISTORTEE:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esDoResistorTee) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (cmd->tx_argc != 4)
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
idx = Lookup(cmd->tx_argv[3], yesno);
|
|
|
|
|
|
if (idx < 0) goto usage;
|
|
|
|
|
|
else if (idx < 3) esDoResistorTee = TRUE;
|
|
|
|
|
|
else esDoResistorTee = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_SCALE:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esScale < 0) ? "on" : "off", NULL);
|
|
|
|
|
|
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) esScale = -1.0;
|
|
|
|
|
|
else esScale = 0.0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_HIERARCHY:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esDoHierarchy) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], yesno);
|
|
|
|
|
|
if (idx < 0) goto usage;
|
|
|
|
|
|
else if (idx < 3) /* yes */
|
|
|
|
|
|
esDoHierarchy = TRUE;
|
|
|
|
|
|
else /* no */
|
|
|
|
|
|
esDoHierarchy = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
|
case EXTTOSPC_BLACKBOX:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esDoBlackBox) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], yesno);
|
|
|
|
|
|
if (idx < 0) goto usage;
|
|
|
|
|
|
else if (idx < 3) /* yes */
|
|
|
|
|
|
esDoBlackBox = TRUE;
|
|
|
|
|
|
else /* no */
|
|
|
|
|
|
esDoBlackBox = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
case EXTTOSPC_RENUMBER:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esDoRenumber) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], yesno);
|
|
|
|
|
|
if (idx < 0) goto usage;
|
|
|
|
|
|
else if (idx < 3) /* yes */
|
|
|
|
|
|
esDoRenumber = TRUE;
|
|
|
|
|
|
else /* no */
|
|
|
|
|
|
esDoRenumber = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_MERGENAMES:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esMergeNames) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], yesno);
|
|
|
|
|
|
if (idx < 0) goto usage;
|
|
|
|
|
|
else if (idx < 3) /* yes */
|
|
|
|
|
|
esMergeNames = TRUE;
|
|
|
|
|
|
else /* no */
|
|
|
|
|
|
esMergeNames = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2018-10-31 19:33:24 +01:00
|
|
|
|
case EXTTOSPC_LVS:
|
|
|
|
|
|
/* Apply default command settings for LVS */
|
|
|
|
|
|
/* hierarchy = on */
|
|
|
|
|
|
/* format = ngspice */
|
|
|
|
|
|
/* cthresh = infinite */
|
|
|
|
|
|
/* rthresh = infinite */
|
|
|
|
|
|
/* renumber = off */
|
|
|
|
|
|
/* scale = off */
|
|
|
|
|
|
/* blackbox = on */
|
2018-10-31 20:41:22 +01:00
|
|
|
|
/* global = off */
|
2018-10-31 19:33:24 +01:00
|
|
|
|
/* subcircuit top = auto */
|
|
|
|
|
|
|
|
|
|
|
|
esDoHierarchy = TRUE;
|
|
|
|
|
|
esFormat = NGSPICE;
|
|
|
|
|
|
LocCapThreshold = (EFCapValue)INFINITE_THRESHOLD_F;
|
|
|
|
|
|
LocResistThreshold = INFINITE_THRESHOLD;
|
|
|
|
|
|
esDoRenumber = FALSE;
|
|
|
|
|
|
esScale = 0.0;
|
|
|
|
|
|
esDoBlackBox = TRUE;
|
2018-10-31 20:41:22 +01:00
|
|
|
|
esMergeNames = FALSE;
|
2018-10-31 19:33:24 +01:00
|
|
|
|
esDoSubckt = 2;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
case EXTTOSPC_SUBCIRCUITS:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esDoPorts) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], subcktopts);
|
|
|
|
|
|
switch (idx) {
|
|
|
|
|
|
case IDX_YES: case IDX_TRUE: case IDX_ON:
|
|
|
|
|
|
esDoPorts = TRUE;
|
|
|
|
|
|
return;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case IDX_NO: case IDX_FALSE: case IDX_OFF:
|
|
|
|
|
|
esDoPorts = FALSE;
|
|
|
|
|
|
return;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case IDX_DESCEND:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, (esDoPorts) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case IDX_TOP:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp,
|
|
|
|
|
|
(esDoSubckt == 2) ? "auto" :
|
|
|
|
|
|
(esDoSubckt == 1) ? "on" : "off", NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc != 4) goto usage;
|
|
|
|
|
|
idx2 = Lookup(cmd->tx_argv[3], subcktopts);
|
|
|
|
|
|
switch (idx2) {
|
|
|
|
|
|
case IDX_YES: case IDX_TRUE: case IDX_ON:
|
|
|
|
|
|
if (idx == IDX_DESCEND)
|
|
|
|
|
|
esDoPorts = TRUE;
|
|
|
|
|
|
else
|
|
|
|
|
|
esDoSubckt = TRUE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case IDX_NO: case IDX_FALSE: case IDX_OFF:
|
|
|
|
|
|
if (idx == IDX_DESCEND)
|
|
|
|
|
|
esDoPorts = FALSE;
|
|
|
|
|
|
else
|
|
|
|
|
|
esDoSubckt = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case IDX_AUTO:
|
|
|
|
|
|
esDoSubckt = AUTO;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_FORMAT:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, cmdExtToSpcFormat[esFormat], NULL);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (cmd->tx_argc < 3) goto usage;
|
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], cmdExtToSpcFormat);
|
|
|
|
|
|
if (idx < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Tcl_SetResult(magicinterp, "Bad format type. Formats are:"
|
2018-10-31 19:33:24 +01:00
|
|
|
|
"spice2, spice3, hspice, and ngspice.", NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
esFormat = idx;
|
|
|
|
|
|
/* By default, use .option to declare scale in HSPICE mode */
|
|
|
|
|
|
if (esFormat == HSPICE) esScale = -1.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_CTHRESH:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!IS_FINITE_F(LocCapThreshold))
|
|
|
|
|
|
Tcl_SetResult(magicinterp, "infinite", NULL);
|
|
|
|
|
|
else
|
|
|
|
|
|
Tcl_SetObjResult(magicinterp,
|
|
|
|
|
|
Tcl_NewDoubleObj((double)LocCapThreshold));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (cmd->tx_argc < 3) goto usage;
|
|
|
|
|
|
|
|
|
|
|
|
/* Note that strtod() (called by StrIsNumeric()) accepts */
|
|
|
|
|
|
/* "infinite" as a valid numerical value; however, the */
|
|
|
|
|
|
/* conversion to C type INF is *not* INFINITE_THRESHOLD, so */
|
|
|
|
|
|
/* we need to check this case first. . . */
|
|
|
|
|
|
|
|
|
|
|
|
if (!strncmp(cmd->tx_argv[2], "inf", 3))
|
|
|
|
|
|
LocCapThreshold = (EFCapValue)INFINITE_THRESHOLD_F;
|
|
|
|
|
|
else if (StrIsNumeric(cmd->tx_argv[2]))
|
|
|
|
|
|
LocCapThreshold = atoCap(cmd->tx_argv[2]);
|
|
|
|
|
|
else
|
|
|
|
|
|
TxError("exttospice: numeric value or \"infinite\" expected.\n");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_RTHRESH:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (LocResistThreshold == INFINITE_THRESHOLD)
|
|
|
|
|
|
Tcl_SetResult(magicinterp, "infinite", NULL);
|
|
|
|
|
|
else
|
|
|
|
|
|
Tcl_SetObjResult(magicinterp,
|
|
|
|
|
|
Tcl_NewIntObj(LocResistThreshold));
|
|
|
|
|
|
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("exttospice: integer value or \"infinite\" expected.\n");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_MERGE:
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (esMergeDevsA)
|
|
|
|
|
|
Tcl_SetResult(magicinterp, "aggressive", NULL);
|
|
|
|
|
|
else if (esMergeDevsC)
|
|
|
|
|
|
Tcl_SetResult(magicinterp, "conservative", NULL);
|
|
|
|
|
|
else
|
|
|
|
|
|
Tcl_SetResult(magicinterp, "none", NULL);
|
|
|
|
|
|
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 EXTTOSPC_DEFAULT:
|
|
|
|
|
|
LocCapThreshold = 2;
|
|
|
|
|
|
LocResistThreshold = INFINITE_THRESHOLD;
|
2019-10-15 03:46:07 +02:00
|
|
|
|
EFTrimFlags = EF_CONVERTCOMMA | EF_CONVERTEQUAL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
EFScale = 0.0;
|
|
|
|
|
|
if (EFArgTech)
|
|
|
|
|
|
{
|
|
|
|
|
|
freeMagic(EFArgTech);
|
|
|
|
|
|
EFArgTech = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (EFSearchPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
freeMagic(EFSearchPath);
|
|
|
|
|
|
EFSearchPath = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_RUN:
|
|
|
|
|
|
goto runexttospice;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_HELP:
|
|
|
|
|
|
usage:
|
|
|
|
|
|
for (msg = &(cmdExtToSpcOption[0]); *msg != NULL; msg++)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxPrintf(" %s\n", *msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
runexttospice:
|
|
|
|
|
|
|
|
|
|
|
|
/* Reset the device indices */
|
|
|
|
|
|
esCapNum = 0;
|
|
|
|
|
|
esDevNum = 1000;
|
|
|
|
|
|
esResNum = 0;
|
|
|
|
|
|
esDiodeNum = 0;
|
|
|
|
|
|
esSbckNum = 0;
|
|
|
|
|
|
esNodeNum = 10; /* just in case we're extracting spice2 */
|
|
|
|
|
|
esFMIndex = 0;
|
|
|
|
|
|
esSpiceDevsMerged = 0;
|
|
|
|
|
|
esDevNodesOnly = FALSE; /* so using -F doesn't become permanent */
|
|
|
|
|
|
|
|
|
|
|
|
EFInit();
|
|
|
|
|
|
|
|
|
|
|
|
EFResistThreshold = LocResistThreshold;
|
|
|
|
|
|
EFCapThreshold = LocCapThreshold;
|
|
|
|
|
|
|
|
|
|
|
|
/* Process command line arguments */
|
|
|
|
|
|
|
|
|
|
|
|
inName = EFArgs(argc, argv, &err_result, spcmainArgs, (ClientData) NULL);
|
|
|
|
|
|
if (err_result == TRUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
EFDone();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ((inName = ((CellUse *) w->w_surfaceID)->cu_def->cd_name) == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("No cell present\n");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Initializations specific to this program.
|
|
|
|
|
|
* Make output name inName.spice if they weren't
|
|
|
|
|
|
* explicitly specified
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if (spcesOutName == spcesDefaultOut)
|
|
|
|
|
|
sprintf(spcesDefaultOut, "%s.spice", inName);
|
|
|
|
|
|
|
|
|
|
|
|
/* Read the hierarchical description of the input circuit */
|
|
|
|
|
|
if (EFReadFile(inName, TRUE, esDoExtResis, FALSE) == FALSE)
|
|
|
|
|
|
{
|
|
|
|
|
|
EFDone();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* If the .ext file was read without error, then open the output file */
|
|
|
|
|
|
|
|
|
|
|
|
if ((esSpiceF = fopen(spcesOutName, "w")) == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
char *tclres = Tcl_Alloc(128);
|
|
|
|
|
|
sprintf(tclres, "exttospice: Unable to open file %s for writing\n",
|
|
|
|
|
|
spcesOutName);
|
|
|
|
|
|
Tcl_SetResult(magicinterp, tclres, TCL_DYNAMIC);
|
|
|
|
|
|
EFDone();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (EFStyle == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("Warning: Current extraction style does not match .ext file!\n");
|
|
|
|
|
|
TxError("Area/Perimeter values and parasitic values will be zero.\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* create default devinfo entries (MOSIS) which can be overridden by
|
|
|
|
|
|
the command line arguments */
|
|
|
|
|
|
|
|
|
|
|
|
for ( i = 0 ; i < MAXDEVTYPES ; i++ ) {
|
|
|
|
|
|
esFetInfo[i].resClassSD = NO_RESCLASS;
|
|
|
|
|
|
esFetInfo[i].resClassSub = NO_RESCLASS;
|
|
|
|
|
|
esFetInfo[i].defSubs = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Get esFetInfo information from the current extraction style */
|
|
|
|
|
|
/* (this works only for the Tcl version with the embedded exttospice */
|
|
|
|
|
|
/* command) */
|
|
|
|
|
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
|
|
while (ExtGetDevInfo(idx++, &devname, &sd_rclass, &sub_rclass, &subname))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (idx == MAXDEVTYPES)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("Error: Ran out of space for device types!\n");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, devname);
|
|
|
|
|
|
if (!strcmp(devname, "None"))
|
|
|
|
|
|
esNoModelType = i;
|
|
|
|
|
|
if (EFStyle != NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
esFetInfo[i].resClassSD = sd_rclass;
|
|
|
|
|
|
esFetInfo[i].resClassSub = sub_rclass;
|
|
|
|
|
|
esFetInfo[i].defSubs = subname;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (EFCompat == TRUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Tcl variable substitution for substrate node names */
|
|
|
|
|
|
if (subname && (subname[0] == '$'))
|
|
|
|
|
|
{
|
|
|
|
|
|
resstr = (char *)Tcl_GetVar(magicinterp, &subname[1],
|
|
|
|
|
|
TCL_GLOBAL_ONLY);
|
|
|
|
|
|
if (resstr != NULL) esFetInfo[i].defSubs = resstr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (esDoHierarchy && (subname != NULL))
|
|
|
|
|
|
{
|
|
|
|
|
|
globalList *glptr;
|
|
|
|
|
|
char *locsubname, *bangptr;
|
|
|
|
|
|
bool isgood = TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
locsubname = StrDup(NULL, subname);
|
|
|
|
|
|
|
|
|
|
|
|
bangptr = locsubname + strlen(locsubname) - 1;
|
|
|
|
|
|
if (*bangptr == '!') *bangptr = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
// Ad-hoc check: Global names with "Error", "err", etc.
|
|
|
|
|
|
// should be rejected from the list. Also node name
|
|
|
|
|
|
// "None" is a common entry indicating that extracting
|
|
|
|
|
|
// an implicit substrate is disallowed.
|
|
|
|
|
|
|
|
|
|
|
|
if (!strncmp(locsubname, "err", 3)) isgood = FALSE;
|
|
|
|
|
|
else if (strstr(locsubname, "error") != NULL) isgood = FALSE;
|
|
|
|
|
|
else if (strstr(locsubname, "Error") != NULL) isgood = FALSE;
|
|
|
|
|
|
else if (strstr(locsubname, "ERROR") != NULL) isgood = FALSE;
|
|
|
|
|
|
else if (!strcasecmp(locsubname, "None")) isgood = FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
for (glptr = glist; glptr; glptr = glptr->gll_next)
|
|
|
|
|
|
if (!strcmp(glptr->gll_name, locsubname))
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
if (isgood && (glptr == NULL))
|
|
|
|
|
|
{
|
|
|
|
|
|
glptr = (globalList *)mallocMagic(sizeof(globalList));
|
|
|
|
|
|
glptr->gll_name = locsubname;
|
|
|
|
|
|
glptr->gll_next = glist;
|
|
|
|
|
|
glist = glptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
freeMagic(locsubname);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (EFCompat == TRUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Keep a pointer to the "GND" variable, if it exists. */
|
|
|
|
|
|
|
|
|
|
|
|
resstr = (char *)Tcl_GetVar(magicinterp, "GND", TCL_GLOBAL_ONLY);
|
|
|
|
|
|
if (resstr == NULL) resstr = "GND"; /* default value */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Write the output file */
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n",
|
|
|
|
|
|
spiceFormats[esFormat], inName, EFTech);
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, ".option scale=%gu\n\n", EFScale / 100.0);
|
|
|
|
|
|
else
|
|
|
|
|
|
esScale = EFScale / 100.0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Set output format flags */
|
|
|
|
|
|
|
|
|
|
|
|
flatFlags = EF_FLATNODES;
|
|
|
|
|
|
if (esMergeNames == FALSE) flatFlags |= EF_NONAMEMERGE;
|
|
|
|
|
|
|
|
|
|
|
|
// This forces options TRIMGLOB and CONVERTEQUAL, not sure that's such a
|
|
|
|
|
|
// good idea. . .
|
2019-10-15 03:46:07 +02:00
|
|
|
|
EFTrimFlags |= EF_TRIMGLOB | EF_CONVERTEQUAL | EF_CONVERTCOMMA;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
if (IS_FINITE_F(EFCapThreshold)) flatFlags |= EF_FLATCAPS;
|
|
|
|
|
|
if (esFormat == HSPICE)
|
|
|
|
|
|
EFTrimFlags |= EF_TRIMLOCAL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Write globals under a ".global" card */
|
|
|
|
|
|
|
|
|
|
|
|
if (esDoHierarchy && (glist != NULL))
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, ".global ");
|
|
|
|
|
|
while (glist != NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (EFCompat == TRUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Handle global names that are TCL variables */
|
|
|
|
|
|
if (glist->gll_name[0] == '$')
|
|
|
|
|
|
{
|
|
|
|
|
|
resstr = (char *)Tcl_GetVar(magicinterp,
|
|
|
|
|
|
&(glist->gll_name[1]), TCL_GLOBAL_ONLY);
|
|
|
|
|
|
if (resstr != NULL)
|
|
|
|
|
|
esFormatSubs(esSpiceF, resstr);
|
|
|
|
|
|
else
|
|
|
|
|
|
esFormatSubs(esSpiceF, glist->gll_name);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
esFormatSubs(esSpiceF, glist->gll_name);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
esFormatSubs(esSpiceF, glist->gll_name);
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, " ");
|
|
|
|
|
|
freeMagic(glist->gll_name);
|
|
|
|
|
|
freeMagic(glist);
|
|
|
|
|
|
glist = glist->gll_next;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, "\n\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Convert the hierarchical description to a flat one */
|
|
|
|
|
|
|
|
|
|
|
|
if (esFormat == HSPICE) {
|
|
|
|
|
|
HashInit(&subcktNameTable, 32, HT_STRINGKEYS);
|
|
|
|
|
|
#ifndef UNSORTED_SUBCKT
|
|
|
|
|
|
DQInit(&subcktNameQueue, 64);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
locDoSubckt = FALSE;
|
|
|
|
|
|
if (esDoHierarchy)
|
|
|
|
|
|
{
|
|
|
|
|
|
ESGenerateHierarchy(inName, flatFlags);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
EFFlatBuild(inName, flatFlags);
|
|
|
|
|
|
|
|
|
|
|
|
/* Determine if this is a subcircuit */
|
|
|
|
|
|
if (esDoSubckt == AUTO) {
|
|
|
|
|
|
if (efFlatRootDef->def_flags & DEF_SUBCIRCUIT)
|
|
|
|
|
|
locDoSubckt = TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
|
2017-08-02 04:14:42 +02:00
|
|
|
|
topVisit(efFlatRootDef, FALSE);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
/* When generating subcircuits, remove the subcircuit */
|
|
|
|
|
|
/* flag from the top level cell. Other than being */
|
|
|
|
|
|
/* used to generate the subcircuit wrapper, it should */
|
|
|
|
|
|
/* not prevent descending into its own hierarchy. */
|
|
|
|
|
|
|
|
|
|
|
|
efFlatRootDef->def_flags &= ~(DEF_SUBCIRCUIT);
|
|
|
|
|
|
|
|
|
|
|
|
/* If we don't want to write subcircuit calls, remove */
|
|
|
|
|
|
/* the subcircuit flag from all cells at this time. */
|
|
|
|
|
|
|
|
|
|
|
|
if (!esDoPorts)
|
|
|
|
|
|
EFVisitSubcircuits(subcktUndef, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
|
|
initMask = ( esDistrJunct ) ? (unsigned long)0 : DEV_CONNECT_MASK;
|
|
|
|
|
|
|
|
|
|
|
|
if (esMergeDevsA || esMergeDevsC)
|
|
|
|
|
|
{
|
|
|
|
|
|
devMerge *p;
|
|
|
|
|
|
|
|
|
|
|
|
EFVisitDevs(devMergeVisit, (ClientData) NULL);
|
|
|
|
|
|
TxPrintf("Devs merged: %d\n", esSpiceDevsMerged);
|
|
|
|
|
|
esFMIndex = 0;
|
|
|
|
|
|
for (p = devMergeList; p != NULL; p = p->next)
|
|
|
|
|
|
freeMagic(p);
|
|
|
|
|
|
devMergeList = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (esDistrJunct)
|
|
|
|
|
|
EFVisitDevs(devDistJunctVisit, (ClientData) NULL);
|
|
|
|
|
|
EFVisitDevs(spcdevVisit, (ClientData) NULL);
|
|
|
|
|
|
initMask = (unsigned long) 0;
|
|
|
|
|
|
if (flatFlags & EF_FLATCAPS)
|
|
|
|
|
|
{
|
|
|
|
|
|
(void) sprintf( esSpiceCapFormat, "C%%d %%s %%s %%.%dlffF\n",
|
|
|
|
|
|
esCapAccuracy);
|
|
|
|
|
|
EFVisitCaps(spccapVisit, (ClientData) NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
EFVisitResists(spcresistVisit, (ClientData) NULL);
|
|
|
|
|
|
EFVisitSubcircuits(subcktVisit, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
|
|
/* Visit nodes to find the substrate node */
|
|
|
|
|
|
EFVisitNodes(spcsubVisit, (ClientData)&substr);
|
|
|
|
|
|
if (substr == NULL)
|
|
|
|
|
|
substr = StrDup((char **)NULL, "0");
|
|
|
|
|
|
|
|
|
|
|
|
(void) sprintf( esSpiceCapFormat, "C%%d %%s %s %%.%dlffF%%s",
|
|
|
|
|
|
substr, esCapAccuracy);
|
|
|
|
|
|
EFVisitNodes(spcnodeVisit, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
|
|
if (EFCompat == FALSE) freeMagic(substr);
|
|
|
|
|
|
|
|
|
|
|
|
if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
|
|
|
|
|
|
fprintf(esSpiceF, ".ends\n");
|
|
|
|
|
|
|
|
|
|
|
|
if (esFormat == HSPICE)
|
|
|
|
|
|
printSubcktDict();
|
|
|
|
|
|
|
|
|
|
|
|
EFFlatDone();
|
|
|
|
|
|
}
|
|
|
|
|
|
EFDone();
|
|
|
|
|
|
|
|
|
|
|
|
if (esSpiceF) fclose(esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
TxPrintf("exttospice finished.\n");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else /* MAGIC_WRAPPER */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* main --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Top level of ext2spice.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
main(argc, argv)
|
|
|
|
|
|
int argc;
|
|
|
|
|
|
char *argv[];
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
int i,flatFlags;
|
|
|
|
|
|
char *inName;
|
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
bool locDoSubckt;
|
|
|
|
|
|
|
|
|
|
|
|
esSpiceDevsMerged = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static char *spiceFormats[] = {
|
|
|
|
|
|
"SPICE2", "SPICE3", "HSPICE", "NGSPICE", NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
EFInit();
|
|
|
|
|
|
EFResistThreshold = INFINITE_THRESHOLD ;
|
|
|
|
|
|
/* create default devinfo entries (MOSIS) which can be overriden by
|
|
|
|
|
|
the command line arguments */
|
|
|
|
|
|
for ( i = 0 ; i < MAXDEVTYPES ; i++ ) {
|
|
|
|
|
|
esFetInfo[i].resClassSD = NO_RESCLASS;
|
|
|
|
|
|
esFetInfo[i].resClassSub = NO_RESCLASS;
|
|
|
|
|
|
esFetInfo[i].defSubs = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, "ndev");
|
|
|
|
|
|
esFetInfo[i].resClassSD = 0 ;
|
|
|
|
|
|
esFetInfo[i].resClassSub = NO_RESCLASS ;
|
|
|
|
|
|
esFetInfo[i].defSubs = "Gnd!";
|
|
|
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, "pdev");
|
|
|
|
|
|
esFetInfo[i].resClassSD = 1 ;
|
|
|
|
|
|
esFetInfo[i].resClassSub = 8 ;
|
|
|
|
|
|
esFetInfo[i].defSubs = "Vdd!";
|
|
|
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, "nmos");
|
|
|
|
|
|
esFetInfo[i].resClassSD = 0 ;
|
|
|
|
|
|
esFetInfo[i].resClassSub = NO_RESCLASS ;
|
|
|
|
|
|
esFetInfo[i].defSubs = "Gnd!";
|
|
|
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, "pmos");
|
|
|
|
|
|
esFetInfo[i].resClassSD = 1 ;
|
|
|
|
|
|
esFetInfo[i].resClassSub = 8 ;
|
|
|
|
|
|
esFetInfo[i].defSubs = "Vdd!";
|
|
|
|
|
|
/* Process command line arguments */
|
|
|
|
|
|
|
|
|
|
|
|
inName = EFArgs(argc, argv, NULL, spcmainArgs, (ClientData) NULL);
|
|
|
|
|
|
if (inName == NULL)
|
|
|
|
|
|
exit (1);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Initializations specific to this program.
|
|
|
|
|
|
* Make output name inName.spice if they weren't
|
|
|
|
|
|
* explicitly specified
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if (spcesOutName == spcesDefaultOut)
|
|
|
|
|
|
sprintf(spcesDefaultOut, "%s.spice", inName);
|
|
|
|
|
|
|
|
|
|
|
|
if ((esSpiceF = fopen(spcesOutName, "w")) == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
perror(spcesOutName);
|
|
|
|
|
|
exit (1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Read the hierarchical description of the input circuit */
|
|
|
|
|
|
if (EFReadFile(inName, TRUE, esDoExtResis, FALSE) == FALSE)
|
|
|
|
|
|
{
|
|
|
|
|
|
exit (1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n",
|
|
|
|
|
|
spiceFormats[esFormat], inName, EFTech);
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF,".option scale=%gu\n\n", EFScale / 100.0);
|
|
|
|
|
|
else
|
|
|
|
|
|
esScale = EFScale / 100.0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Convert the hierarchical description to a flat one */
|
|
|
|
|
|
flatFlags = EF_FLATNODES;
|
|
|
|
|
|
EFTrimFlags |= EF_TRIMGLOB ;
|
|
|
|
|
|
if (IS_FINITE_F(EFCapThreshold)) flatFlags |= EF_FLATCAPS;
|
|
|
|
|
|
if (esFormat == HSPICE) {
|
|
|
|
|
|
EFTrimFlags |= EF_TRIMLOCAL ;
|
|
|
|
|
|
HashInit(&subcktNameTable, 32, HT_STRINGKEYS);
|
|
|
|
|
|
#ifndef UNSORTED_SUBCKT
|
|
|
|
|
|
DQInit(&subcktNameQueue, 64);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
EFFlatBuild(inName, flatFlags);
|
|
|
|
|
|
|
|
|
|
|
|
/* Determine if this is a subcircuit */
|
|
|
|
|
|
locDoSubckt = FALSE;
|
|
|
|
|
|
if (esDoSubckt == AUTO) {
|
|
|
|
|
|
if (efFlatRootDef->def_flags & DEF_SUBCIRCUIT)
|
|
|
|
|
|
locDoSubckt = TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
|
2017-08-02 04:14:42 +02:00
|
|
|
|
topVisit(efFlatRootDef, FALSE);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
/* If we don't want to write subcircuit calls, remove the */
|
|
|
|
|
|
/* subcircuit flag from all cells at this time. */
|
|
|
|
|
|
|
|
|
|
|
|
if (!esDoPorts)
|
|
|
|
|
|
EFVisitSubcircuits(subcktUndef, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
|
|
initMask = ( esDistrJunct ) ? (unsigned long)0 : DEV_CONNECT_MASK ;
|
|
|
|
|
|
|
|
|
|
|
|
if ( esMergeDevsA || esMergeDevsC ) {
|
|
|
|
|
|
EFVisitDevs(devMergeVisit, (ClientData) NULL);
|
|
|
|
|
|
TxPrintf("Devs merged: %d\n", esSpiceDevsMerged);
|
|
|
|
|
|
esFMIndex = 0 ;
|
|
|
|
|
|
{
|
|
|
|
|
|
devMerge *p;
|
|
|
|
|
|
|
|
|
|
|
|
for ( p = devMergeList ; p != NULL ; p=p->next ) freeMagic(p);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if ( esDistrJunct )
|
|
|
|
|
|
EFVisitDevs(devDistJunctVisit, (ClientData) NULL);
|
|
|
|
|
|
EFVisitDevs(spcdevVisit, (ClientData) NULL);
|
|
|
|
|
|
initMask = (unsigned long) 0;
|
|
|
|
|
|
if (flatFlags & EF_FLATCAPS) {
|
|
|
|
|
|
(void) sprintf( esSpiceCapFormat, "C%%d %%s %%s %%.%dlffF\n",esCapAccuracy);
|
|
|
|
|
|
EFVisitCaps(spccapVisit, (ClientData) NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
EFVisitResists(spcresistVisit, (ClientData) NULL);
|
|
|
|
|
|
EFVisitSubcircuits(subcktVisit, (ClientData) NULL);
|
|
|
|
|
|
(void) sprintf( esSpiceCapFormat, "C%%d %%s GND %%.%dlffF%%s", esCapAccuracy);
|
|
|
|
|
|
EFVisitNodes(spcnodeVisit, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
|
|
if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
|
|
|
|
|
|
fprintf(esSpiceF, ".ends\n");
|
|
|
|
|
|
|
|
|
|
|
|
if (esFormat == HSPICE)
|
|
|
|
|
|
printSubcktDict();
|
|
|
|
|
|
|
|
|
|
|
|
EFFlatDone();
|
|
|
|
|
|
EFDone();
|
|
|
|
|
|
|
|
|
|
|
|
if (esSpiceF) fclose(esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
TxPrintf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL));
|
|
|
|
|
|
exit (0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* MAGIC_WRAPPER */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcmainArgs --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Process those arguments that are specific to ext2spice.
|
|
|
|
|
|
* Assumes that *pargv[0][0] is '-', indicating a flag
|
|
|
|
|
|
* argument.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
spcmainArgs(pargc, pargv)
|
|
|
|
|
|
int *pargc;
|
|
|
|
|
|
char ***pargv;
|
|
|
|
|
|
{
|
|
|
|
|
|
char **argv = *pargv, *cp;
|
|
|
|
|
|
int argc = *pargc;
|
|
|
|
|
|
|
2019-10-16 15:17:58 +02:00
|
|
|
|
char usage_text[] = "Usage: ext2spice "
|
|
|
|
|
|
"[-B] [-o spicefile] [-M|-m] [-y cap_digits] "
|
|
|
|
|
|
"[-J flat|hier]\n"
|
|
|
|
|
|
"[-f spice2|spice3|hspice|ngspice] [-M] [-m] "
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
"[file]\n";
|
|
|
|
|
|
#else
|
|
|
|
|
|
"[-j device:sdRclass[/subRclass]/defaultSubstrate]\n"
|
|
|
|
|
|
"file\n\n or else see options to extcheck(1)\n";
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
switch (argv[0][1])
|
|
|
|
|
|
{
|
|
|
|
|
|
case 'd':
|
|
|
|
|
|
esDistrJunct = TRUE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'M':
|
|
|
|
|
|
esMergeDevsA = TRUE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'm':
|
|
|
|
|
|
esMergeDevsC = TRUE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'B':
|
|
|
|
|
|
esNoAttrs = TRUE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'F':
|
|
|
|
|
|
esDevNodesOnly = TRUE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'o':
|
|
|
|
|
|
if ((spcesOutName = ArgStr(&argc, &argv, "filename")) == NULL)
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'f': {
|
|
|
|
|
|
char *ftmp ;
|
|
|
|
|
|
|
|
|
|
|
|
if ((ftmp = ArgStr(&argc, &argv, "format")) == NULL)
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
if (strcasecmp(ftmp, "SPICE2") == 0)
|
|
|
|
|
|
esFormat = SPICE2;
|
|
|
|
|
|
else if (strcasecmp(ftmp, "SPICE3") == 0)
|
|
|
|
|
|
esFormat = SPICE3;
|
|
|
|
|
|
else if (strcasecmp(ftmp, "HSPICE") == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
esFormat = HSPICE;
|
|
|
|
|
|
esScale = -1.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (strcasecmp(ftmp, "NGSPICE") == 0)
|
|
|
|
|
|
esFormat = NGSPICE;
|
|
|
|
|
|
else goto usage;
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
case 'y': {
|
|
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
|
|
|
|
if (( t = ArgStr(&argc, &argv, "cap-accuracy") ) == NULL)
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
esCapAccuracy = atoi(t);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
#ifndef MAGIC_WRAPPER
|
|
|
|
|
|
case 'j':
|
|
|
|
|
|
{
|
|
|
|
|
|
char *rp, subsNode[80] ;
|
|
|
|
|
|
int ndx, rClass, rClassSub = -1;
|
|
|
|
|
|
|
|
|
|
|
|
if ((cp = ArgStr(&argc,&argv,"resistance class")) == NULL)
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
if ( (rp = (char *) strchr(cp,':')) == NULL )
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
else *rp++ = '\0';
|
|
|
|
|
|
if ( sscanf(rp, "%d/%d/%s", &rClass, &rClassSub, subsNode) != 3 ) {
|
|
|
|
|
|
rClassSub = -1 ;
|
|
|
|
|
|
if ( sscanf(rp, "%d/%s", &rClass, subsNode) != 2 ) goto usage;
|
|
|
|
|
|
}
|
|
|
|
|
|
ndx = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, cp);
|
|
|
|
|
|
esFetInfo[ndx].resClassSD = rClass;
|
|
|
|
|
|
esFetInfo[ndx].resClassSub = rClassSub;
|
|
|
|
|
|
if ( ((1<<rClass) & DEV_CONNECT_MASK) ||
|
|
|
|
|
|
((1<<rClass) & DEV_CONNECT_MASK) ) {
|
|
|
|
|
|
TxError("Oops it seems that you have 31\n");
|
|
|
|
|
|
TxError("resistance classes. You will need to recompile");
|
|
|
|
|
|
TxError("the extflat package and change ext2sim/spice\n");
|
|
|
|
|
|
TxError("DEV_CONNECT_MASK and or nodeClient\n");
|
|
|
|
|
|
exit (1);
|
|
|
|
|
|
}
|
|
|
|
|
|
esFetInfo[ndx].defSubs = (char *)mallocMagic((unsigned)(strlen(subsNode)+1));
|
|
|
|
|
|
strcpy(esFetInfo[ndx].defSubs,subsNode);
|
|
|
|
|
|
TxError("info: dev %s(%d) sdRclass=%d subRclass=%d dSub=%s\n",
|
|
|
|
|
|
cp, ndx, esFetInfo[ndx].resClassSD, esFetInfo[ndx].resClassSub,
|
|
|
|
|
|
esFetInfo[ndx].defSubs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif /* MAGIC_WRAPPER */
|
2019-10-16 15:17:58 +02:00
|
|
|
|
case 'h': /* -h or -help, as suggested by "ext2spice help" */
|
|
|
|
|
|
TxPrintf(usage_text);
|
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
default:
|
|
|
|
|
|
TxError("Unrecognized flag: %s\n", argv[0]);
|
|
|
|
|
|
goto usage;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*pargv = argv;
|
|
|
|
|
|
*pargc = argc;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
usage:
|
2019-10-16 15:17:58 +02:00
|
|
|
|
TxError(usage_text);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
#else
|
|
|
|
|
|
exit (1);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* SpiceGetNode --
|
|
|
|
|
|
*
|
|
|
|
|
|
* function to find a node given its hierarchical prefix and suffix
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* a pointer to the node struct or NULL
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
EFNode *
|
|
|
|
|
|
SpiceGetNode(prefix, suffix)
|
|
|
|
|
|
HierName *prefix;
|
|
|
|
|
|
HierName *suffix;
|
|
|
|
|
|
{
|
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
|
|
|
|
|
|
he = EFHNConcatLook(prefix, suffix, "output");
|
|
|
|
|
|
if (he == NULL) return NULL;
|
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
if (nn == NULL) return NULL;
|
|
|
|
|
|
return(nn->efnn_node);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* extHierSDAttr --
|
|
|
|
|
|
* Check if the attribute of the argument dev_terminal or the global
|
|
|
|
|
|
* settings are such that we want a hierarchical extraction of its S/D
|
|
|
|
|
|
*
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* TRUE or FALSE
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
bool extHierSDAttr(term)
|
|
|
|
|
|
DevTerm *term;
|
|
|
|
|
|
{
|
|
|
|
|
|
bool r = esHierAP;
|
|
|
|
|
|
|
|
|
|
|
|
if (term->dterm_attrs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Match(ATTR_HIERAP, term->dterm_attrs) != FALSE)
|
|
|
|
|
|
r = TRUE;
|
|
|
|
|
|
else if (Match(ATTR_FLATAP, term->dterm_attrs) != FALSE)
|
|
|
|
|
|
r = FALSE;
|
|
|
|
|
|
}
|
|
|
|
|
|
return r;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* subcktVisit --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Procedure to output a subcircuit definition to the .spice file.
|
|
|
|
|
|
* Called by EFVisitSubcircuits().
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the file esSpiceF.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Format of a .spice subcircuit call:
|
|
|
|
|
|
*
|
|
|
|
|
|
* X%d node1 node2 ... noden name
|
|
|
|
|
|
*
|
|
|
|
|
|
* where
|
|
|
|
|
|
* node1 node2 ... noden are the nodes connecting to the ports of
|
|
|
|
|
|
* the subcircuit. "name" is the name of the subcircuit. It is
|
|
|
|
|
|
* assumed that the definition of "name" (.defs name ... .ends)
|
|
|
|
|
|
* exists elsewhere and will be appended to the SPICE deck prior
|
|
|
|
|
|
* to simulation (as is also assumed for device models).
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
int
|
|
|
|
|
|
subcktVisit(use, hierName, is_top)
|
|
|
|
|
|
Use *use;
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
bool is_top; /* TRUE if this is the top-level cell */
|
|
|
|
|
|
{
|
|
|
|
|
|
EFNode *snode;
|
|
|
|
|
|
Def *def = use->use_def;
|
|
|
|
|
|
EFNodeName *nodeName;
|
|
|
|
|
|
int portorder, portmax, imp_max, tchars;
|
|
|
|
|
|
char stmp[MAX_STR_SIZE];
|
|
|
|
|
|
char *instname, *subcktname;
|
|
|
|
|
|
DevParam *plist, *pptr;
|
|
|
|
|
|
|
|
|
|
|
|
if (is_top == TRUE) return 0; /* Ignore the top-level cell */
|
|
|
|
|
|
|
|
|
|
|
|
/* Retain instance name unless esDoRenumber is set, or format is Spice2 */
|
|
|
|
|
|
if (use->use_id == NULL || esDoRenumber == TRUE || esFormat == SPICE2)
|
|
|
|
|
|
{
|
2018-12-12 23:23:35 +01:00
|
|
|
|
/* NOTE: This really needs to update subcktNameTable so that */
|
|
|
|
|
|
/* it tracks between instance names and node names, when using */
|
|
|
|
|
|
/* HSPICE format + esDoRenumber. */
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
fprintf(esSpiceF, "X%d", esSbckNum++);
|
|
|
|
|
|
tchars = 5;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
int savflags = EFTrimFlags;
|
2019-10-15 03:46:07 +02:00
|
|
|
|
EFTrimFlags = EF_CONVERTCOMMA; // Only substitute commas on subcircuit names
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
/* Use full hierarchical decomposition for name */
|
|
|
|
|
|
/* (not just use->use_id. hierName already has use->use_id at end) */
|
|
|
|
|
|
EFHNSprintf(stmp, hierName);
|
|
|
|
|
|
fprintf(esSpiceF, "X%s", stmp);
|
|
|
|
|
|
EFTrimFlags = savflags;
|
|
|
|
|
|
tchars = 1 + strlen(stmp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This is not a DEV, but "spcdevOutNode" is a general-purpose routine that */
|
|
|
|
|
|
/* turns a local name in the use's def to a hierarchical name in the */
|
|
|
|
|
|
/* calling def. */
|
|
|
|
|
|
|
|
|
|
|
|
/* Note that the ports of the subcircuit will not necessarily be */
|
|
|
|
|
|
/* ALL the entries in the hash table, so we have to check. */
|
|
|
|
|
|
|
|
|
|
|
|
portmax = EFGetPortMax(def, &imp_max);
|
|
|
|
|
|
|
|
|
|
|
|
if (portmax < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* No port order declared; print them as we see them. */
|
|
|
|
|
|
/* This shouldn't happen for proper .ext files written */
|
|
|
|
|
|
/* by the magic extractor. */
|
|
|
|
|
|
|
|
|
|
|
|
for (snode = (EFNode *) def->def_firstn.efnode_next;
|
|
|
|
|
|
snode != &def->def_firstn;
|
|
|
|
|
|
snode = (EFNode *) snode->efnode_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (snode->efnode_flags & EF_PORT)
|
|
|
|
|
|
for (nodeName = snode->efnode_name; nodeName != NULL;
|
|
|
|
|
|
nodeName = nodeName->efnn_next)
|
|
|
|
|
|
if (nodeName->efnn_port >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
portmax++;
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
tchars += spcdevOutNode(hierName, nodeName->efnn_hier,
|
|
|
|
|
|
"subcircuit", esSpiceF);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Look for all implicit substrate connections that are */
|
|
|
|
|
|
/* declared as local node names, and put them last. */
|
|
|
|
|
|
|
|
|
|
|
|
for (snode = (EFNode *) def->def_firstn.efnode_next;
|
|
|
|
|
|
snode != &def->def_firstn;
|
|
|
|
|
|
snode = (EFNode *) snode->efnode_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (snode->efnode_flags & EF_SUBS_PORT)
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeName = snode->efnode_name;
|
|
|
|
|
|
if (nodeName->efnn_port < 0)
|
|
|
|
|
|
nodeName->efnn_port = ++portmax;
|
|
|
|
|
|
|
|
|
|
|
|
/* This is not a hierarchical name or node! */
|
|
|
|
|
|
EFHNSprintf(stmp, nodeName->efnn_hier);
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, " %s", stmp);
|
|
|
|
|
|
tchars += (1 + strlen(stmp));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Port numbers need not start at zero or be contiguous. */
|
|
|
|
|
|
/* They will be printed in numerical order. */
|
|
|
|
|
|
|
|
|
|
|
|
portorder = 0;
|
|
|
|
|
|
while (portorder <= portmax)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (snode = (EFNode *) def->def_firstn.efnode_next;
|
|
|
|
|
|
snode != &def->def_firstn;
|
|
|
|
|
|
snode = (EFNode *) snode->efnode_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!(snode->efnode_flags & EF_PORT)) continue;
|
|
|
|
|
|
for (nodeName = snode->efnode_name; nodeName != NULL;
|
|
|
|
|
|
nodeName = nodeName->efnn_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
int portidx = nodeName->efnn_port;
|
|
|
|
|
|
if (portidx == portorder)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
tchars += spcdevOutNode(hierName, nodeName->efnn_hier,
|
|
|
|
|
|
"subcircuit", esSpiceF);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (nodeName != NULL) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
portorder++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Look for all implicit substrate connections that are */
|
|
|
|
|
|
/* declared as local node names, and put them last. */
|
|
|
|
|
|
|
|
|
|
|
|
portorder = portmax;
|
|
|
|
|
|
while (portorder <= imp_max)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (snode = (EFNode *) def->def_firstn.efnode_next;
|
|
|
|
|
|
snode != &def->def_firstn;
|
|
|
|
|
|
snode = (EFNode *) snode->efnode_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!(snode->efnode_flags & EF_SUBS_PORT)) continue;
|
|
|
|
|
|
nodeName = snode->efnode_name;
|
|
|
|
|
|
if (nodeName->efnn_port == portorder)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* This is not a hierarchical name or node! */
|
|
|
|
|
|
EFHNSprintf(stmp, nodeName->efnn_hier);
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, " %s", stmp);
|
|
|
|
|
|
tchars += (1 + strlen(stmp));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
portorder++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
|
/* SPICE subcircuit names must begin with A-Z. This will also be */
|
|
|
|
|
|
/* enforced when writing X subcircuit calls. */
|
|
|
|
|
|
subcktname = def->def_name;
|
|
|
|
|
|
while (!isalpha(*subcktname)) subcktname++;
|
|
|
|
|
|
|
|
|
|
|
|
if (tchars > 80) fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
fprintf(esSpiceF, " %s", subcktname); /* subcircuit model name */
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
// Check for a "device parameter" defined with the name of the cell.
|
|
|
|
|
|
// This contains a list of parameter strings to be passed to the
|
|
|
|
|
|
// cell instance.
|
|
|
|
|
|
|
|
|
|
|
|
instname = mallocMagic(2 + strlen(def->def_name));
|
|
|
|
|
|
sprintf(instname, ":%s", def->def_name);
|
|
|
|
|
|
plist = efGetDeviceParams(instname);
|
|
|
|
|
|
for (pptr = plist; pptr; pptr = pptr->parm_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, " %s", pptr->parm_name);
|
|
|
|
|
|
tchars += (1 + strlen(pptr->parm_name));
|
|
|
|
|
|
}
|
|
|
|
|
|
freeMagic(instname);
|
2017-08-02 04:14:42 +02:00
|
|
|
|
fprintf(esSpiceF, "\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* subcktUndef --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Procedure to remove the DEF_SUBCIRCUIT flag from all subcells.
|
|
|
|
|
|
* Called by EFVisitSubcircuits().
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Undefines the DEF_SUBCIRCUIT flag in each encountered Def.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
int
|
|
|
|
|
|
subcktUndef(use, hierName, is_top)
|
|
|
|
|
|
Use *use;
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
bool is_top; /* TRUE if this is the top-level cell */
|
|
|
|
|
|
{
|
|
|
|
|
|
Def *def = use->use_def;
|
|
|
|
|
|
|
|
|
|
|
|
def->def_flags &= ~(DEF_SUBCIRCUIT);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* topVisit --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Procedure to output a subcircuit definition to the .spice file.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the file esSpiceF.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Format of a .spice subcircuit definition:
|
|
|
|
|
|
*
|
|
|
|
|
|
* .subckt name node1 node2 ... noden
|
|
|
|
|
|
*
|
|
|
|
|
|
* where
|
|
|
|
|
|
* node1 node2 ... noden are the nodes connecting to the ports of
|
2017-08-02 04:14:42 +02:00
|
|
|
|
* the subcircuit. "name" is the name of the cell def. If "doStub"
|
|
|
|
|
|
* is TRUE, then the subcircuit is a stub (empty declaration) for a
|
|
|
|
|
|
* subcircuit, and implicit substrate connections should not be
|
|
|
|
|
|
* output.
|
2017-04-25 14:41:48 +02:00
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2017-08-02 04:14:42 +02:00
|
|
|
|
topVisit(def, doStub)
|
2017-04-25 14:41:48 +02:00
|
|
|
|
Def *def;
|
2017-08-02 04:14:42 +02:00
|
|
|
|
bool doStub;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
2019-10-29 14:44:28 +01:00
|
|
|
|
EFNode *snode, *basenode;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
EFNodeName *sname, *nodeName;
|
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
int portorder, portmax, tchars;
|
|
|
|
|
|
DevParam *plist, *pptr;
|
|
|
|
|
|
char *instname;
|
|
|
|
|
|
char *subcktname;
|
|
|
|
|
|
char *pname;
|
|
|
|
|
|
|
|
|
|
|
|
/* SPICE subcircuit names must begin with A-Z. This will also be */
|
|
|
|
|
|
/* enforced when writing X subcircuit calls. */
|
|
|
|
|
|
subcktname = def->def_name;
|
|
|
|
|
|
while (!isalpha(*subcktname)) subcktname++;
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, ".subckt %s", subcktname);
|
|
|
|
|
|
tchars = 8 + strlen(subcktname);
|
|
|
|
|
|
|
|
|
|
|
|
/* Note that the ports of the subcircuit will not necessarily be */
|
|
|
|
|
|
/* ALL the entries in the hash table, so we have to check. */
|
|
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
|
portmax = -1;
|
|
|
|
|
|
|
|
|
|
|
|
while (he = HashNext(&def->def_nodes, &hs))
|
|
|
|
|
|
{
|
|
|
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
if (sname == NULL) continue;
|
|
|
|
|
|
snode = sname->efnn_node;
|
|
|
|
|
|
if (!(snode->efnode_flags & EF_PORT)) continue;
|
|
|
|
|
|
for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
portorder = nodeName->efnn_port;
|
|
|
|
|
|
if (portorder > portmax) portmax = portorder;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (portmax < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* No port order declared; print them and number them */
|
|
|
|
|
|
/* as we encounter them. This normally happens only */
|
|
|
|
|
|
/* when writing hierarchical decks for LVS. */
|
|
|
|
|
|
|
|
|
|
|
|
portorder = 0;
|
|
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
|
while (he = HashNext(&def->def_nodes, &hs))
|
|
|
|
|
|
{
|
|
|
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
if (sname == NULL) continue;
|
|
|
|
|
|
snode = sname->efnn_node;
|
|
|
|
|
|
|
|
|
|
|
|
if (snode->efnode_flags & EF_PORT)
|
2019-10-29 14:44:28 +01:00
|
|
|
|
{
|
|
|
|
|
|
pname = nodeSpiceName(snode->efnode_name->efnn_hier, &basenode);
|
|
|
|
|
|
if (basenode->efnode_name->efnn_port < 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Line continuation */
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, " %s", pname);
|
|
|
|
|
|
tchars += strlen(pname) + 1;
|
2019-10-29 14:44:28 +01:00
|
|
|
|
basenode->efnode_name->efnn_port = portorder++;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
}
|
2019-10-29 14:44:28 +01:00
|
|
|
|
if (snode->efnode_name->efnn_hier->hn_parent == NULL)
|
|
|
|
|
|
snode->efnode_name->efnn_port = basenode->efnode_name->efnn_port;
|
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Port numbers need not start at zero or be contiguous. */
|
|
|
|
|
|
/* They will be printed in numerical order. */
|
|
|
|
|
|
|
|
|
|
|
|
portorder = 0;
|
|
|
|
|
|
while (portorder <= portmax)
|
|
|
|
|
|
{
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
|
while (he = HashNext(&def->def_nodes, &hs))
|
|
|
|
|
|
{
|
|
|
|
|
|
int portidx;
|
|
|
|
|
|
EFNodeName *unnumbered;
|
|
|
|
|
|
|
|
|
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
2019-06-06 20:53:07 +02:00
|
|
|
|
if (sname == NULL) continue; /* Should not happen */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
snode = sname->efnn_node;
|
|
|
|
|
|
|
|
|
|
|
|
if (!(snode->efnode_flags & EF_PORT)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
portidx = nodeName->efnn_port;
|
|
|
|
|
|
if (portidx == portorder)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Line continuation */
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
2019-10-29 14:44:28 +01:00
|
|
|
|
pname = nodeSpiceName(snode->efnode_name->efnn_hier, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
fprintf(esSpiceF, " %s", pname);
|
|
|
|
|
|
tchars += strlen(pname) + 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (portidx < 0)
|
|
|
|
|
|
unnumbered = nodeName;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (nodeName != NULL)
|
|
|
|
|
|
break;
|
|
|
|
|
|
else if (portidx < 0)
|
2017-08-02 04:14:42 +02:00
|
|
|
|
{
|
|
|
|
|
|
// Node has not been assigned a port number.
|
|
|
|
|
|
// Give it one unless this is a black-box circuit
|
|
|
|
|
|
// and "ext2spice blackbox on" is in effect.
|
|
|
|
|
|
|
|
|
|
|
|
if (esDoBlackBox == FALSE || !(def->def_flags & DEF_ABSTRACT))
|
|
|
|
|
|
unnumbered->efnn_port = ++portmax;
|
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
portorder++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add all implicitly-defined local substrate node names */
|
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
|
if (!doStub)
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
|
while (he = HashNext(&def->def_nodes, &hs))
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
if (sname == NULL) continue;
|
|
|
|
|
|
snode = sname->efnn_node;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
|
if (snode->efnode_flags & EF_SUBS_PORT)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (snode->efnode_name->efnn_port < 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
|
char stmp[MAX_STR_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Line continuation */
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* This is not a hierarchical name or node! */
|
|
|
|
|
|
EFHNSprintf(stmp, snode->efnode_name->efnn_hier);
|
|
|
|
|
|
fprintf(esSpiceF, " %s", stmp);
|
|
|
|
|
|
snode->efnode_name->efnn_port = portorder++;
|
|
|
|
|
|
tchars += strlen(stmp) + 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Add any parameters defined by "property parameter" in the cell
|
|
|
|
|
|
|
|
|
|
|
|
instname = mallocMagic(2 + strlen(def->def_name));
|
|
|
|
|
|
sprintf(instname, ":%s", def->def_name);
|
|
|
|
|
|
plist = efGetDeviceParams(instname);
|
|
|
|
|
|
for (pptr = plist; pptr; pptr = pptr->parm_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Line continuation */
|
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
|
tchars = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
pname = pptr->parm_name;
|
|
|
|
|
|
fprintf(esSpiceF, " %s", pname);
|
|
|
|
|
|
tchars += strlen(pname) + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
freeMagic(instname);
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, "\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcWriteParams ---
|
|
|
|
|
|
*
|
|
|
|
|
|
* Write parameters to a device line in SPICE output. This is normally
|
|
|
|
|
|
* restricted to subcircuit devices but may include other devices to
|
|
|
|
|
|
* accomodate various extensions to the basic SPICE format.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM)
|
|
|
|
|
|
Dev *dev; /* Dev being output */
|
|
|
|
|
|
HierName *hierName; /* Hierarchical path down to this dev */
|
|
|
|
|
|
float scale; /* Scale transform for output */
|
|
|
|
|
|
int l; /* Device length, in internal units */
|
|
|
|
|
|
int w; /* Device width, in internal units */
|
|
|
|
|
|
float sdM; /* Device multiplier */
|
|
|
|
|
|
{
|
|
|
|
|
|
bool hierD;
|
|
|
|
|
|
DevParam *plist;
|
|
|
|
|
|
int parmval;
|
|
|
|
|
|
EFNode *dnode, *subnodeFlat = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
bool extHierSDAttr();
|
|
|
|
|
|
|
|
|
|
|
|
plist = efGetDeviceParams(EFDevTypes[dev->dev_type]);
|
|
|
|
|
|
while (plist != NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (plist->parm_type[0])
|
|
|
|
|
|
{
|
|
|
|
|
|
case 'a':
|
|
|
|
|
|
// Check for area of terminal node vs. device area
|
|
|
|
|
|
if (plist->parm_type[1] == '\0' || plist->parm_type[1] == '0')
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
parmval = dev->dev_area;
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", parmval * scale * scale);
|
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", parmval * scale * scale
|
|
|
|
|
|
* esScale * esScale * plist->parm_scale
|
|
|
|
|
|
* 1E-12);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "%gp", parmval * scale * scale
|
|
|
|
|
|
* esScale * esScale);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
int pn;
|
|
|
|
|
|
|
|
|
|
|
|
pn = plist->parm_type[1] - '0';
|
|
|
|
|
|
if (pn >= dev->dev_nterm) pn = dev->dev_nterm - 1;
|
|
|
|
|
|
|
|
|
|
|
|
hierD = extHierSDAttr(&dev->dev_terms[pn]);
|
|
|
|
|
|
|
|
|
|
|
|
// For parameter a<n> followed by parameter p<n>,
|
|
|
|
|
|
// process both at the same time.
|
|
|
|
|
|
|
|
|
|
|
|
if (plist->parm_next && plist->parm_next->parm_type[0]
|
|
|
|
|
|
== 'p' && plist->parm_next->parm_type[1]
|
|
|
|
|
|
== plist->parm_type[1])
|
|
|
|
|
|
{
|
|
|
|
|
|
if (hierD)
|
|
|
|
|
|
spcnAPHier(&dev->dev_terms[pn], hierName,
|
|
|
|
|
|
esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, plist->parm_type,
|
|
|
|
|
|
plist->parm_next->parm_type,
|
|
|
|
|
|
sdM, esSpiceF);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
spcnAP(dnode, esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, plist->parm_name,
|
|
|
|
|
|
plist->parm_next->parm_name,
|
|
|
|
|
|
sdM, esSpiceF, w);
|
|
|
|
|
|
}
|
|
|
|
|
|
plist = plist->parm_next;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (hierD)
|
|
|
|
|
|
spcnAPHier(&dev->dev_terms[pn], hierName,
|
|
|
|
|
|
esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, plist->parm_type, NULL,
|
|
|
|
|
|
sdM, esSpiceF);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
spcnAP(dnode, esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, plist->parm_name, NULL,
|
|
|
|
|
|
sdM, esSpiceF, w);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'p':
|
|
|
|
|
|
// Check for area of terminal node vs. device area
|
|
|
|
|
|
if (plist->parm_type[1] == '\0' || plist->parm_type[1] == '0')
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
parmval = dev->dev_perim;
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", parmval * scale);
|
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", parmval * scale
|
|
|
|
|
|
* esScale * plist->parm_scale * 1E-6);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "%gu", parmval * scale * esScale);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
int pn;
|
|
|
|
|
|
|
|
|
|
|
|
pn = plist->parm_type[1] - '0';
|
|
|
|
|
|
if (pn >= dev->dev_nterm) pn = dev->dev_nterm - 1;
|
|
|
|
|
|
|
|
|
|
|
|
hierD = extHierSDAttr(&dev->dev_terms[pn]);
|
|
|
|
|
|
|
|
|
|
|
|
// For parameter p<n> followed by parameter a<n>,
|
|
|
|
|
|
// process both at the same time.
|
|
|
|
|
|
|
|
|
|
|
|
if (plist->parm_next && plist->parm_next->parm_type[0]
|
|
|
|
|
|
== 'a' && plist->parm_next->parm_type[1]
|
|
|
|
|
|
== plist->parm_type[1])
|
|
|
|
|
|
{
|
|
|
|
|
|
if (hierD)
|
|
|
|
|
|
spcnAPHier(&dev->dev_terms[pn], hierName,
|
|
|
|
|
|
esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, plist->parm_next->parm_type,
|
|
|
|
|
|
plist->parm_type, sdM, esSpiceF);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
spcnAP(dnode, esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, plist->parm_next->parm_name,
|
|
|
|
|
|
plist->parm_name, sdM, esSpiceF, w);
|
|
|
|
|
|
}
|
|
|
|
|
|
plist = plist->parm_next;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (hierD)
|
|
|
|
|
|
spcnAPHier(&dev->dev_terms[pn], hierName,
|
|
|
|
|
|
esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, NULL, plist->parm_type,
|
|
|
|
|
|
sdM, esSpiceF);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
spcnAP(dnode, esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, NULL, plist->parm_name,
|
|
|
|
|
|
sdM, esSpiceF, w);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'l':
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", l * scale);
|
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", l * scale * esScale
|
|
|
|
|
|
* plist->parm_scale * 1E-6);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "%gu", l * scale * esScale);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'w':
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", w * scale);
|
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", w * scale * esScale
|
|
|
|
|
|
* plist->parm_scale * 1E-6);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "%gu", w * scale * esScale);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 's':
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
subnodeFlat = spcdevSubstrate(hierName,
|
|
|
|
|
|
dev->dev_subsnode->efnode_name->efnn_hier,
|
|
|
|
|
|
dev->dev_type, esSpiceF);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'x':
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", dev->dev_rect.r_xbot * scale);
|
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", dev->dev_rect.r_xbot * scale
|
|
|
|
|
|
* esScale * plist->parm_scale * 1E-6);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "%gu", dev->dev_rect.r_xbot * scale
|
|
|
|
|
|
* esScale);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'y':
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", dev->dev_rect.r_ybot * scale);
|
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, "%g", dev->dev_rect.r_ybot * scale
|
|
|
|
|
|
* esScale * plist->parm_scale * 1E-6);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "%gu", dev->dev_rect.r_ybot * scale
|
|
|
|
|
|
* esScale);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'r':
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
fprintf(esSpiceF, "%f", (double)(dev->dev_res));
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'c':
|
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
|
fprintf(esSpiceF, "%ff", (double)(dev->dev_cap));
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
plist = plist->parm_next;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add parameters that are to be copied verbatim */
|
|
|
|
|
|
for (plist = dev->dev_params; plist; plist = plist->parm_next)
|
|
|
|
|
|
fprintf(esSpiceF, " %s", plist->parm_name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* esOutputResistor ---
|
|
|
|
|
|
*
|
|
|
|
|
|
* Routine used by spcdevVisit to print a resistor device. This
|
|
|
|
|
|
* is broken out into a separate routine so that each resistor
|
|
|
|
|
|
* device may be represented (if the option is selected) by a
|
|
|
|
|
|
* "tee" network of two resistors on either side of the central
|
|
|
|
|
|
* node, which then has a capacitance to ground.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Output to the SPICE deck.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
esOutputResistor(dev, hierName, scale, term1, term2, has_model, l, w, dscale)
|
|
|
|
|
|
Dev *dev; /* Dev being output */
|
|
|
|
|
|
HierName *hierName; /* Hierarchical path down to this dev */
|
|
|
|
|
|
float scale; /* Scale transform for output */
|
|
|
|
|
|
DevTerm *term1, *term2; /* Terminals of the device */
|
|
|
|
|
|
bool has_model; /* Is this a modeled resistor? */
|
|
|
|
|
|
int l, w; /* Device length and width */
|
|
|
|
|
|
int dscale; /* Device scaling (for split resistors) */
|
|
|
|
|
|
{
|
|
|
|
|
|
float sdM ;
|
|
|
|
|
|
char name[12], devchar;
|
|
|
|
|
|
|
|
|
|
|
|
/* Resistor is "Rnnn term1 term2 value" */
|
|
|
|
|
|
/* extraction sets two terminals, which are assigned */
|
|
|
|
|
|
/* term1=gate term2=source by the above code. */
|
|
|
|
|
|
/* extracted units are Ohms; output is in Ohms */
|
|
|
|
|
|
|
|
|
|
|
|
spcdevOutNode(hierName, term1->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
spcdevOutNode(hierName, term2->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
sdM = getCurDevMult();
|
|
|
|
|
|
|
|
|
|
|
|
/* SPICE has two resistor types. If the "name" (EFDevTypes) is */
|
|
|
|
|
|
/* "None", the simple resistor type is used, and a value given. */
|
|
|
|
|
|
/* If not, the "semiconductor resistor" is used, and L and W */
|
|
|
|
|
|
/* and the device name are output. */
|
|
|
|
|
|
|
|
|
|
|
|
if (!has_model)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %f", ((double)(dev->dev_res)
|
|
|
|
|
|
/ (double)(dscale)) / (double)sdM);
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, " w=%g l=%g", w * scale, (l * scale) / dscale);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, " w=%gu l=%gu",
|
|
|
|
|
|
w * scale * esScale,
|
|
|
|
|
|
((l * scale * esScale) / dscale));
|
|
|
|
|
|
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
if (sdM != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, " M=%g", sdM);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-10-23 15:33:37 +02:00
|
|
|
|
/* Report if device at index n has been deleted due to merging */
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
devIsKilled(n)
|
|
|
|
|
|
int n;
|
|
|
|
|
|
{
|
|
|
|
|
|
return (esFMult[(n)] <= (float)0.0) ? TRUE : FALSE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add a dev's multiplier to the table and grow it if necessary */
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
addDevMult(f)
|
|
|
|
|
|
float f;
|
|
|
|
|
|
{
|
|
|
|
|
|
int i;
|
|
|
|
|
|
float *op;
|
|
|
|
|
|
|
|
|
|
|
|
if (esFMult == NULL) {
|
|
|
|
|
|
esFMult = (float *) mallocMagic((unsigned) (esFMSize*sizeof(float)));
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (esFMIndex >= esFMSize)
|
|
|
|
|
|
{
|
|
|
|
|
|
op = esFMult;
|
|
|
|
|
|
esFMSize *= 2;
|
|
|
|
|
|
esFMult = (float *)mallocMagic((unsigned)(esFMSize * sizeof(float)));
|
|
|
|
|
|
for (i = 0; i < esFMSize / 2; i++) esFMult[i] = op[i];
|
|
|
|
|
|
if (op) freeMagic(op);
|
|
|
|
|
|
}
|
|
|
|
|
|
esFMult[esFMIndex++] = f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Set the multiplier value f of device at index i */
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
setDevMult(i, f)
|
|
|
|
|
|
int i;
|
|
|
|
|
|
float f;
|
|
|
|
|
|
{
|
|
|
|
|
|
esFMult[i] = f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the multiplier value of the device at the current index esFMIndex */
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
|
getCurDevMult()
|
|
|
|
|
|
{
|
|
|
|
|
|
return (esFMult && (esFMIndex > 0)) ? esFMult[esFMIndex-1] : (float)1.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcdevVisit --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Procedure to output a single dev to the .spice file.
|
|
|
|
|
|
* Called by EFVisitDevs().
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the file esSpiceF.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Format of a .spice dev line:
|
|
|
|
|
|
*
|
|
|
|
|
|
* M%d drain gate source substrate type w=w l=l * x y
|
|
|
|
|
|
* + ad= pd= as= ps= * asub= psub=
|
|
|
|
|
|
* **devattr g= s= d=
|
|
|
|
|
|
*
|
|
|
|
|
|
* where
|
|
|
|
|
|
* type is a name identifying this type of transistor
|
|
|
|
|
|
* other types of transistors are extracted with
|
|
|
|
|
|
* an M card but it should be easy to turn them to whatever
|
|
|
|
|
|
* you want.
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
spcdevVisit(dev, hierName, scale, trans)
|
|
|
|
|
|
Dev *dev; /* Dev being output */
|
|
|
|
|
|
HierName *hierName; /* Hierarchical path down to this dev */
|
|
|
|
|
|
float scale; /* Scale transform for output */
|
|
|
|
|
|
Transform *trans; /* (unused) */
|
|
|
|
|
|
{
|
|
|
|
|
|
DevParam *plist, *pptr;
|
|
|
|
|
|
DevTerm *gate, *source, *drain;
|
|
|
|
|
|
EFNode *subnode, *snode, *dnode, *subnodeFlat = NULL;
|
|
|
|
|
|
int l, w, i, parmval;
|
|
|
|
|
|
bool subAP= FALSE, hierS, hierD, extHierSDAttr() ;
|
|
|
|
|
|
float sdM;
|
|
|
|
|
|
char name[12], devchar;
|
|
|
|
|
|
bool has_model = TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
sprintf(name, "output");
|
|
|
|
|
|
|
|
|
|
|
|
/* If no terminals, can't do much of anything */
|
|
|
|
|
|
if (dev->dev_nterm < 1 )
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
if ( (esMergeDevsA || esMergeDevsC) && devIsKilled(esFMIndex++) )
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Get L and W of device */
|
|
|
|
|
|
EFGetLengthAndWidth(dev, &l, &w);
|
|
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* If any terminal is marked with attribute "D" or "S" */
|
|
|
|
|
|
/* (label "D$" or "S$" at poly-diffusion interface), */
|
|
|
|
|
|
/* then force order of source and drain accordingly. */
|
|
|
|
|
|
|
|
|
|
|
|
if ((dev->dev_terms[1].dterm_attrs &&
|
|
|
|
|
|
!strcmp(dev->dev_terms[1].dterm_attrs, "D")) ||
|
|
|
|
|
|
(dev->dev_terms[2].dterm_attrs &&
|
|
|
|
|
|
!strcmp(dev->dev_terms[2].dterm_attrs, "S")))
|
|
|
|
|
|
{
|
|
|
|
|
|
drain = &dev->dev_terms[1];
|
|
|
|
|
|
source = &dev->dev_terms[2];
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
drain = &dev->dev_terms[2];
|
|
|
|
|
|
}
|
|
|
|
|
|
subnode = dev->dev_subsnode;
|
|
|
|
|
|
|
|
|
|
|
|
/* Check for minimum number of terminals. */
|
|
|
|
|
|
|
|
|
|
|
|
switch(dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
|
if ((dev->dev_nterm < 2) && (subnode == NULL))
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("Diode has only one terminal\n");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
if (dev->dev_nterm < 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("Device other than subcircuit has only "
|
|
|
|
|
|
"one terminal\n");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Original hack for BiCMOS, Tim 10/4/97, is deprecated. */
|
|
|
|
|
|
/* Use of "device bjt" preferred to "fet" with model="npn". */
|
|
|
|
|
|
|
|
|
|
|
|
if (!strcmp(EFDevTypes[dev->dev_type], "npn")) dev->dev_class = DEV_BJT;
|
|
|
|
|
|
|
|
|
|
|
|
/* For resistor and capacitor classes, set a boolean to */
|
|
|
|
|
|
/* denote whether the device has a model or not, so we */
|
|
|
|
|
|
/* don't have to keep doing a string compare on EFDevTypes. */
|
|
|
|
|
|
|
|
|
|
|
|
switch(dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
|
if (dev->dev_nterm < 1)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
if (dev->dev_type == esNoModelType)
|
|
|
|
|
|
has_model = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Flag shorted devices---this should probably be an option */
|
|
|
|
|
|
switch(dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
|
case DEV_FET:
|
|
|
|
|
|
if (source == drain)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
|
|
|
|
|
fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
if (gate == source)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
|
|
|
|
|
fprintf(esSpiceF, "** SHORTED DEVICE\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Generate SPICE device name */
|
|
|
|
|
|
switch(dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
|
case DEV_FET:
|
|
|
|
|
|
devchar = 'M';
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_BJT:
|
|
|
|
|
|
devchar = 'Q';
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
|
devchar = 'D';
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
|
devchar = 'R';
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
|
devchar = 'C';
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
|
devchar = 'X';
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, "%c", devchar);
|
|
|
|
|
|
|
|
|
|
|
|
/* Device index is taken from gate attributes if attached; */
|
|
|
|
|
|
/* otherwise, the device is numbered in sequence. */
|
|
|
|
|
|
|
|
|
|
|
|
if (gate->dterm_attrs)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Output the name found in the gate attributes */
|
|
|
|
|
|
/* prefixed by the hierarchical name. */
|
|
|
|
|
|
fprintf(esSpiceF, "%s%s", EFHNToStr(hierName), gate->dterm_attrs);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
|
fprintf(esSpiceF, "%d", esResNum++);
|
|
|
|
|
|
/* For resistor tee networks, use, e.g., */
|
|
|
|
|
|
/* "R1A" and "R1B", for clarity */
|
|
|
|
|
|
if (esDoResistorTee) fprintf(esSpiceF, "A");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
|
fprintf(esSpiceF, "%d", esDiodeNum++);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
|
fprintf(esSpiceF, "%d", esCapNum++);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
|
fprintf(esSpiceF, "%d", esSbckNum++);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
fprintf(esSpiceF, "%d", esDevNum++);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Order and number of nodes in the output depends on the device class */
|
|
|
|
|
|
|
|
|
|
|
|
switch (dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_BJT:
|
|
|
|
|
|
|
|
|
|
|
|
/* BJT is "Qnnn collector emitter base model" */
|
|
|
|
|
|
/* extraction sets collector=subnode, emitter=gate, base=drain */
|
|
|
|
|
|
|
|
|
|
|
|
sprintf(name, "fet");
|
|
|
|
|
|
spcdevOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSpiceF);
|
|
|
|
|
|
spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
/* fix mixed up drain/source for bjts hace 2/2/99 */
|
|
|
|
|
|
if (gate->dterm_node->efnode_name->efnn_hier ==
|
|
|
|
|
|
source->dterm_node->efnode_name->efnn_hier)
|
|
|
|
|
|
spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
else
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
2019-06-07 20:13:50 +02:00
|
|
|
|
sdM = getCurDevMult();
|
2017-04-25 14:41:48 +02:00
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
|
|
|
|
|
|
|
/* MOS-like subcircuit is "Xnnn source gate [drain [sub]]" */
|
|
|
|
|
|
/* to more conveniently handle cases where MOS devices are */
|
|
|
|
|
|
/* modeled by subcircuits with the same pin ordering. */
|
|
|
|
|
|
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
/* Drop through to below (no break statement) */
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_SUBCKT:
|
2018-10-31 02:56:05 +01:00
|
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
/* Subcircuit is "Xnnn gate [source [drain [sub]]]" */
|
|
|
|
|
|
/* Subcircuit .subckt record must be ordered to match! */
|
|
|
|
|
|
|
|
|
|
|
|
spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
/* Drop through to below (no break statement) */
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_RSUBCKT:
|
|
|
|
|
|
/* RC-like subcircuits are exactly like other subcircuits */
|
|
|
|
|
|
/* except that the "gate" node is treated as an identifier */
|
|
|
|
|
|
/* only and is not output. */
|
|
|
|
|
|
|
|
|
|
|
|
if ((dev->dev_nterm > 1) && (dev->dev_class != DEV_MSUBCKT))
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
if (dev->dev_nterm > 2)
|
|
|
|
|
|
spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
/* The following only applies to DEV_SUBCKT*, which may define as */
|
|
|
|
|
|
/* many terminal types as it wants. */
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 4; i < dev->dev_nterm; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
drain = &dev->dev_terms[i - 1];
|
|
|
|
|
|
spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the device parameters now, and check if the substrate is */
|
|
|
|
|
|
/* passed as a parameter rather than as a node. */
|
|
|
|
|
|
|
|
|
|
|
|
plist = efGetDeviceParams(EFDevTypes[dev->dev_type]);
|
|
|
|
|
|
for (pptr = plist; pptr != NULL; pptr = pptr->parm_next)
|
|
|
|
|
|
if (pptr->parm_type[0] == 's')
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
if ((pptr == NULL) && subnode)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " ");
|
|
|
|
|
|
subnodeFlat = spcdevSubstrate(hierName,
|
|
|
|
|
|
subnode->efnode_name->efnn_hier,
|
|
|
|
|
|
dev->dev_type, esSpiceF);
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
|
|
|
|
|
|
|
|
|
|
|
/* Write all requested parameters to the subcircuit call. */
|
|
|
|
|
|
|
|
|
|
|
|
sdM = getCurDevMult();
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
if (sdM != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, " M=%g", sdM);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
|
if (esDoResistorTee)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* There are three ways of handling capacitance */
|
|
|
|
|
|
/* on resistor networks. One is to ignore it */
|
|
|
|
|
|
/* (the default; generates "floating" nodes in */
|
|
|
|
|
|
/* the SPICE output) which is okay for LVS. */
|
|
|
|
|
|
/* Another way is the Pi network, in which the */
|
|
|
|
|
|
/* capacitance is split evenly between the */
|
|
|
|
|
|
/* terminals. Again, the resistor node is left */
|
|
|
|
|
|
/* floating. The third is the Tee network, in */
|
|
|
|
|
|
/* which the resistance is split in two parts, */
|
|
|
|
|
|
/* connecting to a capacitor to ground in the */
|
|
|
|
|
|
/* middle. This is the best solution but plays */
|
|
|
|
|
|
/* havoc with LVS. So, the choice is a command */
|
|
|
|
|
|
/* line option. */
|
|
|
|
|
|
|
|
|
|
|
|
esOutputResistor(dev, hierName, scale, gate, source, has_model,
|
|
|
|
|
|
l, w, 2);
|
|
|
|
|
|
fprintf(esSpiceF, "\n%c", devchar);
|
|
|
|
|
|
if (gate->dterm_attrs)
|
|
|
|
|
|
fprintf(esSpiceF, "%s%sB", EFHNToStr(hierName), gate->dterm_attrs);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "%dB", esResNum - 1);
|
|
|
|
|
|
esOutputResistor(dev, hierName, scale, gate, drain, has_model,
|
|
|
|
|
|
l, w, 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
esOutputResistor(dev, hierName, scale, source, drain, has_model,
|
|
|
|
|
|
l, w, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
|
|
|
|
|
|
|
/* Diode is "Dnnn top bottom model" */
|
|
|
|
|
|
|
|
|
|
|
|
spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
if (dev->dev_nterm > 1)
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
else if (subnode)
|
|
|
|
|
|
spcdevOutNode(hierName, subnode->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
2019-06-07 21:00:39 +02:00
|
|
|
|
sdM = getCurDevMult();
|
2017-04-25 14:41:48 +02:00
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
|
|
|
|
|
|
|
/* Diode is "Dnnn bottom top model" */
|
|
|
|
|
|
|
|
|
|
|
|
if (dev->dev_nterm > 1)
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
else if (subnode)
|
|
|
|
|
|
spcdevOutNode(hierName, subnode->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
2019-06-07 21:00:39 +02:00
|
|
|
|
sdM = getCurDevMult();
|
2017-04-25 14:41:48 +02:00
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
|
|
|
|
|
|
|
/* Capacitor is "Cnnn top bottom value" */
|
|
|
|
|
|
/* extraction sets top=gate bottom=source */
|
|
|
|
|
|
/* extracted units are fF; output is in fF */
|
|
|
|
|
|
|
|
|
|
|
|
spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
sdM = getCurDevMult();
|
|
|
|
|
|
|
|
|
|
|
|
/* SPICE has two capacitor types. If the "name" (EFDevTypes) is */
|
|
|
|
|
|
/* "None", the simple capacitor type is used, and a value given. */
|
|
|
|
|
|
/* If not, the "semiconductor capacitor" is used, and L and W */
|
|
|
|
|
|
/* and the device name are output. */
|
|
|
|
|
|
|
|
|
|
|
|
if (!has_model)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %ffF", (double)sdM *
|
|
|
|
|
|
(double)(dev->dev_cap));
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, " w=%gu l=%gu",
|
|
|
|
|
|
w * scale * esScale,
|
|
|
|
|
|
l * scale * esScale);
|
|
|
|
|
|
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
if (sdM != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, " M=%g", sdM);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
|
|
|
|
|
|
|
/* Capacitor is "Cnnn bottom top value" */
|
|
|
|
|
|
/* extraction sets top=source bottom=gate */
|
|
|
|
|
|
/* extracted units are fF; output is in fF */
|
|
|
|
|
|
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
|
|
|
|
|
|
sdM = getCurDevMult();
|
|
|
|
|
|
|
|
|
|
|
|
/* SPICE has two capacitor types. If the "name" (EFDevTypes) is */
|
|
|
|
|
|
/* "None", the simple capacitor type is used, and a value given. */
|
|
|
|
|
|
/* If not, the "semiconductor capacitor" is used, and L and W */
|
|
|
|
|
|
/* and the device name are output. */
|
|
|
|
|
|
|
|
|
|
|
|
if (!has_model)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %ffF", (double)sdM *
|
|
|
|
|
|
(double)(dev->dev_cap));
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, " w=%gu l=%gu",
|
|
|
|
|
|
w * scale * esScale,
|
|
|
|
|
|
l * scale * esScale);
|
|
|
|
|
|
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
if (sdM != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, " M=%g", sdM);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_FET:
|
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
|
|
|
|
|
|
|
/* MOSFET is "Mnnn drain gate source [L=x W=x [attributes]]" */
|
|
|
|
|
|
|
|
|
|
|
|
spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
|
if (subnode)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " ");
|
|
|
|
|
|
subnodeFlat = spcdevSubstrate(hierName,
|
|
|
|
|
|
subnode->efnode_name->efnn_hier,
|
|
|
|
|
|
dev->dev_type, esSpiceF);
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 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 .spice file.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
sdM = getCurDevMult();
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, " w=%gu l=%gu",
|
|
|
|
|
|
w * scale * esScale,
|
|
|
|
|
|
l * scale * esScale);
|
|
|
|
|
|
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
|
if (sdM != 1.0)
|
|
|
|
|
|
fprintf(esSpiceF, " M=%g", sdM);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Check controlling attributes and output area and perimeter.
|
|
|
|
|
|
*/
|
|
|
|
|
|
hierS = extHierSDAttr(source);
|
|
|
|
|
|
hierD = extHierSDAttr(drain);
|
|
|
|
|
|
if ( gate->dterm_attrs )
|
|
|
|
|
|
subAP = Match(ATTR_SUBSAP, gate->dterm_attrs ) ;
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, "\n+ ");
|
|
|
|
|
|
if (hierD)
|
|
|
|
|
|
spcnAPHier(drain, hierName, esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, "ad", "pd", sdM, esSpiceF);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dnode = SpiceGetNode(hierName, drain->dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
spcnAP(dnode, esFetInfo[dev->dev_type].resClassSD, scale,
|
|
|
|
|
|
"ad", "pd", sdM, esSpiceF, w);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (hierS)
|
|
|
|
|
|
spcnAPHier(source, hierName, esFetInfo[dev->dev_type].resClassSD,
|
|
|
|
|
|
scale, "as", "ps", sdM, esSpiceF);
|
|
|
|
|
|
else {
|
|
|
|
|
|
snode= SpiceGetNode(hierName, source->dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
spcnAP(snode, esFetInfo[dev->dev_type].resClassSD, scale,
|
|
|
|
|
|
"as", "ps", sdM, esSpiceF, w);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (subAP)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, " * ");
|
|
|
|
|
|
if (esFetInfo[dev->dev_type].resClassSub < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("error: subap for devtype %d unspecified\n",
|
|
|
|
|
|
dev->dev_type);
|
|
|
|
|
|
fprintf(esSpiceF, "asub=0 psub=0");
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (subnodeFlat)
|
|
|
|
|
|
spcnAP(subnodeFlat, esFetInfo[dev->dev_type].resClassSub, scale,
|
|
|
|
|
|
"asub", "psub", sdM, esSpiceF, -1);
|
|
|
|
|
|
else
|
|
|
|
|
|
fprintf(esSpiceF, "asub=0 psub=0");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Now output attributes, if present */
|
|
|
|
|
|
if (!esNoAttrs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (gate->dterm_attrs || source->dterm_attrs || drain->dterm_attrs)
|
|
|
|
|
|
fprintf(esSpiceF,"\n**devattr");
|
|
|
|
|
|
if (gate->dterm_attrs)
|
|
|
|
|
|
fprintf(esSpiceF, " g=%s", gate->dterm_attrs);
|
|
|
|
|
|
if (source->dterm_attrs)
|
|
|
|
|
|
fprintf(esSpiceF, " s=%s", source->dterm_attrs);
|
|
|
|
|
|
if (drain->dterm_attrs)
|
|
|
|
|
|
fprintf(esSpiceF, " d=%s", drain->dterm_attrs);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(esSpiceF, "\n");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcdevSubstrate -
|
|
|
|
|
|
*
|
|
|
|
|
|
* Output the node name of the substrate of a dev. If the suffix is the
|
|
|
|
|
|
* same as the default dont go looking for it just output the default
|
|
|
|
|
|
* (trimmed appropriately). Otherwise look it up ..
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* NULL if not found or the default substrate or the node pointer
|
|
|
|
|
|
* otherwise (might be reused to output area and perimeter of
|
|
|
|
|
|
* the substrate).
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Might allocate the nodeClient for the node through nodeSpiceName.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
EFNode *spcdevSubstrate( prefix, suffix, type, outf)
|
|
|
|
|
|
HierName *prefix, *suffix;
|
|
|
|
|
|
int type;
|
|
|
|
|
|
FILE *outf;
|
|
|
|
|
|
{
|
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
char *suf;
|
|
|
|
|
|
|
|
|
|
|
|
suf = EFHNToStr(suffix);
|
|
|
|
|
|
if (esFetInfo[type].defSubs && strcasecmp(suf,esFetInfo[type].defSubs) == 0) {
|
|
|
|
|
|
esFormatSubs(outf, suf);
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
he = EFHNConcatLook(prefix, suffix, "substrate");
|
|
|
|
|
|
if (he == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (outf)
|
|
|
|
|
|
fprintf(outf, "errGnd!");
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Canonical name */
|
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
if (outf)
|
2019-10-29 14:44:28 +01:00
|
|
|
|
fprintf(outf, "%s", nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier,
|
|
|
|
|
|
NULL));
|
2017-10-20 22:07:52 +02:00
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
/* Mark node as visited */
|
2017-10-20 22:07:52 +02:00
|
|
|
|
if ((nodeClient *)nn->efnn_node->efnode_client == (ClientData)NULL)
|
|
|
|
|
|
initNodeClientHier(nn->efnn_node);
|
|
|
|
|
|
|
2019-10-14 19:49:41 +02:00
|
|
|
|
if (!esDistrJunct)
|
|
|
|
|
|
((nodeClient *)nn->efnn_node->efnode_client)->m_w.visitMask |=
|
|
|
|
|
|
DEV_CONNECT_MASK;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
return nn->efnn_node;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcnAP, spcnAPHier --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Output the area perimeter of the node with type type if it has not
|
|
|
|
|
|
* been visited.
|
|
|
|
|
|
* The spcnAPHier version outputs the area and perimeter only within the
|
|
|
|
|
|
* local subcell with hierarchical name hierName.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Return:
|
|
|
|
|
|
* 0 on success, 1 on error
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Set the visited flags so that the node A/P will not be output multiple
|
|
|
|
|
|
* times
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
int spcnAP(node, resClass, scale, asterm, psterm, m, outf, w)
|
|
|
|
|
|
EFNode *node;
|
|
|
|
|
|
int resClass;
|
|
|
|
|
|
float scale, m;
|
|
|
|
|
|
char *asterm, *psterm;
|
|
|
|
|
|
FILE *outf;
|
|
|
|
|
|
int w;
|
|
|
|
|
|
{
|
|
|
|
|
|
char afmt[15], pfmt[15];
|
|
|
|
|
|
float dsc;
|
|
|
|
|
|
|
|
|
|
|
|
if ((node == NULL) || (node->efnode_client == (ClientData)NULL))
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("spcnAP: major internal inconsistency\n");
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (asterm) sprintf(afmt, " %s=%%g", asterm);
|
|
|
|
|
|
if (psterm) sprintf(pfmt, " %s=%%g", psterm);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (asterm) sprintf(afmt, " %s=%%gp", asterm);
|
|
|
|
|
|
if (psterm) sprintf(pfmt, " %s=%%gu", psterm);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!esDistrJunct || w == -1) goto oldFmt;
|
|
|
|
|
|
|
|
|
|
|
|
dsc = w / ((nodeClient*)node->efnode_client)->m_w.widths[resClass];
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (asterm)
|
|
|
|
|
|
fprintf(outf, afmt,
|
|
|
|
|
|
node->efnode_pa[resClass].pa_area * scale * scale * dsc);
|
|
|
|
|
|
if (psterm)
|
|
|
|
|
|
fprintf(outf, pfmt,
|
|
|
|
|
|
node->efnode_pa[resClass].pa_perim * scale * dsc);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (asterm)
|
|
|
|
|
|
fprintf(outf, afmt,
|
|
|
|
|
|
((float)node->efnode_pa[resClass].pa_area * scale * scale)
|
|
|
|
|
|
* esScale * esScale * dsc);
|
|
|
|
|
|
if (psterm)
|
|
|
|
|
|
fprintf(outf, pfmt,
|
|
|
|
|
|
((float)node->efnode_pa[resClass].pa_perim * scale)
|
|
|
|
|
|
* esScale * dsc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
oldFmt:
|
|
|
|
|
|
if (resClass == NO_RESCLASS ||
|
|
|
|
|
|
beenVisited((nodeClient *)node->efnode_client, resClass))
|
|
|
|
|
|
scale = 0;
|
|
|
|
|
|
else
|
|
|
|
|
|
markVisited((nodeClient *)node->efnode_client, resClass);
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (asterm)
|
|
|
|
|
|
fprintf(outf, afmt,
|
|
|
|
|
|
node->efnode_pa[resClass].pa_area * scale * scale / m);
|
|
|
|
|
|
if (psterm)
|
|
|
|
|
|
fprintf(outf, pfmt,
|
|
|
|
|
|
node->efnode_pa[resClass].pa_perim * scale / m);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (asterm)
|
|
|
|
|
|
fprintf(outf, afmt,
|
|
|
|
|
|
((float)node->efnode_pa[resClass].pa_area * scale * scale)
|
|
|
|
|
|
* esScale * esScale);
|
|
|
|
|
|
if (psterm)
|
|
|
|
|
|
fprintf(outf, pfmt,
|
|
|
|
|
|
((float)node->efnode_pa[resClass].pa_perim * scale)
|
|
|
|
|
|
* esScale);
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int spcnAPHier(dterm, hierName, resClass, scale, asterm, psterm, m, outf)
|
|
|
|
|
|
DevTerm *dterm;
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
int resClass;
|
|
|
|
|
|
float scale, m;
|
|
|
|
|
|
char *asterm, *psterm;
|
|
|
|
|
|
FILE *outf;
|
|
|
|
|
|
{
|
|
|
|
|
|
EFNode *node = dterm->dterm_node;
|
|
|
|
|
|
nodeClientHier *nc;
|
|
|
|
|
|
char afmt[15], pfmt[15];
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
sprintf(afmt," %s=%%g", asterm);
|
|
|
|
|
|
sprintf(pfmt," %s=%%g", psterm);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
sprintf(afmt," %s=%%gp", asterm);
|
|
|
|
|
|
sprintf(pfmt," %s=%%gu", psterm);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (node->efnode_client == (ClientData) NULL)
|
|
|
|
|
|
initNodeClientHier(node);
|
|
|
|
|
|
|
|
|
|
|
|
nc = (nodeClientHier *)node->efnode_client;
|
|
|
|
|
|
if (nc->lastPrefix != hierName)
|
|
|
|
|
|
{
|
|
|
|
|
|
clearVisited(nc);
|
|
|
|
|
|
nc->lastPrefix = hierName;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (resClass == NO_RESCLASS ||
|
|
|
|
|
|
beenVisited((nodeClientHier *)node->efnode_client, resClass) )
|
|
|
|
|
|
scale = 0.0;
|
|
|
|
|
|
else
|
|
|
|
|
|
markVisited((nodeClientHier *)node->efnode_client, resClass);
|
|
|
|
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(outf, afmt,
|
|
|
|
|
|
node->efnode_pa[resClass].pa_area * scale * scale / m);
|
|
|
|
|
|
fprintf(outf, pfmt,
|
|
|
|
|
|
node->efnode_pa[resClass].pa_perim * scale / m);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(outf, afmt,
|
|
|
|
|
|
((float)node->efnode_pa[resClass].pa_area * scale)
|
|
|
|
|
|
* esScale * esScale);
|
|
|
|
|
|
fprintf(outf, pfmt,
|
|
|
|
|
|
((float)node->efnode_pa[resClass].pa_perim * scale)
|
|
|
|
|
|
* esScale);
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcdevOutNode --
|
|
|
|
|
|
*
|
|
|
|
|
|
* 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:
|
|
|
|
|
|
* Return number of characters printed on success, 0 on error.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the file 'outf'.
|
|
|
|
|
|
* Sets the efnode_client field as described above.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
spcdevOutNode(prefix, suffix, name, outf)
|
|
|
|
|
|
HierName *prefix;
|
|
|
|
|
|
HierName *suffix;
|
|
|
|
|
|
char *name;
|
|
|
|
|
|
FILE *outf;
|
|
|
|
|
|
{
|
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
char *nname;
|
|
|
|
|
|
|
|
|
|
|
|
he = EFHNConcatLook(prefix, suffix, name);
|
|
|
|
|
|
if (he == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(outf, " errGnd!");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
2019-10-29 14:44:28 +01:00
|
|
|
|
nname = nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
fprintf(outf, " %s", nname);
|
2019-10-14 19:49:41 +02:00
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
/* Mark node as visited */
|
2019-10-14 19:49:41 +02:00
|
|
|
|
if (!esDistrJunct)
|
|
|
|
|
|
((nodeClient *)nn->efnn_node->efnode_client)->m_w.visitMask |= DEV_CONNECT_MASK;
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
return (1 + strlen(nname));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spccapVisit --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Procedure to output a single capacitor to the .spice file.
|
|
|
|
|
|
* Called by EFVisitCaps().
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the file esSpiceF. Increments esCapNum.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Format of a .spice cap line:
|
|
|
|
|
|
*
|
|
|
|
|
|
* C%d node1 node2 cap
|
|
|
|
|
|
*
|
|
|
|
|
|
* where
|
|
|
|
|
|
* node1, node2 are the terminals of the capacitor
|
|
|
|
|
|
* cap is the capacitance in femtofarads (NOT attofarads).
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
spccapVisit(hierName1, hierName2, cap)
|
|
|
|
|
|
HierName *hierName1;
|
|
|
|
|
|
HierName *hierName2;
|
|
|
|
|
|
double cap;
|
|
|
|
|
|
{
|
|
|
|
|
|
cap = cap / 1000;
|
|
|
|
|
|
if (cap <= EFCapThreshold)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
2019-10-29 14:44:28 +01:00
|
|
|
|
fprintf(esSpiceF, esSpiceCapFormat ,esCapNum++,nodeSpiceName(hierName1, NULL),
|
|
|
|
|
|
nodeSpiceName(hierName2, NULL), cap);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcresistVisit --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Procedure to output a single resistor to the .spice file.
|
|
|
|
|
|
* Called by EFVisitResists().
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the file esSpiceF. Increments esResNum.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Format of a .spice resistor line:
|
|
|
|
|
|
*
|
|
|
|
|
|
* R%d node1 node2 res
|
|
|
|
|
|
*
|
|
|
|
|
|
* where
|
|
|
|
|
|
* node1, node2 are the terminals of the resistor
|
|
|
|
|
|
* res is the resistance in ohms (NOT milliohms)
|
|
|
|
|
|
*
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
int
|
|
|
|
|
|
spcresistVisit(hierName1, hierName2, res)
|
|
|
|
|
|
HierName *hierName1;
|
|
|
|
|
|
HierName *hierName2;
|
|
|
|
|
|
float res;
|
|
|
|
|
|
{
|
2019-10-29 14:44:28 +01:00
|
|
|
|
fprintf(esSpiceF, "R%d %s %s %g\n", esResNum++, nodeSpiceName(hierName1, NULL),
|
|
|
|
|
|
nodeSpiceName(hierName2, NULL), res / 1000.);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcsubVisit --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Routine to find the node that connects to substrate. Copy the
|
|
|
|
|
|
* string name of this node into "resstr" to be returned to the
|
|
|
|
|
|
* caller.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Return 1 if the substrate node has been found, to stop the search.
|
|
|
|
|
|
* Otherwise return 0 to keep the search going.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
spcsubVisit(node, res, cap, resstr)
|
|
|
|
|
|
EFNode *node;
|
|
|
|
|
|
int res; // Unused
|
|
|
|
|
|
double cap; // Unused
|
|
|
|
|
|
char **resstr;
|
|
|
|
|
|
{
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
char *nsn;
|
|
|
|
|
|
|
|
|
|
|
|
if (node->efnode_flags & EF_SUBS_NODE)
|
|
|
|
|
|
{
|
|
|
|
|
|
hierName = (HierName *) node->efnode_name->efnn_hier;
|
2019-10-29 14:44:28 +01:00
|
|
|
|
nsn = nodeSpiceName(hierName, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
*resstr = StrDup((char **)NULL, nsn);
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* spcnodeVisit --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Procedure to output a single node to the .spice file along with its
|
|
|
|
|
|
* attributes and its dictionary (if present). Called by EFVisitNodes().
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the files esSpiceF
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
spcnodeVisit(node, res, cap)
|
|
|
|
|
|
EFNode *node;
|
|
|
|
|
|
int res;
|
|
|
|
|
|
double cap;
|
|
|
|
|
|
{
|
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
bool isConnected = FALSE;
|
|
|
|
|
|
char *fmt, *nsn;
|
|
|
|
|
|
EFAttr *ap;
|
|
|
|
|
|
|
|
|
|
|
|
if (node->efnode_client)
|
|
|
|
|
|
{
|
|
|
|
|
|
isConnected = (esDistrJunct) ?
|
|
|
|
|
|
(((nodeClient *)node->efnode_client)->m_w.widths != NULL) :
|
|
|
|
|
|
((((nodeClient *)node->efnode_client)->m_w.visitMask
|
|
|
|
|
|
& DEV_CONNECT_MASK) != 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!isConnected && esDevNodesOnly)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Don't mark known ports as "FLOATING" nodes */
|
|
|
|
|
|
if (!isConnected && node->efnode_flags & EF_PORT) isConnected = TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
hierName = (HierName *) node->efnode_name->efnn_hier;
|
2019-10-29 14:44:28 +01:00
|
|
|
|
nsn = nodeSpiceName(hierName, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
if (esFormat == SPICE2 || esFormat == HSPICE && strncmp(nsn, "z@", 2)==0 ) {
|
|
|
|
|
|
static char ntmp[MAX_STR_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
EFHNSprintf(ntmp, hierName);
|
|
|
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
|
|
|
|
|
fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
|
|
|
|
|
|
}
|
|
|
|
|
|
cap = cap / 1000;
|
|
|
|
|
|
if (cap > EFCapThreshold)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, esSpiceCapFormat, esCapNum++, nsn, cap,
|
|
|
|
|
|
(isConnected) ? "\n" :
|
|
|
|
|
|
(esFormat == NGSPICE) ? " ; **FLOATING\n" :
|
|
|
|
|
|
" **FLOATING\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (node->efnode_attrs && !esNoAttrs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, " ; ");
|
|
|
|
|
|
fprintf(esSpiceF, "**nodeattr %s :",nsn );
|
|
|
|
|
|
for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
|
|
|
|
|
|
{
|
|
|
|
|
|
fprintf(esSpiceF, fmt, ap->efa_text);
|
|
|
|
|
|
fmt = ",%s";
|
|
|
|
|
|
}
|
|
|
|
|
|
putc('\n', esSpiceF);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* a debugging procedure */
|
|
|
|
|
|
int
|
|
|
|
|
|
nodeVisitDebug(node, res, cap)
|
|
|
|
|
|
EFNode *node;
|
|
|
|
|
|
int res;
|
|
|
|
|
|
double cap;
|
|
|
|
|
|
{
|
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
char *fmt, *nsn;
|
|
|
|
|
|
EFAttr *ap;
|
|
|
|
|
|
|
|
|
|
|
|
hierName = (HierName *) node->efnode_name->efnn_hier;
|
2019-10-29 14:44:28 +01:00
|
|
|
|
nsn = nodeSpiceName(hierName, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
TxError("** %s (%x)\n", nsn, node);
|
|
|
|
|
|
|
|
|
|
|
|
printf("\t client.name=%s, client.m_w=%p\n",
|
|
|
|
|
|
((nodeClient *)node->efnode_client)->spiceNodeName,
|
|
|
|
|
|
((nodeClient *)node->efnode_client)->m_w.widths);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* nodeSpiceName --
|
|
|
|
|
|
* Find the real spice name for the node with hierarchical name hname.
|
|
|
|
|
|
* SPICE2 ==> numeric
|
|
|
|
|
|
* SPICE3 ==> full magic path
|
|
|
|
|
|
* HSPICE ==> less than 15 characters long
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Returns the spice node name.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Allocates nodeClients for the node.
|
2019-10-29 14:44:28 +01:00
|
|
|
|
* Returns the node in the "rnode" pointer, if non-NULL.
|
2017-04-25 14:41:48 +02:00
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
static char esTempName[MAX_STR_SIZE];
|
|
|
|
|
|
|
2019-10-29 14:44:28 +01:00
|
|
|
|
char *nodeSpiceName(hname, rnode)
|
2017-04-25 14:41:48 +02:00
|
|
|
|
HierName *hname;
|
2019-10-29 14:44:28 +01:00
|
|
|
|
EFNode **rnode;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
EFNode *node;
|
|
|
|
|
|
|
2019-10-29 14:44:28 +01:00
|
|
|
|
if (rnode) *rnode = (EFNode *)NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
he = EFHNLook(hname, (char *) NULL, "nodeName");
|
|
|
|
|
|
if ( he == NULL )
|
|
|
|
|
|
return "errGnd!";
|
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
node = nn->efnn_node;
|
2019-10-29 14:44:28 +01:00
|
|
|
|
if (rnode) *rnode = node;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
if ( (nodeClient *) (node->efnode_client) == NULL ) {
|
|
|
|
|
|
initNodeClient(node);
|
|
|
|
|
|
goto makeName;
|
|
|
|
|
|
} else if ( ((nodeClient *) (node->efnode_client))->spiceNodeName == NULL)
|
|
|
|
|
|
goto makeName;
|
|
|
|
|
|
else goto retName;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
makeName:
|
|
|
|
|
|
if ( esFormat == SPICE2 )
|
|
|
|
|
|
sprintf(esTempName, "%d", esNodeNum++);
|
|
|
|
|
|
else {
|
|
|
|
|
|
EFHNSprintf(esTempName, node->efnode_name->efnn_hier);
|
|
|
|
|
|
if ( esFormat == HSPICE ) /* more processing */
|
|
|
|
|
|
nodeHspiceName(esTempName);
|
|
|
|
|
|
}
|
|
|
|
|
|
((nodeClient *) (node->efnode_client))->spiceNodeName =
|
|
|
|
|
|
StrDup(NULL, esTempName);
|
|
|
|
|
|
|
|
|
|
|
|
retName:
|
|
|
|
|
|
return ((nodeClient *) (node->efnode_client))->spiceNodeName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* EFHNSprintf --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Create a hierarchical node name.
|
|
|
|
|
|
* The flags in EFTrimFlags control whether global (!) or local (#)
|
|
|
|
|
|
* suffixes are to be trimmed. Also substitutes \. with \@ if the
|
|
|
|
|
|
* format is hspice.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Changes the area pointed to by str
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
EFHNSprintf(str, hierName)
|
|
|
|
|
|
char *str;
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
{
|
2019-10-15 03:46:07 +02:00
|
|
|
|
bool trimGlob, trimLocal, convertComma, convertEqual, convertBrackets;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
char *s, *cp, c;
|
|
|
|
|
|
char *efHNSprintfPrefix(HierName *, char *);
|
|
|
|
|
|
|
|
|
|
|
|
s = str;
|
|
|
|
|
|
if (hierName->hn_parent) str = efHNSprintfPrefix(hierName->hn_parent, str);
|
|
|
|
|
|
if (EFTrimFlags)
|
|
|
|
|
|
{
|
|
|
|
|
|
cp = hierName->hn_name;
|
|
|
|
|
|
trimGlob = (EFTrimFlags & EF_TRIMGLOB);
|
|
|
|
|
|
trimLocal = (EFTrimFlags & EF_TRIMLOCAL);
|
2019-10-15 03:46:07 +02:00
|
|
|
|
convertComma = (EFTrimFlags & EF_CONVERTCOMMA);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
convertEqual = (EFTrimFlags & EF_CONVERTEQUAL);
|
2019-10-15 03:46:07 +02:00
|
|
|
|
convertBrackets = (EFTrimFlags & EF_CONVERTBRACKETS);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
while (c = *cp++)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (c)
|
|
|
|
|
|
{
|
|
|
|
|
|
case '!': if (!trimGlob) *str++ = c; break;
|
|
|
|
|
|
case '.': *str++ = (esFormat == HSPICE)?'@':'.'; break;
|
|
|
|
|
|
case '=': if (convertEqual) *str++ = ':'; break;
|
2019-10-15 03:46:07 +02:00
|
|
|
|
case ',': if (convertComma) *str++ = '|'; break;
|
|
|
|
|
|
case '[': *str++ = (convertBrackets) ? '_' : '['; break;
|
|
|
|
|
|
case ']': *str++ = (convertBrackets) ? '_' : ']'; break;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
case '#': if (trimLocal) break; // else fall through
|
|
|
|
|
|
default: *str++ = c; break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*str++ = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
else strcpy(str, hierName->hn_name);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *efHNSprintfPrefix(hierName, str)
|
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
|
char *str;
|
|
|
|
|
|
{
|
|
|
|
|
|
char *cp, c;
|
|
|
|
|
|
bool convertEqual = (EFTrimFlags & EF_CONVERTEQUAL) ? TRUE : FALSE;
|
2019-10-15 03:46:07 +02:00
|
|
|
|
bool convertComma = (EFTrimFlags & EF_CONVERTCOMMA) ? TRUE : FALSE;
|
|
|
|
|
|
bool convertBrackets = (EFTrimFlags & EF_CONVERTBRACKETS) ? TRUE : FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
if (hierName->hn_parent)
|
|
|
|
|
|
str = efHNSprintfPrefix(hierName->hn_parent, str);
|
|
|
|
|
|
|
|
|
|
|
|
cp = hierName->hn_name;
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
if (convertEqual && (*cp == '='))
|
|
|
|
|
|
*str = ':';
|
2019-10-15 03:46:07 +02:00
|
|
|
|
else if (convertBrackets && ((*cp == '[') || (*cp == ']')))
|
|
|
|
|
|
*str = '_';
|
|
|
|
|
|
else if (*cp == ',')
|
|
|
|
|
|
{
|
|
|
|
|
|
if (convertComma) *str = '|';
|
|
|
|
|
|
else str--;
|
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
else
|
|
|
|
|
|
*str = *cp;
|
|
|
|
|
|
if (!(*str)) break;
|
|
|
|
|
|
str++;
|
|
|
|
|
|
cp++;
|
|
|
|
|
|
}
|
|
|
|
|
|
*str = '/';
|
|
|
|
|
|
return ++str;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* nodeHspiceName --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Convert the hierarchical node name used in Berkeley spice
|
2018-12-12 23:23:35 +01:00
|
|
|
|
* to a name understood by hspice and hopefully by the user.
|
2017-04-25 14:41:48 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
2018-12-12 23:23:35 +01:00
|
|
|
|
* A somewhat meaningful node name
|
2017-04-25 14:41:48 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Mucks with the hash table above.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int nodeHspiceName(s)
|
|
|
|
|
|
char *s;
|
|
|
|
|
|
{
|
|
|
|
|
|
char *p, *sf;
|
|
|
|
|
|
int l, snum = -1;
|
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
static char map[MAX_STR_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* find the suffix
|
|
|
|
|
|
*/
|
|
|
|
|
|
l = strlen(s);
|
|
|
|
|
|
for (p = s + l; (p > s) && *p != '/'; p--);
|
|
|
|
|
|
if (p == s)
|
|
|
|
|
|
{
|
|
|
|
|
|
strcpy(map, s);
|
|
|
|
|
|
goto topLevel;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* break it into prefix '/0' suffix
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (*p == '/')
|
|
|
|
|
|
*p = 0;
|
|
|
|
|
|
sf = p + 1;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2018-12-12 23:23:35 +01:00
|
|
|
|
* look up prefix in the hash table and create it if doesn't exist
|
2017-04-25 14:41:48 +02:00
|
|
|
|
*/
|
|
|
|
|
|
if ((he = HashLookOnly(&subcktNameTable, s)) == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
snum = esSbckNum++;
|
|
|
|
|
|
he = HashFind(&subcktNameTable, s);
|
|
|
|
|
|
HashSetValue(he, (ClientData)(pointertype) snum);
|
|
|
|
|
|
#ifndef UNSORTED_SUBCKT
|
|
|
|
|
|
DQPushRear(&subcktNameQueue, he);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
snum = (spointertype) HashGetValue(he);
|
|
|
|
|
|
sprintf(map, "x%d/%s", snum, sf);
|
|
|
|
|
|
|
|
|
|
|
|
topLevel:
|
|
|
|
|
|
strcpy(s, map);
|
|
|
|
|
|
if (strlen(s) > 15)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* still hspice does not get it */
|
|
|
|
|
|
sprintf(s, "z@%d", esNodeNum++);
|
|
|
|
|
|
if (strlen(s) > 15)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* screw it: hspice will not work */
|
|
|
|
|
|
TxError("Error: too many nodes in this circuit to be "
|
|
|
|
|
|
"output as names\n");
|
|
|
|
|
|
TxError(" use spice2 format or call and complain "
|
|
|
|
|
|
"to Meta software about their stupid parser\n");
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
return TCL_ERROR;
|
|
|
|
|
|
#else
|
|
|
|
|
|
exit(1);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* printSubcktDict --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Print out the hspice subcircuit dictionary. Ideally this should go to a
|
|
|
|
|
|
* pa0 file but uncfortunately hspice crashes if the node names contain
|
|
|
|
|
|
* dots so we just append it at the end of the spice file
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Writes to the output file
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int printSubcktDict()
|
|
|
|
|
|
{
|
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF,"\n** hspice subcircuit dictionary\n");
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef UNSORTED_SUBCKT
|
|
|
|
|
|
while ((he = (HashEntry *)DQPopFront(&subcktNameQueue)) != NULL)
|
|
|
|
|
|
#else
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
|
while ((he = HashNext(&subcktNameTable, &hs)) != NULL)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
fprintf(esSpiceF,"* x%"DLONG_PREFIX"d\t%s\n", (dlong) HashGetValue(he), he->h_key.h_name);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* mkDevMerge --
|
|
|
|
|
|
* Create a new devMerge structure.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* Obvious
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Allocates memory and sets the fields of the structure.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
devMerge *mkDevMerge(l, w, g, s, d, b, hn, dev)
|
|
|
|
|
|
float l, w;
|
|
|
|
|
|
EFNode *g, *s, *d, *b;
|
|
|
|
|
|
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 --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Determine if two devs are in parallel
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* NOT_PARALLEL if not in parallel
|
|
|
|
|
|
* PARALLEL if parallel and an exact match
|
|
|
|
|
|
* ANTIPARALLEL if parallel but reversed source<->drain nodes
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* None.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
parallelDevs(f1, f2)
|
|
|
|
|
|
devMerge *f1, *f2;
|
|
|
|
|
|
{
|
|
|
|
|
|
/* If the devices are not in the same class, then */
|
|
|
|
|
|
/* they cannot be parallel. */
|
|
|
|
|
|
|
|
|
|
|
|
if (f1->dev->dev_class != f2->dev->dev_class)
|
|
|
|
|
|
return NOT_PARALLEL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Can't merge devices with different models */
|
|
|
|
|
|
if (f1->dev->dev_type != f2->dev->dev_type)
|
|
|
|
|
|
return NOT_PARALLEL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Class-dependent action */
|
|
|
|
|
|
switch(f1->dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
|
case DEV_FET:
|
|
|
|
|
|
|
|
|
|
|
|
if (f1->b != f2->b) return NOT_PARALLEL;
|
|
|
|
|
|
if ((f1->g == f2->g) && (f1->l == f2->l)
|
|
|
|
|
|
&& (esMergeDevsA || (f1->w == f2->w)))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((f1->d == f2->d) && (f1->s == f2->s))
|
|
|
|
|
|
return PARALLEL;
|
|
|
|
|
|
else if ((f1->s == f2->d) && (f1->d == f2->s))
|
|
|
|
|
|
return ANTIPARALLEL;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
|
|
|
|
|
|
|
if (f1->b != f2->b) return NOT_PARALLEL;
|
|
|
|
|
|
if ((f1->g == f2->g) && (f1->d == f2->d) && (f1->s == f2->s)
|
|
|
|
|
|
&& (f1->l == f2->l) && (esMergeDevsA || (f1->w == f2->w)))
|
|
|
|
|
|
{
|
|
|
|
|
|
return PARALLEL;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* Capacitors match if top ("gate") and bottom ("source") are */
|
|
|
|
|
|
/* the same. Do not attempt to swap top and bottom, as we do */
|
|
|
|
|
|
/* not know when it is safe to do so. */
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
|
if ((f1->g != f2->g) || (f1->s != f2->s))
|
|
|
|
|
|
return NOT_PARALLEL;
|
|
|
|
|
|
|
|
|
|
|
|
else if (f1->dev->dev_type == esNoModelType)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Unmodeled capacitor */
|
|
|
|
|
|
if (esMergeDevsA || (f1->dev->dev_cap == f2->dev->dev_cap))
|
|
|
|
|
|
return PARALLEL;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (esMergeDevsA || ((f1->l == f2->l) && (f1->w == f2->w)))
|
|
|
|
|
|
return PARALLEL;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* We can't merge resistors because we accumulate capacitance */
|
|
|
|
|
|
/* on the central ("gate") node. Merging the devices would */
|
|
|
|
|
|
/* cause nodes to disappear. */
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* For the remaining devices, it might be possible to merge */
|
|
|
|
|
|
/* if we know that the device model level accepts length and */
|
|
|
|
|
|
/* width parameters. However, at this time, no such */
|
|
|
|
|
|
/* information is passed to the SPICE deck, so we don't try to */
|
|
|
|
|
|
/* merge these devices. */
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_BJT:
|
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* There is no way to merge subcircuit devices */
|
|
|
|
|
|
|
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
return NOT_PARALLEL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* mergeAttr --
|
|
|
|
|
|
*
|
|
|
|
|
|
* merge two attribute strings
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* The merged strings
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* Might allocate and free memory.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
mergeAttr(a1, a2)
|
|
|
|
|
|
char **a1, **a2;
|
|
|
|
|
|
{
|
|
|
|
|
|
if (*a1 == NULL)
|
|
|
|
|
|
*a1 = *a2;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
char *t;
|
|
|
|
|
|
int l1 = strlen(*a1);
|
|
|
|
|
|
int l2 = strlen(*a2);
|
|
|
|
|
|
t = (char *) mallocMagic((unsigned int)((l1 + l2) + 1));
|
|
|
|
|
|
t = (char *) strcat(*a1, *a2);
|
|
|
|
|
|
freeMagic(*a1);
|
|
|
|
|
|
*a1 = t;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* devMergeVisit --
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
devMergeVisit(dev, hierName, scale, trans)
|
|
|
|
|
|
Dev *dev; /* Dev to examine */
|
|
|
|
|
|
HierName *hierName; /* Hierarchical path down to this dev */
|
|
|
|
|
|
float scale; /* Scale transform */
|
|
|
|
|
|
Transform *trans; /* (unused) */
|
|
|
|
|
|
{
|
|
|
|
|
|
DevTerm *gate, *source, *drain;
|
|
|
|
|
|
Dev *cf;
|
|
|
|
|
|
DevTerm *cg, *cs, *cd;
|
|
|
|
|
|
EFNode *subnode, *snode, *dnode, *gnode;
|
|
|
|
|
|
int pmode, l, w;
|
|
|
|
|
|
bool hS, hD, chS, chD;
|
|
|
|
|
|
devMerge *fp, *cfp;
|
|
|
|
|
|
float m;
|
|
|
|
|
|
|
|
|
|
|
|
if (esDistrJunct)
|
|
|
|
|
|
devDistJunctVisit(dev, hierName, scale, trans);
|
|
|
|
|
|
|
|
|
|
|
|
if (dev->dev_nterm < 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("outPremature\n");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
|
|
|
|
|
|
|
gnode = SpiceGetNode(hierName, gate->dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
if (dev->dev_nterm >= 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
snode = SpiceGetNode(hierName, source->dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
dnode = SpiceGetNode(hierName, drain->dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (dev->dev_subsnode)
|
|
|
|
|
|
subnode = spcdevSubstrate(hierName,
|
|
|
|
|
|
dev->dev_subsnode->efnode_name->efnn_hier,
|
|
|
|
|
|
dev->dev_type, NULL);
|
|
|
|
|
|
else
|
|
|
|
|
|
subnode = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Get length and width of the device */
|
|
|
|
|
|
EFGetLengthAndWidth(dev, &l, &w);
|
|
|
|
|
|
|
|
|
|
|
|
fp = mkDevMerge((float)((float)l * scale), (float)((float)w * scale),
|
|
|
|
|
|
gnode, snode, dnode, subnode, hierName, dev);
|
|
|
|
|
|
hS = extHierSDAttr(source);
|
|
|
|
|
|
hD = extHierSDAttr(drain);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* run the list of devs. compare the current one with
|
|
|
|
|
|
* each one in the list. if they fullfill the matching requirements
|
|
|
|
|
|
* merge them only if:
|
|
|
|
|
|
* 1) they have both apf S, D attributes
|
|
|
|
|
|
* or
|
|
|
|
|
|
* 2) one of them has aph S, D attributes and they have the same
|
|
|
|
|
|
* hierarchical prefix
|
|
|
|
|
|
* If one of them has apf and the other aph print a warning.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
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 == ANTIPARALLEL)
|
|
|
|
|
|
cs = &cfp->dev->dev_terms[2];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
chS = extHierSDAttr(cs); chD = extHierSDAttr(cd);
|
|
|
|
|
|
if (!(chS || chD || hS || hD)) /* all flat S, D */
|
|
|
|
|
|
goto mergeThem;
|
|
|
|
|
|
if (cfp->hierName != hierName &&
|
|
|
|
|
|
((chS && !hS) || (chD && !hD) ||
|
|
|
|
|
|
(!chS && hS) || (!chD && hD)))
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
efHNSprintfPrefix((cfp->hierName)?cfp->hierName:hierName,
|
|
|
|
|
|
esTempName);
|
|
|
|
|
|
TxError("Warning: conflicting SD attributes of parallel"
|
|
|
|
|
|
" devs in cell: %s\n", esTempName);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (cfp->hierName == hierName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (hS && !chS)
|
|
|
|
|
|
{
|
|
|
|
|
|
mergeAttr(&cs->dterm_attrs, &source->dterm_attrs);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (hD && !chD)
|
|
|
|
|
|
{
|
|
|
|
|
|
mergeAttr(&cd->dterm_attrs, &drain->dterm_attrs);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else /* cfp->hierName != hierName */
|
|
|
|
|
|
break;
|
|
|
|
|
|
mergeThem:
|
|
|
|
|
|
switch(dev->dev_class)
|
|
|
|
|
|
{
|
|
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
|
case DEV_FET:
|
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->w / cfp->w);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DEV_RSUBCKT:
|
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
|
if (fp->dev->dev_type == esNoModelType)
|
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->dev->dev_res
|
|
|
|
|
|
/ cfp->dev->dev_res);
|
|
|
|
|
|
else
|
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->l / cfp->l);
|
|
|
|
|
|
break;
|
2018-10-30 21:19:20 +01:00
|
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
|
case DEV_CAP:
|
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
|
if (fp->dev->dev_type == esNoModelType)
|
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->dev->dev_cap
|
|
|
|
|
|
/ cfp->dev->dev_cap);
|
|
|
|
|
|
else
|
|
|
|
|
|
m = esFMult[cfp->esFMIndex] +
|
|
|
|
|
|
((fp->l * fp->w) / (cfp->l * cfp->w));
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
setDevMult(fp->esFMIndex, DEV_KILLED);
|
|
|
|
|
|
setDevMult(cfp->esFMIndex, m);
|
|
|
|
|
|
esSpiceDevsMerged++;
|
|
|
|
|
|
/* Need to do attribute stuff here */
|
|
|
|
|
|
freeMagic(fp);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* No parallel devs to it yet */
|
|
|
|
|
|
fp->next = devMergeList;
|
|
|
|
|
|
devMergeList = fp;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* update_w --
|
|
|
|
|
|
* Updates the width client of node n with the current dev width
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* N/A
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* might allocate node client and widths
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
update_w(resClass, w, n)
|
|
|
|
|
|
short resClass;
|
|
|
|
|
|
int w;
|
|
|
|
|
|
EFNode *n;
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeClient *nc;
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
if (n->efnode_client == (ClientData)NULL)
|
|
|
|
|
|
initNodeClient(n);
|
|
|
|
|
|
nc = (nodeClient *) n->efnode_client;
|
|
|
|
|
|
if (nc->m_w.widths == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
(nc->m_w.widths) = (float *)mallocMagic((unsigned)sizeof(float)
|
|
|
|
|
|
* efNumResistClasses);
|
|
|
|
|
|
for (i = 0; i < EFDevNumTypes; i++)
|
|
|
|
|
|
nc->m_w.widths[i] = 0.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
nc->m_w.widths[resClass] += (float)w;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* devDistJunctVisit --
|
|
|
|
|
|
* Called for every dev and updates the nodeclients of its terminals
|
|
|
|
|
|
*
|
|
|
|
|
|
* Results:
|
|
|
|
|
|
* 0 to keep the calling procedure going
|
|
|
|
|
|
*
|
|
|
|
|
|
* Side effects:
|
|
|
|
|
|
* calls update_w which might allocate stuff
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
devDistJunctVisit(dev, hierName, scale, trans)
|
|
|
|
|
|
Dev *dev; /* Dev to examine */
|
|
|
|
|
|
HierName *hierName; /* Hierarchical path down to this dev */
|
|
|
|
|
|
float scale; /* Scale transform */
|
|
|
|
|
|
Transform *trans; /* (unused) */
|
|
|
|
|
|
{
|
|
|
|
|
|
EFNode *n;
|
|
|
|
|
|
int i;
|
|
|
|
|
|
int l, w;
|
|
|
|
|
|
|
|
|
|
|
|
if (dev->dev_nterm < 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("outPremature\n");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EFGetLengthAndWidth(dev, &l, &w);
|
2019-06-07 20:13:50 +02:00
|
|
|
|
w = (int)((float)w * scale);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
for (i = 1; i<dev->dev_nterm; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
n = SpiceGetNode(hierName,
|
|
|
|
|
|
dev->dev_terms[i].dterm_node->efnode_name->efnn_hier);
|
|
|
|
|
|
update_w(esFetInfo[dev->dev_type].resClassSD, w, n);
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|