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>
|
2024-09-30 00:00:00 +02:00
|
|
|
#include <stdint.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <stdlib.h> /* for atof() */
|
|
|
|
|
#include <string.h>
|
2024-10-04 19:59:39 +02:00
|
|
|
#include <strings.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <ctype.h>
|
2023-02-16 17:59:13 +01:00
|
|
|
#include <math.h> /* for fabs() */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#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"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "dbwind/dbwind.h" /* for DBWclientID */
|
|
|
|
|
#include "commands/commands.h" /* for module auto-load */
|
|
|
|
|
#include "textio/txcommands.h"
|
|
|
|
|
#include "extflat/extflat.h"
|
|
|
|
|
#include "extflat/EFint.h"
|
|
|
|
|
#include "extract/extract.h" /* for extDevTable */
|
|
|
|
|
#include "utils/runstats.h"
|
|
|
|
|
|
|
|
|
|
#include "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];
|
2025-07-24 15:20:12 +02:00
|
|
|
const char *esSpiceCapNode;
|
|
|
|
|
const char esSpiceDefaultGnd[] = "0";
|
2017-04-25 14:41:48 +02:00
|
|
|
char *spcesOutName = spcesDefaultOut;
|
|
|
|
|
FILE *esSpiceF = NULL;
|
|
|
|
|
float esScale = -1.0 ; /* negative if hspice the EFScale/100 otherwise */
|
|
|
|
|
|
|
|
|
|
unsigned short esFormat = SPICE3 ;
|
|
|
|
|
|
2021-07-11 03:13:24 +02:00
|
|
|
int esCapNum, esDevNum, esResNum, esDiodeNum, esVoltNum;
|
2017-04-25 14:41:48 +02:00
|
|
|
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) */
|
|
|
|
|
|
|
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* 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
|
2017-04-25 14:41:48 +02:00
|
|
|
* which also are meaningful.
|
|
|
|
|
*/
|
|
|
|
|
HashTable subcktNameTable ; /* the hash table itself */
|
2021-12-13 04:09:31 +01:00
|
|
|
DQueue subcktNameQueue ; /* q used to print it sorted at the end */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-05-27 22:13:06 +02:00
|
|
|
fetInfoList esFetInfo[TT_MAXTYPES];
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Record for keeping a list of global names */
|
|
|
|
|
|
|
|
|
|
typedef struct GLL *globalListPtr;
|
|
|
|
|
|
|
|
|
|
typedef struct GLL {
|
|
|
|
|
globalListPtr gll_next;
|
2020-05-23 23:13:14 +02:00
|
|
|
char *gll_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
} globalList;
|
|
|
|
|
|
2021-05-27 22:13:06 +02:00
|
|
|
TileTypeBitMask initMask; /* Used for device types, not tile types */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/*
|
2017-04-25 14:41:48 +02:00
|
|
|
*---------------------------------------------------------
|
2020-05-23 23:13:14 +02:00
|
|
|
* 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
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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 ;
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
#define atoCap(s) ((EFCapValue)atof(s))
|
|
|
|
|
|
|
|
|
|
extern void ESGenerateHierarchy(); /* forward reference */
|
|
|
|
|
|
2021-12-14 00:05:53 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* esFreeNodeClient ---
|
|
|
|
|
*
|
|
|
|
|
* Free the string spiceNodeName associated with the nodeClient
|
|
|
|
|
* record that ext2spice allocates per each node structure.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* 0 always.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Frees an allocated string.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
esFreeNodeClient(client)
|
|
|
|
|
nodeClient *client;
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
if (client != (nodeClient *)NULL)
|
2021-12-14 00:05:53 +01:00
|
|
|
if (client->spiceNodeName != NULL)
|
|
|
|
|
freeMagic((char *)client->spiceNodeName);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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;
|
2024-10-04 18:24:47 +02:00
|
|
|
if (((EFOutputFlags & EF_TRIMGLOB ) && suf[l] == '!') ||
|
|
|
|
|
((EFOutputFlags & EF_TRIMLOCAL) && suf[l] == '#'))
|
2017-04-25 14:41:48 +02:00
|
|
|
suf[l] = '\0' ;
|
2021-07-11 03:13:24 +02:00
|
|
|
if (EFOutputFlags & EF_CONVERTCOMMA)
|
2017-04-25 14:41:48 +02:00
|
|
|
while ((specchar = strchr(suf, ',')) != NULL)
|
2019-10-15 03:46:07 +02:00
|
|
|
*specchar = '|';
|
2021-07-11 03:13:24 +02:00
|
|
|
if (EFOutputFlags & EF_CONVERTBRACKETS)
|
2019-10-15 03:46:07 +02:00
|
|
|
{
|
|
|
|
|
while ((specchar = strchr(suf, '[')) != NULL)
|
|
|
|
|
*specchar = '_';
|
|
|
|
|
while ((specchar = strchr(suf, ']')) != NULL)
|
|
|
|
|
*specchar = '_';
|
|
|
|
|
}
|
2021-07-11 03:13:24 +02:00
|
|
|
if (EFOutputFlags & EF_CONVERTEQUAL)
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2024-10-21 10:19:15 +02:00
|
|
|
if (Tcl_InitStubs(interp, Tclmagic_InitStubsVersion, 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 */
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
#endif /* MAGIC_WRAPPER */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2021-07-11 03:13:24 +02:00
|
|
|
#define EXTTOSPC_SHORT 9
|
|
|
|
|
#define EXTTOSPC_SUBCIRCUITS 10
|
|
|
|
|
#define EXTTOSPC_HIERARCHY 11
|
|
|
|
|
#define EXTTOSPC_BLACKBOX 12
|
|
|
|
|
#define EXTTOSPC_RENUMBER 13
|
|
|
|
|
#define EXTTOSPC_MERGENAMES 14
|
|
|
|
|
#define EXTTOSPC_LVS 15
|
|
|
|
|
#define EXTTOSPC_HELP 16
|
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;
|
2024-10-10 21:16:09 +02:00
|
|
|
const char * const *msg;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *resstr = NULL;
|
|
|
|
|
char *substr = NULL;
|
|
|
|
|
bool err_result, locDoSubckt;
|
|
|
|
|
|
2020-09-11 23:29:12 +02:00
|
|
|
short s_rclass, d_rclass, sub_rclass;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *devname;
|
|
|
|
|
char *subname;
|
2020-10-20 18:22:02 +02:00
|
|
|
TileType devtype;
|
2017-04-25 14:41:48 +02:00
|
|
|
int idx, idx2;
|
|
|
|
|
globalList *glist = NULL;
|
|
|
|
|
|
|
|
|
|
static EFCapValue LocCapThreshold = 2;
|
2020-05-23 23:13:14 +02:00
|
|
|
static int LocResistThreshold = INFINITE_THRESHOLD;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const spiceFormats[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"SPICE2", "SPICE3", "HSPICE", "NGSPICE", NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const cmdExtToSpcOption[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"[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",
|
2021-07-11 03:13:24 +02:00
|
|
|
"short [voltage|resistor|none]\n"
|
|
|
|
|
" set method for handling shorted ports",
|
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
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const cmdMergeTypes[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"none don't merge parallel devices",
|
|
|
|
|
"conservative merge devices with same L, W",
|
|
|
|
|
"aggressive merge devices with same L",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const cmdShortTypes[] = {
|
2021-07-11 03:13:24 +02:00
|
|
|
"none merge shorted ports",
|
|
|
|
|
"resistor separate shorted ports with 0 ohm resistor",
|
|
|
|
|
"voltage separate shorted ports with 0 volt source",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const cmdExtToSpcFormat[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"spice2",
|
|
|
|
|
"spice3",
|
|
|
|
|
"hspice",
|
|
|
|
|
"ngspice",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const yesno[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"yes",
|
|
|
|
|
"true",
|
|
|
|
|
"on",
|
|
|
|
|
"no",
|
|
|
|
|
"false",
|
|
|
|
|
"off",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const subcktopts[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"yes",
|
|
|
|
|
"true",
|
|
|
|
|
"on",
|
|
|
|
|
"no",
|
|
|
|
|
"false",
|
|
|
|
|
"off",
|
|
|
|
|
"automatic",
|
|
|
|
|
"top",
|
|
|
|
|
"descend",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const shorttypes[] = {
|
2021-07-11 03:13:24 +02:00
|
|
|
"none",
|
|
|
|
|
"resistor",
|
|
|
|
|
"voltage",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
typedef enum {
|
|
|
|
|
IDX_YES, IDX_TRUE, IDX_ON, IDX_NO, IDX_FALSE, IDX_OFF,
|
|
|
|
|
IDX_AUTO, IDX_TOP, IDX_DESCEND
|
2020-05-23 23:13:14 +02:00
|
|
|
} yesnoType;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esDoExtResis) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Extresist: %s\n", (esDoExtResis) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esDoResistorTee) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Resistor tee: %s\n", (esDoResistorTee) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esScale < 0) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Scale: %s\n", (esScale < 0) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esDoHierarchy) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Hierarchy: %s\n", (esDoHierarchy) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-08-02 04:14:42 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esDoBlackBox) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Black box: %s\n", (esDoBlackBox) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-08-02 04:14:42 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esDoRenumber) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Renumber: %s\n", (esDoRenumber) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esMergeNames) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Merge names: %s\n", (esMergeNames) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2021-07-11 03:13:24 +02:00
|
|
|
case EXTTOSPC_SHORT:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
if ((EFOutputFlags & EF_SHORT_MASK) == EF_SHORT_NONE)
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2021-07-11 03:13:24 +02:00
|
|
|
Tcl_SetResult(magicinterp, "none", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Shorts: none\n");
|
|
|
|
|
#endif
|
2021-07-11 03:13:24 +02:00
|
|
|
else if ((EFOutputFlags & EF_SHORT_MASK) == EF_SHORT_R)
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2021-07-11 03:13:24 +02:00
|
|
|
Tcl_SetResult(magicinterp, "resistor", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Shorts: resistor\n");
|
|
|
|
|
#endif
|
2021-07-11 03:13:24 +02:00
|
|
|
else if ((EFOutputFlags & EF_SHORT_MASK) == EF_SHORT_V)
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2021-07-11 03:13:24 +02:00
|
|
|
Tcl_SetResult(magicinterp, "voltage source", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Shorts: voltage source\n");
|
|
|
|
|
#endif
|
2021-07-11 03:13:24 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], cmdShortTypes);
|
|
|
|
|
if (idx < 0) goto usage;
|
|
|
|
|
else switch (idx)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
EFOutputFlags &= ~EF_SHORT_MASK;
|
|
|
|
|
EFOutputFlags |= EF_SHORT_NONE;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
EFOutputFlags &= ~EF_SHORT_MASK;
|
|
|
|
|
EFOutputFlags |= EF_SHORT_R;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
EFOutputFlags &= ~EF_SHORT_MASK;
|
|
|
|
|
EFOutputFlags |= EF_SHORT_V;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esDoPorts) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Ports: %s\n", (esDoPorts) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, (esDoPorts) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Ports: %s\n", (esDoPorts) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case IDX_TOP:
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp,
|
|
|
|
|
(esDoSubckt == 2) ? "auto" :
|
|
|
|
|
(esDoSubckt == 1) ? "on" : "off", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Top subcircuit: %s\n",
|
|
|
|
|
(esDoSubckt == 2) ? "auto" :
|
|
|
|
|
(esDoSubckt == 1) ? "on" : "off");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2024-10-10 21:16:09 +02:00
|
|
|
/* FIXME check NULL is ok here, should this be TCL_STATIC ? TCL_STATIC==0 maybe using TCL_STATIC is better? */
|
|
|
|
|
Tcl_SetResult(magicinterp, (char*)cmdExtToSpcFormat[esFormat], NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Format: %s\n", cmdExtToSpcFormat[esFormat]);
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (cmd->tx_argc < 3) goto usage;
|
|
|
|
|
idx = Lookup(cmd->tx_argv[2], cmdExtToSpcFormat);
|
|
|
|
|
if (idx < 0)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, "Bad format type. Formats are:"
|
2018-10-31 19:33:24 +01:00
|
|
|
"spice2, spice3, hspice, and ngspice.", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxError("Bad format type. Formats are:"
|
|
|
|
|
"spice2, spice3, hspice, and ngspice.");
|
|
|
|
|
#endif
|
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))
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, "infinite", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Capacitance threshold: infinite\n");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetObjResult(magicinterp,
|
|
|
|
|
Tcl_NewDoubleObj((double)LocCapThreshold));
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Capacitance threshold: %lf\n",
|
|
|
|
|
(double)LocCapThreshold);
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (cmd->tx_argc < 3) goto usage;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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]);
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("exttospice: numeric value or \"infinite\" expected.\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_RTHRESH:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
if (LocResistThreshold == INFINITE_THRESHOLD)
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, "infinite", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Resistance threshold: infinite\n");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetObjResult(magicinterp,
|
|
|
|
|
Tcl_NewIntObj(LocResistThreshold));
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Resistance threshold: %lf\n",
|
|
|
|
|
(double)LocResistThreshold);
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("exttospice: integer value or \"infinite\" expected.\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case EXTTOSPC_MERGE:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
if (esMergeDevsA)
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, "aggressive", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Merge: aggressive\n");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
else if (esMergeDevsC)
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, "conservative", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Merge: conservative\n");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
Tcl_SetResult(magicinterp, "none", NULL);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxPrintf("Merge: none\n");
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case EXTTOSPC_DEFAULT:
|
|
|
|
|
LocCapThreshold = 2;
|
|
|
|
|
LocResistThreshold = INFINITE_THRESHOLD;
|
2021-07-11 03:27:19 +02:00
|
|
|
/* Clear EFOutputFlags but preserve "short" behavior */
|
|
|
|
|
EFOutputFlags &= ~EF_TRIM_MASK;
|
|
|
|
|
EFOutputFlags |= 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;
|
2021-07-11 03:13:24 +02:00
|
|
|
esVoltNum = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
inName = EFArgs(argc, argv, &err_result, spcParseArgs, (ClientData) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (err_result == TRUE)
|
|
|
|
|
{
|
2024-09-30 00:00:00 +02:00
|
|
|
EFDone(NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inName == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Assume that we want to do exttospice on the currently loaded cell */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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.
|
2020-05-23 23:13:14 +02:00
|
|
|
* Make output name inName.spice if they weren't
|
2017-04-25 14:41:48 +02:00
|
|
|
* explicitly specified
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (spcesOutName == spcesDefaultOut)
|
|
|
|
|
sprintf(spcesDefaultOut, "%s.spice", inName);
|
|
|
|
|
|
|
|
|
|
/* Read the hierarchical description of the input circuit */
|
2024-04-03 03:55:57 +02:00
|
|
|
if (EFReadFile(inName, esDoHierarchy, esDoExtResis, FALSE, TRUE)
|
|
|
|
|
== FALSE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-09-30 00:00:00 +02:00
|
|
|
EFDone(NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the .ext file was read without error, then open the output file */
|
|
|
|
|
|
|
|
|
|
if ((esSpiceF = fopen(spcesOutName, "w")) == NULL)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
char *tclres = Tcl_Alloc(128);
|
|
|
|
|
sprintf(tclres, "exttospice: Unable to open file %s for writing\n",
|
|
|
|
|
spcesOutName);
|
|
|
|
|
Tcl_SetResult(magicinterp, tclres, TCL_DYNAMIC);
|
2022-02-08 22:12:07 +01:00
|
|
|
#else
|
|
|
|
|
TxError("exttospice: Unable to open file %s for writing\n", spcesOutName);
|
|
|
|
|
#endif
|
2024-09-30 00:00:00 +02:00
|
|
|
EFDone(NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
|
|
|
|
|
2021-05-27 22:13:06 +02:00
|
|
|
for ( i = 0 ; i < TT_MAXTYPES ; i++ ) {
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[i].resClassSource = NO_RESCLASS;
|
|
|
|
|
esFetInfo[i].resClassDrain = NO_RESCLASS;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-10-20 18:22:02 +02:00
|
|
|
while (ExtGetDevInfo(idx++, &devname, &devtype, &s_rclass, &d_rclass,
|
|
|
|
|
&sub_rclass, &subname))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-27 22:13:06 +02:00
|
|
|
if (idx == TT_MAXTYPES)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxError("Error: Ran out of space for device types!\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-05-27 22:13:06 +02:00
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, devname);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!strcmp(devname, "None"))
|
|
|
|
|
esNoModelType = i;
|
|
|
|
|
if (EFStyle != NULL)
|
|
|
|
|
{
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[i].resClassSource = s_rclass;
|
|
|
|
|
esFetInfo[i].resClassDrain = d_rclass;
|
2017-04-25 14:41:48 +02:00
|
|
|
esFetInfo[i].resClassSub = sub_rclass;
|
|
|
|
|
esFetInfo[i].defSubs = subname;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-08 22:12:07 +01:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
|
|
|
|
}
|
2022-02-08 22:12:07 +01:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Write the output file */
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n",
|
|
|
|
|
spiceFormats[esFormat], inName, EFTech);
|
2020-05-23 23:13:14 +02:00
|
|
|
if (esScale < 0)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, ".option scale=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * EFScale / 100.0);
|
|
|
|
|
fprintf(esSpiceF, "\n\n");
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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. . .
|
2021-07-11 03:13:24 +02:00
|
|
|
EFOutputFlags |= 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)
|
2021-07-11 03:13:24 +02:00
|
|
|
EFOutputFlags |= EF_TRIMLOCAL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Write globals under a ".global" card */
|
|
|
|
|
|
|
|
|
|
if (esDoHierarchy && (glist != NULL))
|
|
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, ".global ");
|
|
|
|
|
while (glist != NULL)
|
|
|
|
|
{
|
2022-02-08 22:12:07 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2022-02-08 22:12:07 +01:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
|
2021-05-27 22:13:06 +02:00
|
|
|
TTMaskZero(&initMask);
|
|
|
|
|
if (!esDistrJunct)
|
2023-01-27 17:47:37 +01:00
|
|
|
TTMaskSetType(&initMask, efNumResistClasses);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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);
|
2021-05-27 22:13:06 +02:00
|
|
|
TTMaskZero(&initMask);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (flatFlags & EF_FLATCAPS)
|
|
|
|
|
EFVisitCaps(spccapVisit, (ClientData) NULL);
|
2023-03-13 18:21:34 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
EFVisitResists(spcresistVisit, (ClientData) NULL);
|
|
|
|
|
EFVisitSubcircuits(subcktVisit, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
/* Visit nodes to find the substrate node */
|
|
|
|
|
EFVisitNodes(spcsubVisit, (ClientData)&substr);
|
|
|
|
|
if (substr == NULL)
|
2023-03-14 17:53:22 +01:00
|
|
|
substr = StrDup((char **)NULL, esSpiceDefaultGnd);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-03-13 18:21:34 +01:00
|
|
|
esSpiceCapNode = substr;
|
2017-04-25 14:41:48 +02:00
|
|
|
EFVisitNodes(spcnodeVisit, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
if (EFCompat == FALSE) freeMagic(substr);
|
|
|
|
|
|
|
|
|
|
if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
|
|
|
|
|
fprintf(esSpiceF, ".ends\n");
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (esFormat == HSPICE)
|
2017-04-25 14:41:48 +02:00
|
|
|
printSubcktDict();
|
|
|
|
|
|
2021-12-14 00:05:53 +01:00
|
|
|
EFFlatDone(esFreeNodeClient);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-12-14 00:05:53 +01:00
|
|
|
EFDone(esFreeNodeClient);
|
2021-12-13 04:09:31 +01:00
|
|
|
if (esFormat == HSPICE) {
|
|
|
|
|
HashKill(&subcktNameTable);
|
|
|
|
|
#ifndef UNSORTED_SUBCKT
|
|
|
|
|
DQFree(&subcktNameQueue);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (esSpiceF) fclose(esSpiceF);
|
|
|
|
|
|
|
|
|
|
TxPrintf("exttospice finished.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
#if 0 /* Independent program "ext2spice" has been deprecated */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* main --
|
|
|
|
|
*
|
|
|
|
|
* Top level of ext2spice.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main(argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
int i,flatFlags;
|
|
|
|
|
char *inName;
|
|
|
|
|
FILE *f;
|
|
|
|
|
bool locDoSubckt;
|
|
|
|
|
|
|
|
|
|
esSpiceDevsMerged = 0;
|
|
|
|
|
|
2024-10-10 21:16:09 +02:00
|
|
|
static const char * const spiceFormats[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"SPICE2", "SPICE3", "HSPICE", "NGSPICE", NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
EFInit();
|
|
|
|
|
EFResistThreshold = INFINITE_THRESHOLD ;
|
|
|
|
|
/* create default devinfo entries (MOSIS) which can be overriden by
|
|
|
|
|
the command line arguments */
|
2021-05-27 22:13:06 +02:00
|
|
|
for ( i = 0 ; i < TT_MAXTYPES ; i++ ) {
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[i].resClassSource = NO_RESCLASS;
|
|
|
|
|
esFetInfo[i].resClassDrain = NO_RESCLASS;
|
2017-04-25 14:41:48 +02:00
|
|
|
esFetInfo[i].resClassSub = NO_RESCLASS;
|
|
|
|
|
esFetInfo[i].defSubs = NULL;
|
|
|
|
|
}
|
2021-05-27 22:13:06 +02:00
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "ndev");
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 0 ;
|
2017-04-25 14:41:48 +02:00
|
|
|
esFetInfo[i].resClassSub = NO_RESCLASS ;
|
|
|
|
|
esFetInfo[i].defSubs = "Gnd!";
|
2021-05-27 22:13:06 +02:00
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "pdev");
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 1 ;
|
2017-04-25 14:41:48 +02:00
|
|
|
esFetInfo[i].resClassSub = 8 ;
|
|
|
|
|
esFetInfo[i].defSubs = "Vdd!";
|
2021-05-27 22:13:06 +02:00
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "nmos");
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 0 ;
|
2017-04-25 14:41:48 +02:00
|
|
|
esFetInfo[i].resClassSub = NO_RESCLASS ;
|
|
|
|
|
esFetInfo[i].defSubs = "Gnd!";
|
2021-05-27 22:13:06 +02:00
|
|
|
i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "pmos");
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 1 ;
|
2017-04-25 14:41:48 +02:00
|
|
|
esFetInfo[i].resClassSub = 8 ;
|
|
|
|
|
esFetInfo[i].defSubs = "Vdd!";
|
|
|
|
|
/* Process command line arguments */
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
inName = EFArgs(argc, argv, NULL, spcParseArgs, (ClientData) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (inName == NULL)
|
|
|
|
|
exit (1);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initializations specific to this program.
|
2020-05-23 23:13:14 +02:00
|
|
|
* Make output name inName.spice if they weren't
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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 */
|
2023-07-28 15:40:41 +02:00
|
|
|
if (EFReadFile(inName, TRUE, esDoExtResis, FALSE, TRUE) == FALSE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n",
|
|
|
|
|
spiceFormats[esFormat], inName, EFTech);
|
2020-05-23 23:13:14 +02:00
|
|
|
if (esScale < 0)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF,".option scale=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * EFScale / 100.0);
|
|
|
|
|
fprintf(esSpiceF, "\n\n");
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
esScale = EFScale / 100.0;
|
|
|
|
|
|
|
|
|
|
/* Convert the hierarchical description to a flat one */
|
|
|
|
|
flatFlags = EF_FLATNODES;
|
2021-07-11 03:13:24 +02:00
|
|
|
EFOutputFlags |= EF_TRIMGLOB ;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (IS_FINITE_F(EFCapThreshold)) flatFlags |= EF_FLATCAPS;
|
|
|
|
|
if (esFormat == HSPICE) {
|
2021-07-11 03:13:24 +02:00
|
|
|
EFOutputFlags |= EF_TRIMLOCAL ;
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
|
2021-05-27 22:13:06 +02:00
|
|
|
TTMaskZero(&initMask);
|
|
|
|
|
if (!esDistrJunct)
|
2023-01-27 17:47:37 +01:00
|
|
|
TTMaskSetType(&initMask, efNumResistClasses);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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);
|
2021-05-27 22:13:06 +02:00
|
|
|
TTMaskZero(&initMask);
|
2023-03-13 18:21:34 +01:00
|
|
|
if (flatFlags & EF_FLATCAPS)
|
2017-04-25 14:41:48 +02:00
|
|
|
EFVisitCaps(spccapVisit, (ClientData) NULL);
|
2023-03-13 18:21:34 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
EFVisitResists(spcresistVisit, (ClientData) NULL);
|
|
|
|
|
EFVisitSubcircuits(subcktVisit, (ClientData) NULL);
|
2023-03-14 17:53:22 +01:00
|
|
|
esSpiceCapNode = StrDup((char **)NULL, esSpiceDefaultGnd);
|
2017-04-25 14:41:48 +02:00
|
|
|
EFVisitNodes(spcnodeVisit, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
|
|
|
|
|
fprintf(esSpiceF, ".ends\n");
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (esFormat == HSPICE)
|
2017-04-25 14:41:48 +02:00
|
|
|
printSubcktDict();
|
|
|
|
|
|
2021-12-14 00:05:53 +01:00
|
|
|
EFFlatDone(esFreeNodeClient);
|
|
|
|
|
EFDone(esFreeNodeClient);
|
2021-12-13 04:09:31 +01:00
|
|
|
if (esFormat == HSPICE) {
|
|
|
|
|
HashKill(&subcktNameTable);
|
|
|
|
|
#ifndef UNSORTED_SUBCKT
|
|
|
|
|
DQFree(&subcktNameQueue);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (esSpiceF) fclose(esSpiceF);
|
|
|
|
|
|
|
|
|
|
TxPrintf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL));
|
|
|
|
|
exit (0);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
#endif /* Deprecated */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
2022-02-08 22:12:07 +01:00
|
|
|
* spcParseArgs --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
2022-02-08 22:12:07 +01:00
|
|
|
spcParseArgs(pargc, pargv)
|
2017-04-25 14:41:48 +02:00
|
|
|
int *pargc;
|
|
|
|
|
char ***pargv;
|
|
|
|
|
{
|
|
|
|
|
char **argv = *pargv, *cp;
|
|
|
|
|
int argc = *pargc;
|
2023-03-13 18:21:34 +01:00
|
|
|
char *ftmp, *t;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2025-01-10 06:19:52 +01:00
|
|
|
const char usage_text[] = "Usage: ext2spice "
|
2023-03-13 18:21:34 +01:00
|
|
|
"[-B] [-o spicefile] [-M|-m] [-J flat|hier]\n"
|
2019-10-16 15:17:58 +02:00
|
|
|
"[-f spice2|spice3|hspice|ngspice] [-M] [-m] "
|
|
|
|
|
"[file]\n";
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
switch (argv[0][1])
|
|
|
|
|
{
|
|
|
|
|
case 'd':
|
|
|
|
|
esDistrJunct = TRUE;
|
|
|
|
|
break;
|
2023-03-13 18:21:34 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'M':
|
|
|
|
|
esMergeDevsA = TRUE;
|
|
|
|
|
break;
|
2023-03-13 18:21:34 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'm':
|
|
|
|
|
esMergeDevsC = TRUE;
|
|
|
|
|
break;
|
2023-03-13 18:21:34 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'B':
|
|
|
|
|
esNoAttrs = TRUE;
|
|
|
|
|
break;
|
2023-03-13 18:21:34 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'F':
|
|
|
|
|
esDevNodesOnly = TRUE;
|
|
|
|
|
break;
|
2023-03-13 18:21:34 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'o':
|
|
|
|
|
if ((spcesOutName = ArgStr(&argc, &argv, "filename")) == NULL)
|
|
|
|
|
goto usage;
|
|
|
|
|
break;
|
|
|
|
|
|
2023-03-13 18:21:34 +01:00
|
|
|
case 'f':
|
|
|
|
|
if ((ftmp = ArgStr(&argc, &argv, "format")) == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
goto usage;
|
2023-03-13 18:21:34 +01:00
|
|
|
if (strcasecmp(ftmp, "SPICE2") == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
esFormat = SPICE2;
|
2023-03-13 18:21:34 +01:00
|
|
|
else if (strcasecmp(ftmp, "SPICE3") == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
esFormat = SPICE3;
|
2023-03-13 18:21:34 +01:00
|
|
|
else if (strcasecmp(ftmp, "HSPICE") == 0)
|
|
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
esFormat = HSPICE;
|
|
|
|
|
esScale = -1.0;
|
2023-03-13 18:21:34 +01:00
|
|
|
}
|
|
|
|
|
else if (strcasecmp(ftmp, "NGSPICE") == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
esFormat = NGSPICE;
|
2023-03-13 18:21:34 +01:00
|
|
|
else goto usage;
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-03-13 18:21:34 +01:00
|
|
|
case 'J':
|
|
|
|
|
if ((ftmp = ArgStr(&argc, &argv, "hierAP_SD")) == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
goto usage;
|
2023-03-13 18:21:34 +01:00
|
|
|
if ( strcasecmp(ftmp, "HIER") == 0 )
|
2017-04-25 14:41:48 +02:00
|
|
|
esHierAP = TRUE ;
|
2023-03-13 18:21:34 +01:00
|
|
|
else if (strcasecmp(ftmp, "FLAT") == 0 )
|
2017-04-25 14:41:48 +02:00
|
|
|
esHierAP = FALSE ;
|
2023-03-13 18:21:34 +01:00
|
|
|
else goto usage;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-03-13 18:21:34 +01:00
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-03-13 18:21:34 +01:00
|
|
|
case 'y':
|
|
|
|
|
if ((t = ArgStr(&argc, &argv, "cap-accuracy")) == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
goto usage;
|
2023-03-13 18:21:34 +01:00
|
|
|
TxPrintf("The cap accuracy flag 'y' is deprecated.\n");
|
|
|
|
|
break;
|
|
|
|
|
|
2019-10-16 15:17:58 +02:00
|
|
|
case 'h': /* -h or -help, as suggested by "ext2spice help" */
|
|
|
|
|
TxPrintf(usage_text);
|
|
|
|
|
break;
|
2023-03-13 18:21:34 +01:00
|
|
|
|
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
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE or FALSE
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool extHierSDAttr(term)
|
|
|
|
|
DevTerm *term;
|
|
|
|
|
{
|
|
|
|
|
bool r = esHierAP;
|
|
|
|
|
|
|
|
|
|
if (term->dterm_attrs)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
if (Match(ATTR_HIERAP, term->dterm_attrs) != FALSE)
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2021-12-13 17:33:02 +01:00
|
|
|
EFNodeName *sname, *nodeName;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
2022-01-12 21:30:07 +01:00
|
|
|
int portorder, portmax, portidx, tchars;
|
2017-04-25 14:41:48 +02:00
|
|
|
char stmp[MAX_STR_SIZE];
|
|
|
|
|
char *instname, *subcktname;
|
|
|
|
|
DevParam *plist, *pptr;
|
2019-10-29 18:55:28 +01:00
|
|
|
EFNodeName **nodeList;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
2021-07-11 03:13:24 +02:00
|
|
|
int savflags = EFOutputFlags;
|
2021-07-11 03:27:19 +02:00
|
|
|
EFOutputFlags &= ~EF_TRIM_MASK;
|
|
|
|
|
EFOutputFlags |= 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);
|
2021-07-11 03:13:24 +02:00
|
|
|
EFOutputFlags = savflags;
|
2017-04-25 14:41:48 +02:00
|
|
|
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. */
|
|
|
|
|
|
2022-01-12 21:30:07 +01:00
|
|
|
portmax = EFGetPortMax(def);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (portmax < 0)
|
|
|
|
|
{
|
|
|
|
|
/* No port order declared; print them as we see them. */
|
|
|
|
|
/* This shouldn't happen for proper .ext files written */
|
2021-08-04 19:54:08 +02:00
|
|
|
/* by the magic extractor, since explicit port order is */
|
|
|
|
|
/* generated during topVisit(). */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-12-13 17:33:02 +01:00
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:21:15 +02:00
|
|
|
while ((he = HashNext(&def->def_nodes, &hs)))
|
2021-12-13 17:33:02 +01:00
|
|
|
{
|
|
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
if (sname == NULL) continue;
|
|
|
|
|
snode = sname->efnn_node;
|
|
|
|
|
|
|
|
|
|
if (snode && (snode->efnode_flags & EF_PORT))
|
|
|
|
|
for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
|
2017-04-25 14:41:48 +02:00
|
|
|
if (nodeName->efnn_port >= 0)
|
|
|
|
|
{
|
|
|
|
|
portmax++;
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
tchars = 1;
|
|
|
|
|
}
|
|
|
|
|
tchars += spcdevOutNode(hierName, nodeName->efnn_hier,
|
2020-05-23 23:13:14 +02:00
|
|
|
"subcircuit", esSpiceF);
|
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. */
|
|
|
|
|
|
2021-12-03 18:29:02 +01:00
|
|
|
nodeList = (EFNodeName **)mallocMagic((portmax + 1) * sizeof(EFNodeName *));
|
|
|
|
|
for (portidx = 0; portidx <= portmax; portidx++)
|
2019-10-29 18:55:28 +01:00
|
|
|
nodeList[portidx] = (EFNodeName *)NULL;
|
|
|
|
|
|
2021-12-13 17:33:02 +01:00
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:21:15 +02:00
|
|
|
while ((he = HashNext(&def->def_nodes, &hs)))
|
2021-12-13 17:33:02 +01:00
|
|
|
{
|
|
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
if (sname == NULL) continue;
|
|
|
|
|
snode = sname->efnn_node;
|
|
|
|
|
|
|
|
|
|
if ((snode == NULL) || !(snode->efnode_flags & EF_PORT)) continue;
|
|
|
|
|
|
|
|
|
|
for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
|
2019-10-29 18:55:28 +01:00
|
|
|
{
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
char *pname;
|
|
|
|
|
|
|
|
|
|
portidx = nodeName->efnn_port;
|
2019-10-31 20:19:30 +01:00
|
|
|
if (portidx < 0) continue;
|
2019-10-29 18:55:28 +01:00
|
|
|
if (nodeList[portidx] == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-29 18:55:28 +01:00
|
|
|
nodeList[portidx] = nodeName;
|
|
|
|
|
}
|
|
|
|
|
else if (EFHNBest(nodeName->efnn_hier, nodeList[portidx]->efnn_hier))
|
|
|
|
|
{
|
|
|
|
|
nodeList[portidx] = nodeName;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 18:29:02 +01:00
|
|
|
for (portidx = 0; portidx <= portmax; portidx++)
|
2019-10-29 18:55:28 +01:00
|
|
|
{
|
|
|
|
|
nodeName = nodeList[portidx];
|
|
|
|
|
|
2020-01-13 18:58:04 +01:00
|
|
|
if (nodeName != NULL)
|
2019-10-29 18:55:28 +01:00
|
|
|
{
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
tchars = 1;
|
|
|
|
|
}
|
|
|
|
|
tchars += spcdevOutNode(hierName, nodeName->efnn_hier,
|
2020-05-23 23:13:14 +02:00
|
|
|
"subcircuit", esSpiceF);
|
2019-10-29 18:55:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
freeMagic(nodeList);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-07 01:39:34 +01:00
|
|
|
/* SPICE subcircuit names must begin with A-Z. */
|
2017-08-02 04:14:42 +02:00
|
|
|
subcktname = def->def_name;
|
2021-03-07 01:39:34 +01:00
|
|
|
if (!isalpha(*subcktname))
|
|
|
|
|
{
|
|
|
|
|
subcktname = mallocMagic(2 + strlen(def->def_name));
|
|
|
|
|
sprintf(subcktname, "x%s", def->def_name);
|
|
|
|
|
freeMagic(def->def_name);
|
|
|
|
|
def->def_name = subcktname;
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
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));
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-04 19:54:08 +02:00
|
|
|
/* Define a linked node name list */
|
|
|
|
|
|
|
|
|
|
typedef struct _lnn {
|
|
|
|
|
EFNodeName *lnn_nodeName;
|
|
|
|
|
struct _lnn *lnn_next;
|
|
|
|
|
} linkedNodeName;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
*
|
2020-11-24 21:30:49 +01:00
|
|
|
* NOTE: The cookie-cutter method for extraction can result in multiple
|
|
|
|
|
* connections to the same port if the net spans multiple extraction regions.
|
|
|
|
|
* Because of this, it is necessary to make sure that the same port name is
|
|
|
|
|
* not output twice.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
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;
|
2022-01-13 23:00:07 +01:00
|
|
|
HashEntry *he, *hep, *heh;
|
2020-11-24 21:30:49 +01:00
|
|
|
HashTable portNameTable;
|
2021-08-04 19:54:08 +02:00
|
|
|
int portorder, portmax, tchars;
|
2021-07-25 19:49:13 +02:00
|
|
|
bool explicit;
|
2017-04-25 14:41:48 +02:00
|
|
|
DevParam *plist, *pptr;
|
|
|
|
|
char *instname;
|
|
|
|
|
char *subcktname;
|
|
|
|
|
char *pname;
|
2021-10-05 01:52:22 +02:00
|
|
|
char **sorted_ports;
|
2021-08-04 19:54:08 +02:00
|
|
|
linkedNodeName *lnn = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-05-04 22:43:38 +02:00
|
|
|
/* Primitive devices are not output at all */
|
|
|
|
|
if (def->def_flags & DEF_PRIMITIVE) return;
|
|
|
|
|
|
2020-11-24 21:30:49 +01:00
|
|
|
HashInit(&portNameTable, 32, HT_STRINGKEYS);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* SPICE subcircuit names must begin with A-Z. This will also be */
|
|
|
|
|
/* enforced when writing X subcircuit calls. */
|
|
|
|
|
subcktname = def->def_name;
|
2021-03-07 01:39:34 +01:00
|
|
|
if (!isalpha(*subcktname))
|
|
|
|
|
{
|
|
|
|
|
subcktname = mallocMagic(2 + strlen(def->def_name));
|
|
|
|
|
sprintf(subcktname, "x%s", def->def_name);
|
|
|
|
|
freeMagic(def->def_name);
|
|
|
|
|
def->def_name = subcktname;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2024-10-04 18:21:15 +02:00
|
|
|
while ((he = HashNext(&def->def_nodes, &hs)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
if (sname == NULL) continue;
|
|
|
|
|
snode = sname->efnn_node;
|
2020-03-20 19:50:56 +01:00
|
|
|
if ((!snode) || (!(snode->efnode_flags & EF_PORT))) continue;
|
2021-07-25 19:49:13 +02:00
|
|
|
explicit = FALSE;
|
2024-08-06 17:17:27 +02:00
|
|
|
portorder = snode->efnode_name->efnn_port;
|
|
|
|
|
if (portorder > portmax) portmax = portorder;
|
|
|
|
|
if (portorder != -1) explicit = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
|
|
|
|
|
{
|
|
|
|
|
portorder = nodeName->efnn_port;
|
|
|
|
|
if (portorder > portmax) portmax = portorder;
|
2021-07-25 19:49:13 +02:00
|
|
|
if (portorder != -1) explicit = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-08-04 19:54:08 +02:00
|
|
|
if (explicit == FALSE)
|
|
|
|
|
{
|
|
|
|
|
/* Tag this an an implicit port (port without an assigned index) */
|
|
|
|
|
linkedNodeName *newlnn =
|
|
|
|
|
(linkedNodeName *)mallocMagic(sizeof(linkedNodeName));
|
|
|
|
|
newlnn->lnn_next = lnn;
|
|
|
|
|
newlnn->lnn_nodeName = sname;
|
|
|
|
|
lnn = newlnn;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-04 19:54:08 +02:00
|
|
|
/* Make all port numbers explicit (unless this is a black-box */
|
|
|
|
|
/* circuit "ext2spice blackbox on" is in effect). */
|
|
|
|
|
|
|
|
|
|
while (lnn != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-08-04 19:54:08 +02:00
|
|
|
sname = lnn->lnn_nodeName;
|
|
|
|
|
if (esDoBlackBox == FALSE || !(def->def_flags & DEF_ABSTRACT))
|
|
|
|
|
sname->efnn_port = ++portmax;
|
|
|
|
|
freeMagic(lnn);
|
|
|
|
|
lnn = lnn->lnn_next;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-10-05 01:52:22 +02:00
|
|
|
/* Port numbers need not start at zero or be contiguous. They will be */
|
|
|
|
|
/* printed in numerical order. This is done by allocating space for */
|
|
|
|
|
/* the output first and generating text into the allocated array */
|
|
|
|
|
/* indexed by port, to avoid multiple scans through the hash table. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-10-05 01:52:22 +02:00
|
|
|
sorted_ports = (char **)mallocMagic((portmax + 1) * sizeof(char *));
|
2022-01-12 23:19:29 +01:00
|
|
|
for (portorder = 0; portorder <= portmax; portorder++)
|
|
|
|
|
sorted_ports[portorder] = NULL;
|
2021-10-05 01:52:22 +02:00
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:21:15 +02:00
|
|
|
while ((he = HashNext(&def->def_nodes, &hs)))
|
2021-08-04 19:54:08 +02:00
|
|
|
{
|
2021-10-05 01:52:22 +02:00
|
|
|
char stmp[MAX_STR_SIZE];
|
|
|
|
|
int portidx;
|
2021-08-04 19:54:08 +02:00
|
|
|
|
2021-10-05 01:52:22 +02:00
|
|
|
sname = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
if (sname == NULL) continue; /* Should not happen */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-12 21:30:07 +01:00
|
|
|
snode = sname->efnn_node;
|
2021-10-05 01:52:22 +02:00
|
|
|
if ((!snode) || (!(snode->efnode_flags & EF_PORT))) continue;
|
2021-08-04 19:54:08 +02:00
|
|
|
|
2021-10-05 01:52:22 +02:00
|
|
|
for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
|
|
|
|
|
{
|
|
|
|
|
portidx = nodeName->efnn_port;
|
2021-10-07 16:58:21 +02:00
|
|
|
if (portidx < 0) continue;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-13 23:00:07 +01:00
|
|
|
/* Check if the same hierName is recorded in the flattened/optimized
|
|
|
|
|
* def's efNodeHashTable. If not, then it has been optimized out
|
|
|
|
|
* and should be removed from the port list.
|
|
|
|
|
*/
|
|
|
|
|
if (def->def_flags & DEF_ABSTRACT)
|
2022-01-14 17:07:08 +01:00
|
|
|
heh = HashLookOnly(&efNodeHashTable, (char *)nodeName->efnn_hier);
|
2022-01-13 23:00:07 +01:00
|
|
|
else
|
2022-01-14 17:07:08 +01:00
|
|
|
heh = HashLookOnly(&efNodeHashTable,
|
|
|
|
|
(char *)snode->efnode_name->efnn_hier);
|
2022-01-13 23:00:07 +01:00
|
|
|
|
2021-10-05 01:52:22 +02:00
|
|
|
/* If view is abstract, rely on the given port name, not
|
|
|
|
|
* the node. Otherwise, artifacts of the abstract view
|
|
|
|
|
* may cause nodes to be merged and the names lost.
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-10-05 01:52:22 +02:00
|
|
|
if (def->def_flags & DEF_ABSTRACT)
|
|
|
|
|
{
|
|
|
|
|
EFHNSprintf(stmp, nodeName->efnn_hier);
|
|
|
|
|
pname = stmp;
|
|
|
|
|
}
|
|
|
|
|
else
|
2023-06-13 23:14:36 +02:00
|
|
|
// pname = nodeSpiceName(snode->efnode_name->efnn_hier, NULL);
|
|
|
|
|
pname = nodeSpiceName(nodeName->efnn_hier, NULL);
|
2020-11-24 21:30:49 +01:00
|
|
|
|
2024-10-04 21:09:28 +02:00
|
|
|
if (heh == (HashEntry *)NULL) /* pname now resolved for log output */
|
|
|
|
|
{
|
|
|
|
|
/* Port was optimized out */
|
|
|
|
|
snode->efnode_flags &= ~EF_PORT;
|
|
|
|
|
TxPrintf("Note: Port %s was optimized out of %s\n",
|
|
|
|
|
pname, def->def_name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-13 17:33:02 +01:00
|
|
|
hep = HashLookOnly(&portNameTable, pname);
|
2022-01-13 23:00:07 +01:00
|
|
|
if (hep == (HashEntry *)NULL)
|
2021-10-05 01:52:22 +02:00
|
|
|
{
|
2022-01-13 23:00:07 +01:00
|
|
|
hep = HashFind(&portNameTable, pname);
|
|
|
|
|
HashSetValue(hep, (ClientData)(pointertype)nodeName->efnn_port);
|
|
|
|
|
if (sorted_ports[portidx] == NULL)
|
|
|
|
|
sorted_ports[portidx] = StrDup((char **)NULL, pname);
|
2021-10-05 01:52:22 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2022-01-13 23:00:07 +01:00
|
|
|
/* Node that was unassigned has been found to be
|
|
|
|
|
* a repeat (see NOTE at top), so make sure its
|
|
|
|
|
* port number is set correctly.
|
|
|
|
|
*/
|
|
|
|
|
nodeName->efnn_port = (int)(pointertype)HashGetValue(hep);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-24 21:30:49 +01:00
|
|
|
HashKill(&portNameTable);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-10-05 01:52:22 +02:00
|
|
|
/* Output all ports, in order */
|
|
|
|
|
|
|
|
|
|
for (portorder = 0; portorder <= portmax; portorder++)
|
|
|
|
|
{
|
|
|
|
|
if (sorted_ports[portorder] != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (tchars > 80)
|
|
|
|
|
{
|
|
|
|
|
/* Line continuation */
|
|
|
|
|
fprintf(esSpiceF, "\n+");
|
|
|
|
|
tchars = 1;
|
|
|
|
|
}
|
|
|
|
|
fprintf(esSpiceF, " %s", sorted_ports[portorder]);
|
2021-10-05 14:50:45 +02:00
|
|
|
tchars += strlen(sorted_ports[portorder]) + 1;
|
2021-10-05 01:52:22 +02:00
|
|
|
|
|
|
|
|
freeMagic(sorted_ports[portorder]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
freeMagic(sorted_ports);
|
|
|
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2022-12-15 03:40:24 +01:00
|
|
|
DevParam *plist, *dparam;
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2024-09-30 08:46:35 +02:00
|
|
|
fprintf(esSpiceF, "%g", (double)parmval * (double)scale * (double)scale
|
|
|
|
|
* (double)esScale * (double)esScale * plist->parm_scale
|
2017-04-25 14:41:48 +02:00
|
|
|
* 1E-12);
|
|
|
|
|
else
|
2023-06-22 02:44:38 +02:00
|
|
|
esSIvalue(esSpiceF, 1.0E-12 * (parmval + plist->parm_offset)
|
|
|
|
|
* scale * scale * esScale * esScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-09-11 23:29:12 +02:00
|
|
|
int pn, resclass;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
pn = plist->parm_type[1] - '0';
|
|
|
|
|
if (pn >= dev->dev_nterm) pn = dev->dev_nterm - 1;
|
|
|
|
|
|
|
|
|
|
hierD = extHierSDAttr(&dev->dev_terms[pn]);
|
|
|
|
|
|
2021-03-18 16:52:56 +01:00
|
|
|
resclass = (pn > 1) ? esFetInfo[dev->dev_type].resClassDrain :
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[dev->dev_type].resClassSource;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
// 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,
|
2020-09-11 23:29:12 +02:00
|
|
|
resclass, scale, plist->parm_type,
|
2017-04-25 14:41:48 +02:00
|
|
|
plist->parm_next->parm_type,
|
|
|
|
|
sdM, esSpiceF);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
2023-02-16 17:59:13 +01:00
|
|
|
spcnAP(&dev->dev_terms[pn], dnode, resclass, scale,
|
|
|
|
|
plist->parm_name, plist->parm_next->parm_name,
|
2017-04-25 14:41:48 +02:00
|
|
|
sdM, esSpiceF, w);
|
|
|
|
|
}
|
|
|
|
|
plist = plist->parm_next;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (hierD)
|
|
|
|
|
spcnAPHier(&dev->dev_terms[pn], hierName,
|
2020-09-11 23:29:12 +02:00
|
|
|
resclass, scale, plist->parm_type, NULL,
|
2017-04-25 14:41:48 +02:00
|
|
|
sdM, esSpiceF);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
2023-02-16 17:59:13 +01:00
|
|
|
spcnAP(&dev->dev_terms[pn], dnode, resclass, scale,
|
|
|
|
|
plist->parm_name, NULL, sdM, esSpiceF, w);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case 'p':
|
2022-12-15 03:40:24 +01:00
|
|
|
// Check for perimeter of terminal node vs. device perimeter
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2024-09-30 08:46:50 +02:00
|
|
|
fprintf(esSpiceF, "%g", (double)parmval * scale
|
2017-04-25 14:41:48 +02:00
|
|
|
* esScale * plist->parm_scale * 1E-6);
|
|
|
|
|
else
|
2023-06-22 02:44:38 +02:00
|
|
|
esSIvalue(esSpiceF, 1.0E-12 * (parmval + plist->parm_offset)
|
|
|
|
|
* scale * esScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-09-11 23:29:12 +02:00
|
|
|
int pn, resclass;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
pn = plist->parm_type[1] - '0';
|
|
|
|
|
if (pn >= dev->dev_nterm) pn = dev->dev_nterm - 1;
|
|
|
|
|
|
2021-03-18 16:52:56 +01:00
|
|
|
resclass = (pn > 1) ? esFetInfo[dev->dev_type].resClassDrain :
|
2020-09-11 23:29:12 +02:00
|
|
|
esFetInfo[dev->dev_type].resClassSource;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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,
|
2020-09-11 23:29:12 +02:00
|
|
|
resclass, scale, plist->parm_next->parm_type,
|
2017-04-25 14:41:48 +02:00
|
|
|
plist->parm_type, sdM, esSpiceF);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
2023-02-16 17:59:13 +01:00
|
|
|
spcnAP(&dev->dev_terms[pn], dnode, resclass, scale,
|
|
|
|
|
plist->parm_next->parm_name,
|
2017-04-25 14:41:48 +02:00
|
|
|
plist->parm_name, sdM, esSpiceF, w);
|
|
|
|
|
}
|
|
|
|
|
plist = plist->parm_next;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (hierD)
|
|
|
|
|
spcnAPHier(&dev->dev_terms[pn], hierName,
|
2020-09-11 23:29:12 +02:00
|
|
|
resclass, scale, NULL, plist->parm_type,
|
2017-04-25 14:41:48 +02:00
|
|
|
sdM, esSpiceF);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dnode = SpiceGetNode(hierName,
|
|
|
|
|
dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
|
2023-02-16 17:59:13 +01:00
|
|
|
spcnAP(&dev->dev_terms[pn], dnode, resclass, scale,
|
|
|
|
|
NULL, plist->parm_name, sdM, esSpiceF, w);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'l':
|
2022-12-15 03:40:24 +01:00
|
|
|
// Check for length of device vs. depth of terminal
|
|
|
|
|
if (plist->parm_type[1] == '\0' || plist->parm_type[1] == '0')
|
|
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
fprintf(esSpiceF, "%g", l * scale);
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
2024-09-30 08:47:05 +02:00
|
|
|
fprintf(esSpiceF, "%g", (double)l * scale * esScale
|
2017-04-25 14:41:48 +02:00
|
|
|
* plist->parm_scale * 1E-6);
|
2022-12-15 03:40:24 +01:00
|
|
|
else
|
2023-06-22 02:44:38 +02:00
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * (l + plist->parm_offset)
|
|
|
|
|
* scale * esScale);
|
2022-12-15 03:40:24 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2022-12-15 03:40:24 +01:00
|
|
|
{
|
|
|
|
|
/* l1, l2, etc. used to indicate the length of the terminal */
|
|
|
|
|
/* Find the value in dev_params */
|
|
|
|
|
for (dparam = dev->dev_params; dparam; dparam = dparam->parm_next)
|
|
|
|
|
{
|
|
|
|
|
if ((strlen(dparam->parm_name) > 2) &&
|
|
|
|
|
(dparam->parm_name[0] == 'l') &&
|
|
|
|
|
(dparam->parm_name[1] == plist->parm_type[1]) &&
|
|
|
|
|
(dparam->parm_name[2] == '='))
|
|
|
|
|
{
|
|
|
|
|
int dval;
|
|
|
|
|
if (sscanf(&dparam->parm_name[3], "%d", &dval) == 1)
|
|
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
fprintf(esSpiceF, "%g", dval * scale);
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
2024-09-30 00:00:00 +02:00
|
|
|
fprintf(esSpiceF, "%g", (double)dval * scale * esScale
|
2022-12-15 03:40:24 +01:00
|
|
|
* plist->parm_scale * 1E-6);
|
|
|
|
|
else
|
2023-06-22 02:44:38 +02:00
|
|
|
esSIvalue(esSpiceF, (dval + plist->parm_offset)
|
|
|
|
|
* scale * esScale * 1.0E-6);
|
2022-12-15 03:40:24 +01:00
|
|
|
dparam->parm_name[0] = '\0';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
2022-12-15 03:40:24 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'w':
|
2024-12-06 22:42:43 +01:00
|
|
|
// Check for width of device vs. width of terminal
|
|
|
|
|
if (plist->parm_type[1] == '\0' || plist->parm_type[1] == '0')
|
|
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
fprintf(esSpiceF, "%g", w * scale);
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
fprintf(esSpiceF, "%g", (double)w * scale * esScale
|
2017-04-25 14:41:48 +02:00
|
|
|
* plist->parm_scale * 1E-6);
|
2024-12-06 22:42:43 +01:00
|
|
|
else
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * (w + plist->parm_offset)
|
2023-06-22 02:44:38 +02:00
|
|
|
* scale * esScale);
|
2024-12-06 22:42:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* w1, w2, etc. used to indicate the width of the terminal */
|
|
|
|
|
/* Find the value in dev_params */
|
|
|
|
|
for (dparam = dev->dev_params; dparam; dparam = dparam->parm_next)
|
|
|
|
|
{
|
|
|
|
|
if ((strlen(dparam->parm_name) > 2) &&
|
|
|
|
|
(dparam->parm_name[0] == 'w') &&
|
|
|
|
|
(dparam->parm_name[1] == plist->parm_type[1]) &&
|
|
|
|
|
(dparam->parm_name[2] == '='))
|
|
|
|
|
{
|
|
|
|
|
int dval;
|
|
|
|
|
if (sscanf(&dparam->parm_name[3], "%d", &dval) == 1)
|
|
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " %s=", plist->parm_name);
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
fprintf(esSpiceF, "%g", dval * scale);
|
|
|
|
|
else if (plist->parm_scale != 1.0)
|
|
|
|
|
fprintf(esSpiceF, "%g", (double)dval * scale * esScale
|
|
|
|
|
* plist->parm_scale * 1E-6);
|
|
|
|
|
else
|
|
|
|
|
esSIvalue(esSpiceF, (dval + plist->parm_offset)
|
|
|
|
|
* scale * esScale * 1.0E-6);
|
|
|
|
|
dparam->parm_name[0] = '\0';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2024-09-30 08:47:40 +02:00
|
|
|
fprintf(esSpiceF, "%g", (double)dev->dev_rect.r_xbot * scale
|
2017-04-25 14:41:48 +02:00
|
|
|
* esScale * plist->parm_scale * 1E-6);
|
|
|
|
|
else
|
2023-06-22 02:44:38 +02:00
|
|
|
esSIvalue(esSpiceF, (dev->dev_rect.r_xbot + plist->parm_offset)
|
|
|
|
|
* scale * esScale * 1.0E-6);
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2024-09-30 08:11:16 +02:00
|
|
|
fprintf(esSpiceF, "%g", (double)dev->dev_rect.r_ybot * (double)scale
|
|
|
|
|
* (double)esScale * plist->parm_scale * 1E-6);
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2023-06-22 02:44:38 +02:00
|
|
|
esSIvalue(esSpiceF, (dev->dev_rect.r_ybot + plist->parm_offset)
|
|
|
|
|
* scale * esScale * 1.0E-6);
|
2017-04-25 14:41:48 +02:00
|
|
|
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) */
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
float sdM ;
|
2017-04-25 14:41:48 +02:00
|
|
|
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]);
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (esScale < 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(esSpiceF, " w=%g l=%g", w * scale, (l * scale) / dscale);
|
|
|
|
|
else
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " w=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale);
|
|
|
|
|
fprintf(esSpiceF, " l=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * (l * scale * esScale) / dscale);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-09 17:34:24 +02:00
|
|
|
|
|
|
|
|
/*
|
2021-02-18 03:08:10 +01:00
|
|
|
*-----------------------------------------------------------------------------
|
2020-08-09 17:34:24 +02:00
|
|
|
* swapDrainSource
|
|
|
|
|
*
|
|
|
|
|
* Swap drain and source ordering and the related stuff
|
|
|
|
|
* including the drain/source area parameters
|
|
|
|
|
*
|
2021-02-18 03:08:10 +01:00
|
|
|
* This is typically called if any terminal is marked with attribute "D" or "S"
|
2020-08-09 17:34:24 +02:00
|
|
|
* (label "D$" or "S$" at poly-diffusion interface),
|
|
|
|
|
* then swap order of source and drain compared to the default ordering.
|
2021-02-18 03:08:10 +01:00
|
|
|
*
|
|
|
|
|
* Note:
|
|
|
|
|
* Before calling this function, ensure that dev->dev_nterm >= 3
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Soure (dev->dev_terms[1]) and drain (dev->dev_terms[2]) terminals
|
|
|
|
|
* are swapped.
|
2020-08-09 17:34:24 +02:00
|
|
|
*
|
2021-02-18 03:08:10 +01:00
|
|
|
*-----------------------------------------------------------------------------
|
2020-08-09 17:34:24 +02:00
|
|
|
*/
|
2021-02-18 03:08:10 +01:00
|
|
|
|
2020-08-09 17:34:24 +02:00
|
|
|
void
|
2021-02-18 03:08:10 +01:00
|
|
|
swapDrainSource(dev)
|
2020-08-09 17:34:24 +02:00
|
|
|
Dev *dev;
|
|
|
|
|
{
|
2021-02-18 03:08:10 +01:00
|
|
|
DevTerm tmpTerm;
|
2020-08-09 19:11:38 +02:00
|
|
|
|
2021-02-18 03:08:10 +01:00
|
|
|
/* swap original terminals */
|
|
|
|
|
memcpy(&tmpTerm, &(dev->dev_terms[1]), sizeof(DevTerm));
|
|
|
|
|
memcpy(&(dev->dev_terms[1]), &(dev->dev_terms[2]), sizeof(DevTerm));
|
|
|
|
|
memcpy(&(dev->dev_terms[2]), &tmpTerm, sizeof(DevTerm));
|
2020-08-09 17:34:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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
|
2020-05-23 23:13:14 +02:00
|
|
|
* + ad= pd= as= ps= * asub= psub=
|
|
|
|
|
* **devattr g= s= d=
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* where
|
|
|
|
|
* type is a name identifying this type of transistor
|
|
|
|
|
* other types of transistors are extracted with
|
2020-05-23 23:13:14 +02:00
|
|
|
* an M card but it should be easy to turn them to whatever
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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
|
2020-06-01 23:14:22 +02:00
|
|
|
spcdevVisit(dev, hc, scale, trans)
|
2017-04-25 14:41:48 +02:00
|
|
|
Dev *dev; /* Dev being output */
|
2020-06-01 23:14:22 +02:00
|
|
|
HierContext *hc; /* Hierarchical context down to this dev */
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2021-02-18 03:08:10 +01:00
|
|
|
bool subAP= FALSE, hierS, hierD, extHierSDAttr();
|
2020-05-23 23:13:14 +02:00
|
|
|
float sdM;
|
2017-04-25 14:41:48 +02:00
|
|
|
char name[12], devchar;
|
|
|
|
|
bool has_model = TRUE;
|
2020-06-01 23:14:22 +02:00
|
|
|
HierName *hierName = hc->hc_hierName;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
sprintf(name, "output");
|
|
|
|
|
|
|
|
|
|
/* If no terminals, can't do much of anything */
|
2020-05-23 23:13:14 +02:00
|
|
|
if (dev->dev_nterm < 1 )
|
2017-04-25 14:41:48 +02:00
|
|
|
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];
|
2021-02-18 03:08:10 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (dev->dev_nterm >= 3)
|
|
|
|
|
{
|
2021-02-18 03:08:10 +01:00
|
|
|
drain = &dev->dev_terms[2];
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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")))
|
|
|
|
|
{
|
2021-02-18 03:08:10 +01:00
|
|
|
swapDrainSource(dev);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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:
|
2020-05-23 23:13:14 +02:00
|
|
|
if (dev->dev_nterm < 1)
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
2021-07-11 03:13:24 +02:00
|
|
|
if ((dev->dev_type == esNoModelType) ||
|
|
|
|
|
!strcmp(EFDevTypes[dev->dev_type], "None"))
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2020-06-24 21:22:04 +02:00
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (gate == source)
|
|
|
|
|
{
|
2020-06-24 21:22:04 +02:00
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2021-07-11 03:13:24 +02:00
|
|
|
case DEV_VOLT:
|
|
|
|
|
devchar = 'V';
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2021-07-11 03:13:24 +02:00
|
|
|
case DEV_VOLT:
|
|
|
|
|
fprintf(esSpiceF, "%d", esVoltNum++);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
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");
|
2020-05-23 23:13:14 +02:00
|
|
|
spcdevOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSpiceF);
|
2017-04-25 14:41:48 +02:00
|
|
|
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:
|
|
|
|
|
|
2020-07-29 20:13:23 +02:00
|
|
|
/* MOS-like subcircuit is "Xnnn drain gate [source [sub]]" */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* to more conveniently handle cases where MOS devices are */
|
|
|
|
|
/* modeled by subcircuits with the same pin ordering. */
|
|
|
|
|
|
2020-07-29 20:13:23 +02:00
|
|
|
spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
|
2017-04-25 14:41:48 +02:00
|
|
|
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. */
|
|
|
|
|
|
2020-07-29 20:13:23 +02:00
|
|
|
if (dev->dev_class != DEV_MSUBCKT)
|
|
|
|
|
{
|
|
|
|
|
if (dev->dev_nterm > 1)
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
else /* class DEV_MSUBCKT */
|
|
|
|
|
{
|
|
|
|
|
if (dev->dev_nterm > 2)
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2021-07-11 03:13:24 +02:00
|
|
|
case DEV_VOLT:
|
|
|
|
|
/* The voltage source is "Vnnn term1 term2 0.0" and is used
|
|
|
|
|
* only to separate shorted port names.
|
|
|
|
|
*/
|
|
|
|
|
if (dev->dev_nterm > 1)
|
|
|
|
|
spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
name, esSpiceF);
|
2021-09-21 02:14:25 +02:00
|
|
|
if (dev->dev_nterm > 2)
|
2021-07-11 03:13:24 +02:00
|
|
|
spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
|
|
|
|
|
name, esSpiceF);
|
|
|
|
|
fprintf(esSpiceF, " 0.0");
|
|
|
|
|
break;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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,
|
2020-05-23 23:13:14 +02:00
|
|
|
name, esSpiceF);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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,
|
2020-05-23 23:13:14 +02:00
|
|
|
name, esSpiceF);
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2023-03-13 18:21:34 +01:00
|
|
|
/* extracted units are fF. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2023-03-13 18:21:34 +01:00
|
|
|
esSIvalue(esSpiceF, 1.0E-15 * (double)sdM * (double)dev->dev_cap);
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " w=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale);
|
|
|
|
|
fprintf(esSpiceF, " l=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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 */
|
2023-03-13 18:21:34 +01:00
|
|
|
/* extracted units are fF. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2023-03-13 18:21:34 +01:00
|
|
|
esSIvalue(esSpiceF, 1.0E-15 * (double)sdM * (double)dev->dev_cap);
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " w=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale);
|
|
|
|
|
fprintf(esSpiceF, " l=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
fprintf(esSpiceF, " w=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale);
|
|
|
|
|
fprintf(esSpiceF, " l=");
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
spcWriteParams(dev, hierName, scale, l, w, sdM);
|
|
|
|
|
if (sdM != 1.0)
|
|
|
|
|
fprintf(esSpiceF, " M=%g", sdM);
|
|
|
|
|
|
|
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Check controlling attributes and output area and perimeter.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
hierS = extHierSDAttr(source);
|
|
|
|
|
hierD = extHierSDAttr(drain);
|
2020-05-23 23:13:14 +02:00
|
|
|
if ( gate->dterm_attrs )
|
2017-04-25 14:41:48 +02:00
|
|
|
subAP = Match(ATTR_SUBSAP, gate->dterm_attrs ) ;
|
|
|
|
|
|
|
|
|
|
fprintf(esSpiceF, "\n+ ");
|
2020-05-23 23:13:14 +02:00
|
|
|
if (hierD)
|
2020-09-11 23:29:12 +02:00
|
|
|
spcnAPHier(drain, hierName, esFetInfo[dev->dev_type].resClassDrain,
|
2017-04-25 14:41:48 +02:00
|
|
|
scale, "ad", "pd", sdM, esSpiceF);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dnode = SpiceGetNode(hierName, drain->dterm_node->efnode_name->efnn_hier);
|
2023-02-16 17:59:13 +01:00
|
|
|
spcnAP(drain, dnode, esFetInfo[dev->dev_type].resClassDrain, scale,
|
2017-04-25 14:41:48 +02:00
|
|
|
"ad", "pd", sdM, esSpiceF, w);
|
|
|
|
|
}
|
|
|
|
|
if (hierS)
|
2020-09-11 23:29:12 +02:00
|
|
|
spcnAPHier(source, hierName, esFetInfo[dev->dev_type].resClassSource,
|
2017-04-25 14:41:48 +02:00
|
|
|
scale, "as", "ps", sdM, esSpiceF);
|
|
|
|
|
else {
|
|
|
|
|
snode= SpiceGetNode(hierName, source->dterm_node->efnode_name->efnn_hier);
|
2023-02-16 17:59:13 +01:00
|
|
|
spcnAP(source, snode, esFetInfo[dev->dev_type].resClassSource, scale,
|
2017-04-25 14:41:48 +02:00
|
|
|
"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");
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
else if (subnodeFlat)
|
2023-02-16 17:59:13 +01:00
|
|
|
spcnAP(NULL, subnodeFlat, esFetInfo[dev->dev_type].resClassSub,
|
|
|
|
|
scale, "asub", "psub", sdM, esSpiceF, -1);
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
fprintf(esSpiceF, "asub=0 psub=0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now output attributes, if present */
|
|
|
|
|
if (!esNoAttrs)
|
|
|
|
|
{
|
2023-02-16 17:59:13 +01:00
|
|
|
bool haveSattr = FALSE;
|
|
|
|
|
bool haveDattr = FALSE;
|
|
|
|
|
|
|
|
|
|
if (source->dterm_attrs && (*source->dterm_attrs))
|
|
|
|
|
haveSattr = TRUE;
|
|
|
|
|
if (drain->dterm_attrs && (*drain->dterm_attrs))
|
|
|
|
|
haveDattr = TRUE;
|
|
|
|
|
|
|
|
|
|
if (gate->dterm_attrs || haveSattr || haveDattr)
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(esSpiceF,"\n**devattr");
|
|
|
|
|
if (gate->dterm_attrs)
|
|
|
|
|
fprintf(esSpiceF, " g=%s", gate->dterm_attrs);
|
2023-02-16 17:59:13 +01:00
|
|
|
if (haveSattr)
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(esSpiceF, " s=%s", source->dterm_attrs);
|
2023-02-16 17:59:13 +01:00
|
|
|
if (haveDattr)
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(esSpiceF, " d=%s", drain->dterm_attrs);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fprintf(esSpiceF, "\n");
|
2020-08-09 19:11:38 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* spcdevSubstrate -
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Output the node name of the substrate of a dev. If the suffix is the
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
if (outf)
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(outf, "errGnd!");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
/* Canonical name */
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
2020-05-23 23:13:14 +02:00
|
|
|
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
|
|
|
|
2021-12-20 19:12:49 +01:00
|
|
|
/* Create node client if it doesn't exist */
|
2022-02-08 22:12:07 +01:00
|
|
|
if ((nodeClient *)nn->efnn_node->efnode_client == (nodeClient *)NULL)
|
2017-10-20 22:07:52 +02:00
|
|
|
initNodeClientHier(nn->efnn_node);
|
|
|
|
|
|
2021-12-20 19:12:49 +01:00
|
|
|
/* Mark node as visited (set bit one higher than number of resist classes) */
|
|
|
|
|
if (esDistrJunct)
|
|
|
|
|
update_w(efNumResistClasses, 1, nn->efnn_node);
|
|
|
|
|
else
|
|
|
|
|
markVisited((nodeClientHier *)nn->efnn_node->efnode_client,
|
|
|
|
|
efNumResistClasses);
|
2017-04-25 14:41:48 +02:00
|
|
|
return nn->efnn_node;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-06 17:49:29 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* nDecimals() ---
|
|
|
|
|
*
|
|
|
|
|
* Code taken from discussion at
|
|
|
|
|
* https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf
|
|
|
|
|
*
|
|
|
|
|
* Modifies value "d" by truncating it to precision "n", writing the result
|
|
|
|
|
* into string "s".
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nDecimals(char *s, double d, int n)
|
|
|
|
|
{
|
|
|
|
|
int sz;
|
|
|
|
|
double d2;
|
|
|
|
|
|
|
|
|
|
// Allow for negative.
|
|
|
|
|
d2 = (d >= 0) ? d : -d;
|
|
|
|
|
sz = (d >= 0) ? 0 : 1;
|
|
|
|
|
|
|
|
|
|
// Add one for each whole digit (0.xx special case).
|
|
|
|
|
if (d2 < 1) sz++;
|
|
|
|
|
while (d2 >= 1)
|
|
|
|
|
{
|
|
|
|
|
d2 /= 10.0;
|
|
|
|
|
sz++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adjust for decimal point and fractionals.
|
|
|
|
|
sz += 1 + n;
|
|
|
|
|
|
|
|
|
|
// Create formatted string then use it.
|
|
|
|
|
sprintf(s, "%*.*f", sz, n, d);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* morphNumericString() ---
|
|
|
|
|
*
|
|
|
|
|
* Code taken from discussion at
|
|
|
|
|
* https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf
|
|
|
|
|
*
|
|
|
|
|
* Remove trailing zeros from the number represented in string "s".
|
|
|
|
|
* Assume a precision (defined as precision after the decimal point) of "n".
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
morphNumericString(char *s, int n)
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
int count;
|
|
|
|
|
|
|
|
|
|
p = strchr (s, '.'); // Find decimal point, if any.
|
|
|
|
|
if (p != NULL)
|
|
|
|
|
{
|
|
|
|
|
count = n; // Adjust for more or less decimals.
|
|
|
|
|
while (count >= 0) // Maximum decimals allowed.
|
|
|
|
|
{
|
|
|
|
|
count--;
|
|
|
|
|
if (*p == '\0') // If there's less than desired.
|
|
|
|
|
break;
|
|
|
|
|
p++; // Next character.
|
|
|
|
|
}
|
|
|
|
|
*p-- = '\0'; // Truncate string.
|
|
|
|
|
while (*p == '0') // Remove trailing zeros.
|
|
|
|
|
*p-- = '\0';
|
|
|
|
|
|
|
|
|
|
if (*p == '.') // If all decimals were zeros, remove ".".
|
|
|
|
|
*p = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-16 17:59:13 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* esSIvalue --
|
|
|
|
|
*
|
|
|
|
|
* Print an output in appropriate SI units used by SPICE. e.g., 1.0e-6
|
|
|
|
|
* will be printed as "1.0u"; 1.0e-12 will be printed as "1.0p", etc.
|
|
|
|
|
*
|
|
|
|
|
* Return value:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Generates output to stream "file".
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
esSIvalue(file, value)
|
|
|
|
|
FILE *file;
|
|
|
|
|
float value;
|
|
|
|
|
{
|
2025-01-06 17:49:29 +01:00
|
|
|
char vstr[32];
|
2023-02-16 17:59:13 +01:00
|
|
|
char suffix = '\0';
|
2023-11-05 17:00:30 +01:00
|
|
|
int precision;
|
2023-02-16 17:59:13 +01:00
|
|
|
float avalue;
|
|
|
|
|
|
|
|
|
|
avalue = fabsf(value);
|
|
|
|
|
|
|
|
|
|
if (avalue < 1.0E-18)
|
|
|
|
|
{
|
|
|
|
|
/* Do nothing---value is probably zero */
|
|
|
|
|
}
|
2023-03-23 20:38:31 +01:00
|
|
|
else if (avalue < 0.9999E-13)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-13 18:21:34 +01:00
|
|
|
/* NOTE: ngspice does not support "a" for "atto" */
|
2023-02-16 17:59:13 +01:00
|
|
|
suffix = 'f';
|
|
|
|
|
value *= 1.0E15;
|
|
|
|
|
}
|
2023-03-23 20:38:31 +01:00
|
|
|
else if (avalue < 1.0001E-10)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
suffix = 'p';
|
|
|
|
|
value *= 1.0E12;
|
|
|
|
|
}
|
2023-03-23 20:38:31 +01:00
|
|
|
else if (avalue < 1.0001E-7)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
suffix = 'n';
|
|
|
|
|
value *= 1.0E9;
|
|
|
|
|
}
|
2023-03-23 20:38:31 +01:00
|
|
|
else if (avalue < 1.0001E-4)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
suffix = 'u';
|
|
|
|
|
value *= 1.0E6;
|
|
|
|
|
}
|
2023-03-23 20:38:31 +01:00
|
|
|
else if (avalue < 1.0001E-2)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
suffix = 'm';
|
|
|
|
|
value *= 1.0E3;
|
|
|
|
|
}
|
2023-03-23 20:38:31 +01:00
|
|
|
else if (avalue > 0.9999E9)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
suffix = 'G';
|
|
|
|
|
value /= 1.0E9;
|
|
|
|
|
}
|
2023-03-23 20:38:31 +01:00
|
|
|
else if (avalue > 0.9999E3)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
|
|
|
|
suffix = 'k';
|
|
|
|
|
value /= 1.0E3;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-05 17:00:30 +01:00
|
|
|
/* Note that "%g" is preferred because it produces more readable
|
|
|
|
|
* output. However, it changes the definition of the precision
|
|
|
|
|
* from significant digits after the radix to total significant
|
2025-01-06 17:49:29 +01:00
|
|
|
* digits. Using a solution provided in StackOverflow (see above).
|
2023-11-05 17:00:30 +01:00
|
|
|
*/
|
|
|
|
|
|
2025-01-06 17:49:29 +01:00
|
|
|
nDecimals(vstr, (double)value, 5);
|
|
|
|
|
morphNumericString(vstr, 5);
|
2023-11-05 17:00:30 +01:00
|
|
|
|
2023-02-16 17:59:13 +01:00
|
|
|
if (suffix == '\0')
|
2025-01-06 17:49:29 +01:00
|
|
|
fprintf(file, "%s", vstr);
|
2023-02-16 17:59:13 +01:00
|
|
|
else
|
2025-01-06 17:49:29 +01:00
|
|
|
fprintf(file, "%s%c", vstr, suffix);
|
2023-02-16 17:59:13 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* spcnAP, spcnAPHier --
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Output the area perimeter of the node with type type if it has not
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
2023-02-16 17:59:13 +01:00
|
|
|
int spcnAP(dterm, node, resClass, scale, asterm, psterm, m, outf, w)
|
|
|
|
|
DevTerm *dterm;
|
2017-04-25 14:41:48 +02:00
|
|
|
EFNode *node;
|
|
|
|
|
int resClass;
|
|
|
|
|
float scale, m;
|
|
|
|
|
char *asterm, *psterm;
|
|
|
|
|
FILE *outf;
|
|
|
|
|
int w;
|
|
|
|
|
{
|
|
|
|
|
char afmt[15], pfmt[15];
|
|
|
|
|
float dsc;
|
2023-02-16 17:59:13 +01:00
|
|
|
int area, perim;
|
|
|
|
|
char *cptr;
|
|
|
|
|
bool haveAttrs = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if ((node == NULL) || (node->efnode_client == (ClientData)NULL))
|
|
|
|
|
{
|
|
|
|
|
TxError("spcnAP: major internal inconsistency\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-22 03:04:30 +01:00
|
|
|
if (asterm) snprintf(afmt, sizeof afmt, " %s=", asterm);
|
|
|
|
|
if (psterm) snprintf(pfmt, sizeof pfmt, " %s=", psterm);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-02-16 17:59:13 +01:00
|
|
|
if (!esDistrJunct || w == -1) goto newFmt;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-03 23:13:37 +01:00
|
|
|
if (((nodeClient*)node->efnode_client)->m_w.widths != NULL)
|
|
|
|
|
dsc = w / ((nodeClient*)node->efnode_client)->m_w.widths[resClass];
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Device missing records for source/drain area/perim.\n");
|
|
|
|
|
dsc = w;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (esScale < 0)
|
|
|
|
|
{
|
|
|
|
|
if (asterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(afmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-12 * node->efnode_pa[resClass].pa_area
|
|
|
|
|
* scale * scale * dsc);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (psterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(pfmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-6 * node->efnode_pa[resClass].pa_perim * scale * dsc);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (asterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(afmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-12 * ((float)node->efnode_pa[resClass].pa_area
|
|
|
|
|
* scale * scale) * esScale * esScale * dsc);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (psterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(pfmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-6 * ((float)node->efnode_pa[resClass].pa_perim
|
|
|
|
|
* scale) * esScale * dsc);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
2023-02-16 17:59:13 +01:00
|
|
|
newFmt:
|
|
|
|
|
/* New format introduced 2/15/2023: Area and perimeter of each terminal
|
|
|
|
|
* are maintained in the terminal attributes for "fet" or "device mosfet"
|
|
|
|
|
* type devices (otherwise, for subcircuit device types, this routine is
|
|
|
|
|
* not used and the same values are found in the device parameters).
|
|
|
|
|
*
|
|
|
|
|
* Values are the last two values in a comma-separated list in
|
|
|
|
|
* dterm_attrs. If not found, then default to the
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
cptr = (dterm) ? dterm->dterm_attrs : NULL;
|
|
|
|
|
while (cptr)
|
|
|
|
|
{
|
|
|
|
|
if (*cptr == ',') cptr++;
|
|
|
|
|
if (sscanf(cptr, "%d,%d", &area, &perim) != 2)
|
|
|
|
|
cptr = strchr(cptr, ',');
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
haveAttrs = TRUE;
|
|
|
|
|
*cptr = '\0';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!haveAttrs)
|
|
|
|
|
{
|
|
|
|
|
area = node->efnode_pa[resClass].pa_area;
|
|
|
|
|
perim = node->efnode_pa[resClass].pa_perim;
|
|
|
|
|
|
|
|
|
|
if (resClass == NO_RESCLASS ||
|
|
|
|
|
beenVisited((nodeClient *)node->efnode_client, resClass))
|
|
|
|
|
scale = 0;
|
|
|
|
|
else
|
|
|
|
|
markVisited((nodeClient *)node->efnode_client, resClass);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
{
|
|
|
|
|
if (asterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(afmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-12 * area * scale * scale / m);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (psterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(pfmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-6 * perim * scale / m);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (asterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(afmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-12 * ((float)area * scale * scale) * esScale * esScale);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (psterm)
|
2023-02-16 17:59:13 +01:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(pfmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-6 * ((float)perim * scale) * esScale);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
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];
|
2023-02-16 17:59:13 +01:00
|
|
|
int area, perim;
|
|
|
|
|
char *cptr;
|
|
|
|
|
bool haveAttrs = FALSE;
|
|
|
|
|
|
|
|
|
|
sprintf(afmt," %s=", asterm);
|
|
|
|
|
sprintf(pfmt," %s=", psterm);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (node->efnode_client == (ClientData) NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
initNodeClientHier(node);
|
|
|
|
|
|
|
|
|
|
nc = (nodeClientHier *)node->efnode_client;
|
|
|
|
|
if (nc->lastPrefix != hierName)
|
|
|
|
|
{
|
|
|
|
|
clearVisited(nc);
|
|
|
|
|
nc->lastPrefix = hierName;
|
|
|
|
|
}
|
2023-02-16 17:59:13 +01:00
|
|
|
|
|
|
|
|
/* Check for area and perim values in dterm_attrs */
|
|
|
|
|
|
|
|
|
|
cptr = dterm->dterm_attrs;
|
|
|
|
|
while (cptr)
|
|
|
|
|
{
|
|
|
|
|
if (*cptr == ',') cptr++;
|
|
|
|
|
if (sscanf(cptr, "%d,%d", &area, &perim) != 2)
|
|
|
|
|
cptr = strchr(cptr, ',');
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
haveAttrs = TRUE;
|
|
|
|
|
*cptr = '\0';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!haveAttrs)
|
|
|
|
|
{
|
|
|
|
|
area = node->efnode_pa[resClass].pa_area;
|
|
|
|
|
perim = node->efnode_pa[resClass].pa_perim;
|
|
|
|
|
|
|
|
|
|
if (resClass == NO_RESCLASS ||
|
|
|
|
|
beenVisited((nodeClient *)node->efnode_client, resClass))
|
|
|
|
|
scale = 0;
|
|
|
|
|
else
|
|
|
|
|
markVisited((nodeClient *)node->efnode_client, resClass);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (esScale < 0)
|
|
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(afmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-12 * area * scale * scale / m);
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(pfmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-6 * perim * scale / m);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(afmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-12 * ((float)area * scale) * esScale * esScale);
|
2023-03-22 03:04:30 +01:00
|
|
|
fputs(pfmt, outf);
|
2023-02-16 17:59:13 +01:00
|
|
|
esSIvalue(outf, 1.0E-6 * ((float)perim * scale) * esScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2021-12-20 19:12:49 +01:00
|
|
|
/* Mark node as visited (set bit one higher than number of resist classes) */
|
|
|
|
|
if (esDistrJunct)
|
|
|
|
|
update_w(efNumResistClasses, 1, nn->efnn_node);
|
|
|
|
|
else
|
|
|
|
|
markVisited((nodeClientHier *)nn->efnn_node->efnode_client,
|
|
|
|
|
efNumResistClasses);
|
2019-10-14 19:49:41 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
return (1 + strlen(nname));
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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;
|
|
|
|
|
|
2023-03-13 18:21:34 +01:00
|
|
|
fprintf(esSpiceF, "C%d %s %s ", esCapNum++, nodeSpiceName(hierName1, NULL),
|
|
|
|
|
nodeSpiceName(hierName2, NULL));
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-15 * cap);
|
|
|
|
|
fprintf(esSpiceF, "\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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;
|
|
|
|
|
{
|
2023-02-28 19:11:32 +01:00
|
|
|
HashEntry *he;
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
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
|
|
|
|
2023-02-28 19:11:32 +01:00
|
|
|
/* Mark nodes as visited so that associated capacitances won't be marked
|
|
|
|
|
* as "floating". This is inefficient since nodeSpiceName() already does
|
|
|
|
|
* a hash lookup of the EFNodeName. Could be improved, but is not a big
|
|
|
|
|
* performance issue.
|
|
|
|
|
*/
|
|
|
|
|
he = EFHNLook(hierName1, (char *)NULL, "nodeName");
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
|
|
|
|
/* Mark node as visited (set bit one higher than number of resist classes) */
|
|
|
|
|
if (esDistrJunct)
|
|
|
|
|
update_w(efNumResistClasses, 1, nn->efnn_node);
|
|
|
|
|
else
|
|
|
|
|
markVisited((nodeClientHier *)nn->efnn_node->efnode_client,
|
|
|
|
|
efNumResistClasses);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
he = EFHNLook(hierName2, (char *)NULL, "nodeName");
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
|
|
|
|
/* Mark node as visited (set bit one higher than number of resist classes) */
|
|
|
|
|
if (esDistrJunct)
|
|
|
|
|
update_w(efNumResistClasses, 1, nn->efnn_node);
|
|
|
|
|
else
|
|
|
|
|
markVisited((nodeClientHier *)nn->efnn_node->efnode_client,
|
|
|
|
|
efNumResistClasses);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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;
|
|
|
|
|
|
2021-12-30 19:27:42 +01:00
|
|
|
if (node->efnode_flags & EF_GLOB_SUBS_NODE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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 --
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Procedure to output a single node to the .spice file along with its
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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;
|
2020-05-23 23:13:14 +02:00
|
|
|
int res;
|
2017-04-25 14:41:48 +02:00
|
|
|
double cap;
|
|
|
|
|
{
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
bool isConnected = FALSE;
|
2024-10-20 14:46:02 +02:00
|
|
|
const char *fmt;
|
|
|
|
|
char *nsn;
|
2017-04-25 14:41:48 +02:00
|
|
|
EFAttr *ap;
|
|
|
|
|
|
|
|
|
|
if (node->efnode_client)
|
|
|
|
|
{
|
|
|
|
|
isConnected = (esDistrJunct) ?
|
|
|
|
|
(((nodeClient *)node->efnode_client)->m_w.widths != NULL) :
|
2021-12-20 19:12:49 +01:00
|
|
|
(!TTMaskHasType(&((nodeClient *)node->efnode_client)->m_w.visitMask,
|
|
|
|
|
efNumResistClasses));
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
if (!isConnected && esDevNodesOnly)
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
2024-10-04 18:24:47 +02:00
|
|
|
if (esFormat == SPICE2 || (esFormat == HSPICE && strncmp(nsn, "z@", 2)==0 )) {
|
2017-04-25 14:41:48 +02:00
|
|
|
static char ntmp[MAX_STR_SIZE];
|
|
|
|
|
|
|
|
|
|
EFHNSprintf(ntmp, hierName);
|
2020-06-24 21:22:04 +02:00
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
|
|
|
|
|
}
|
|
|
|
|
cap = cap / 1000;
|
|
|
|
|
if (cap > EFCapThreshold)
|
|
|
|
|
{
|
2023-03-13 18:21:34 +01:00
|
|
|
fprintf(esSpiceF, "C%d %s %s ", esCapNum++, nsn, esSpiceCapNode);
|
|
|
|
|
esSIvalue(esSpiceF, 1.0E-15 * cap);
|
|
|
|
|
if (!isConnected)
|
|
|
|
|
{
|
|
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, " $");
|
|
|
|
|
fprintf(esSpiceF, " **FLOATING");
|
|
|
|
|
}
|
|
|
|
|
fprintf(esSpiceF, "\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
if (node->efnode_attrs && !esNoAttrs)
|
|
|
|
|
{
|
2020-06-24 21:22:04 +02:00
|
|
|
if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
int res;
|
2017-04-25 14:41:48 +02:00
|
|
|
double cap;
|
|
|
|
|
{
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
HierName *hierName;
|
2024-10-20 14:46:02 +02:00
|
|
|
char *nsn;
|
2017-04-25 14:41:48 +02:00
|
|
|
EFAttr *ap;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
hierName = (HierName *) node->efnode_name->efnn_hier;
|
2019-10-29 14:44:28 +01:00
|
|
|
nsn = nodeSpiceName(hierName, NULL);
|
2024-09-30 00:00:00 +02:00
|
|
|
TxError("** %s (%lx)\n", nsn, (intmax_t) node);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
printf("\t client.name=%s, client.m_w=%p\n",
|
|
|
|
|
((nodeClient *)node->efnode_client)->spiceNodeName,
|
2025-01-06 17:12:11 +01:00
|
|
|
(void *)((nodeClient *)node->efnode_client)->m_w.widths);
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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:
|
2020-05-23 23:13:14 +02:00
|
|
|
if ( esFormat == SPICE2 )
|
2017-04-25 14:41:48 +02:00
|
|
|
sprintf(esTempName, "%d", esNodeNum++);
|
|
|
|
|
else {
|
|
|
|
|
EFHNSprintf(esTempName, node->efnode_name->efnn_hier);
|
|
|
|
|
if ( esFormat == HSPICE ) /* more processing */
|
|
|
|
|
nodeHspiceName(esTempName);
|
|
|
|
|
}
|
2021-02-24 18:35:06 +01:00
|
|
|
((nodeClient *) (node->efnode_client))->spiceNodeName =
|
|
|
|
|
StrDup(NULL, esTempName);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
retName:
|
|
|
|
|
return ((nodeClient *) (node->efnode_client))->spiceNodeName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNSprintf --
|
|
|
|
|
*
|
|
|
|
|
* Create a hierarchical node name.
|
2021-07-11 03:13:24 +02:00
|
|
|
* The flags in EFOutputFlags control whether global (!) or local (#)
|
2020-05-23 23:13:14 +02:00
|
|
|
* suffixes are to be trimmed. Also substitutes \. with \@ if the
|
2021-02-24 18:35:06 +01:00
|
|
|
* format is hspice.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Changes the area pointed to by str
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
int
|
2017-04-25 14:41:48 +02:00
|
|
|
EFHNSprintf(str, hierName)
|
|
|
|
|
char *str;
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
{
|
2019-10-15 03:46:07 +02:00
|
|
|
bool trimGlob, trimLocal, convertComma, convertEqual, convertBrackets;
|
2021-02-24 18:35:06 +01:00
|
|
|
char *s, *cp, c;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *efHNSprintfPrefix(HierName *, char *);
|
|
|
|
|
|
2021-02-24 18:35:06 +01:00
|
|
|
s = str;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (hierName->hn_parent) str = efHNSprintfPrefix(hierName->hn_parent, str);
|
2021-07-11 03:27:19 +02:00
|
|
|
if (EFOutputFlags & EF_TRIM_MASK)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-02-24 18:35:06 +01:00
|
|
|
cp = hierName->hn_name;
|
2021-07-11 03:13:24 +02:00
|
|
|
trimGlob = (EFOutputFlags & EF_TRIMGLOB);
|
|
|
|
|
trimLocal = (EFOutputFlags & EF_TRIMLOCAL);
|
|
|
|
|
convertComma = (EFOutputFlags & EF_CONVERTCOMMA);
|
|
|
|
|
convertEqual = (EFOutputFlags & EF_CONVERTEQUAL);
|
|
|
|
|
convertBrackets = (EFOutputFlags & EF_CONVERTBRACKETS);
|
2024-10-04 18:21:15 +02:00
|
|
|
while ((c = *cp++))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-02-24 18:35:06 +01:00
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case '!': if (!trimGlob) *str++ = c; break;
|
|
|
|
|
case '.': *str++ = (esFormat == HSPICE)?'@':'.'; break;
|
|
|
|
|
case '=': if (convertEqual) *str++ = ':'; break;
|
|
|
|
|
case ',': if (convertComma) *str++ = '|'; break;
|
|
|
|
|
case '[': *str++ = (convertBrackets) ? '_' : '['; break;
|
|
|
|
|
case ']': *str++ = (convertBrackets) ? '_' : ']'; break;
|
|
|
|
|
case '#': if (trimLocal) break; // else fall through
|
|
|
|
|
default: *str++ = c; break;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-02-24 18:35:06 +01:00
|
|
|
*str++ = '\0';
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-02-24 18:35:06 +01:00
|
|
|
else strcpy(str, hierName->hn_name);
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *efHNSprintfPrefix(hierName, str)
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
char *str;
|
|
|
|
|
{
|
|
|
|
|
char *cp, c;
|
2021-07-11 03:13:24 +02:00
|
|
|
bool convertEqual = (EFOutputFlags & EF_CONVERTEQUAL) ? TRUE : FALSE;
|
|
|
|
|
bool convertComma = (EFOutputFlags & EF_CONVERTCOMMA) ? TRUE : FALSE;
|
|
|
|
|
bool convertBrackets = (EFOutputFlags & 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 --
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* 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];
|
|
|
|
|
|
|
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* find the suffix
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
l = strlen(s);
|
2020-05-23 23:13:14 +02:00
|
|
|
for (p = s + l; (p > s) && *p != '/'; p--);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (p == s)
|
|
|
|
|
{
|
|
|
|
|
strcpy(map, s);
|
|
|
|
|
goto topLevel;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/*
|
|
|
|
|
* break it into prefix '/0' suffix
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
if (*p == '/')
|
|
|
|
|
*p = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
sf = p + 1;
|
|
|
|
|
|
2020-05-23 23:13:14 +02: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
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
2020-05-23 23:13:14 +02:00
|
|
|
while ((he = HashNext(&subcktNameTable, &hs)) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
#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;
|
2021-07-11 03:13:24 +02:00
|
|
|
|
|
|
|
|
case DEV_VOLT:
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
{
|
2025-02-24 09:31:42 +01:00
|
|
|
size_t l1 = strlen(*a1);
|
|
|
|
|
size_t l2 = strlen(*a2);
|
|
|
|
|
char *t = (char *) mallocMagic(l1 + l2 + 1);
|
|
|
|
|
strcpy(t, *a1); /* strcpy_advance() */
|
|
|
|
|
strcat(t, *a2);
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2020-06-01 23:14:22 +02:00
|
|
|
devMergeVisit(dev, hc, scale, trans)
|
2017-04-25 14:41:48 +02:00
|
|
|
Dev *dev; /* Dev to examine */
|
2020-06-01 23:14:22 +02:00
|
|
|
HierContext *hc; /* Hierarchical context down to this dev */
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-06-01 23:14:22 +02:00
|
|
|
HierName *hierName = hc->hc_hierName;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (esDistrJunct)
|
2020-06-01 23:14:22 +02:00
|
|
|
devDistJunctVisit(dev, hc, scale, trans);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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,
|
2020-05-23 23:13:14 +02:00
|
|
|
dev->dev_subsnode->efnode_name->efnn_hier,
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* run the list of devs. compare the current one with
|
2017-04-25 14:41:48 +02:00
|
|
|
* each one in the list. if they fullfill the matching requirements
|
|
|
|
|
* merge them only if:
|
|
|
|
|
* 1) they have both apf S, D attributes
|
2020-05-23 23:13:14 +02:00
|
|
|
* or
|
|
|
|
|
* 2) one of them has aph S, D attributes and they have the same
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
if (pmode == PARALLEL)
|
2017-04-25 14:41:48 +02:00
|
|
|
cd = &cfp->dev->dev_terms[2];
|
2020-05-23 23:13:14 +02:00
|
|
|
else if (pmode == ANTIPARALLEL)
|
2017-04-25 14:41:48 +02:00
|
|
|
cs = &cfp->dev->dev_terms[2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chS = extHierSDAttr(cs); chD = extHierSDAttr(cd);
|
|
|
|
|
if (!(chS || chD || hS || hD)) /* all flat S, D */
|
|
|
|
|
goto mergeThem;
|
2020-05-23 23:13:14 +02:00
|
|
|
if (cfp->hierName != hierName &&
|
2017-04-25 14:41:48 +02:00
|
|
|
((chS && !hS) || (chD && !hD) ||
|
|
|
|
|
(!chS && hS) || (!chD && hD)))
|
2020-05-23 23:13:14 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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:
|
2023-05-21 18:13:01 +02:00
|
|
|
/* Default case is to add counts */
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + esFMult[fp->esFMIndex];
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
switch(dev->dev_class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
case DEV_FET:
|
2023-05-21 18:13:01 +02:00
|
|
|
if (cfp->w > 0)
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->w / cfp->w);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEV_RSUBCKT:
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
if (fp->dev->dev_type == esNoModelType)
|
2023-05-21 18:13:01 +02:00
|
|
|
{
|
|
|
|
|
if (cfp->dev->dev_res > 0)
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->dev->dev_res
|
|
|
|
|
/ cfp->dev->dev_res);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2023-05-21 18:13:01 +02:00
|
|
|
{
|
|
|
|
|
if (cfp->l > 0)
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->l / cfp->l);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2023-05-21 18:13:01 +02:00
|
|
|
{
|
|
|
|
|
if (cfp->dev->dev_cap > 0)
|
|
|
|
|
m = esFMult[cfp->esFMIndex] + (fp->dev->dev_cap
|
|
|
|
|
/ cfp->dev->dev_cap);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2023-05-21 18:13:01 +02:00
|
|
|
{
|
|
|
|
|
if ((cfp->l > 0) && (cfp->w > 0))
|
|
|
|
|
m = esFMult[cfp->esFMIndex] +
|
|
|
|
|
((fp->l * fp->w) / (cfp->l * cfp->w));
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
setDevMult(fp->esFMIndex, DEV_KILLED);
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
int
|
2017-04-25 14:41:48 +02:00
|
|
|
update_w(resClass, w, n)
|
|
|
|
|
short resClass;
|
|
|
|
|
int w;
|
|
|
|
|
EFNode *n;
|
|
|
|
|
{
|
|
|
|
|
nodeClient *nc;
|
|
|
|
|
int i;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (n->efnode_client == (ClientData)NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
initNodeClient(n);
|
|
|
|
|
nc = (nodeClient *) n->efnode_client;
|
|
|
|
|
if (nc->m_w.widths == NULL)
|
|
|
|
|
{
|
|
|
|
|
(nc->m_w.widths) = (float *)mallocMagic((unsigned)sizeof(float)
|
2021-12-20 19:12:49 +01:00
|
|
|
* (efNumResistClasses + 1));
|
|
|
|
|
for (i = 0; i <= efNumResistClasses; i++)
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
int
|
2020-06-01 23:14:22 +02:00
|
|
|
devDistJunctVisit(dev, hc, scale, trans)
|
2017-04-25 14:41:48 +02:00
|
|
|
Dev *dev; /* Dev to examine */
|
2020-06-01 23:14:22 +02:00
|
|
|
HierContext *hc; /* Hierarchical path down to this dev */
|
2017-04-25 14:41:48 +02:00
|
|
|
float scale; /* Scale transform */
|
|
|
|
|
Transform *trans; /* (unused) */
|
|
|
|
|
{
|
|
|
|
|
EFNode *n;
|
|
|
|
|
int i;
|
|
|
|
|
int l, w;
|
2020-06-01 23:14:22 +02:00
|
|
|
HierName *hierName = hc->hc_hierName;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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);
|
2020-09-11 23:29:12 +02:00
|
|
|
if (i == 1)
|
|
|
|
|
update_w(esFetInfo[dev->dev_type].resClassSource, w, n);
|
|
|
|
|
else
|
|
|
|
|
update_w(esFetInfo[dev->dev_type].resClassDrain, w, n);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|